diff --git a/deltachat-ffi/src/lib.rs b/deltachat-ffi/src/lib.rs index e2714091d..cb27bc503 100644 --- a/deltachat-ffi/src/lib.rs +++ b/deltachat-ffi/src/lib.rs @@ -1319,8 +1319,13 @@ pub unsafe extern "C" fn dc_is_contact_in_chat( } let ctx = &*context; - block_on(async move { chat::is_contact_in_chat(ctx, ChatId::new(chat_id), contact_id).await }) - .into() + block_on(chat::is_contact_in_chat( + ctx, + ChatId::new(chat_id), + contact_id, + )) + .log_err(ctx, "is_contact_in_chat failed") + .unwrap_or_default() as libc::c_int } #[no_mangle] @@ -1335,9 +1340,13 @@ pub unsafe extern "C" fn dc_add_contact_to_chat( } let ctx = &*context; - block_on(async move { - chat::add_contact_to_chat(ctx, ChatId::new(chat_id), contact_id).await as libc::c_int - }) + block_on(chat::add_contact_to_chat( + ctx, + ChatId::new(chat_id), + contact_id, + )) + .log_err(ctx, "Failed to add contact") + .is_ok() as libc::c_int } #[no_mangle] @@ -1352,12 +1361,13 @@ pub unsafe extern "C" fn dc_remove_contact_from_chat( } let ctx = &*context; - block_on(async move { - chat::remove_contact_from_chat(ctx, ChatId::new(chat_id), contact_id) - .await - .map(|_| 1) - .unwrap_or_log_default(ctx, "Failed to remove contact") - }) + block_on(chat::remove_contact_from_chat( + ctx, + ChatId::new(chat_id), + contact_id, + )) + .log_err(ctx, "Failed to remove contact") + .is_ok() as libc::c_int } #[no_mangle] @@ -2019,12 +2029,9 @@ pub unsafe extern "C" fn dc_get_securejoin_qr( Some(ChatId::new(chat_id)) }; - block_on(async move { - securejoin::dc_get_securejoin_qr(ctx, chat_id) - .await - .unwrap_or_else(|| "".to_string()) - .strdup() - }) + block_on(securejoin::dc_get_securejoin_qr(ctx, chat_id)) + .unwrap_or_else(|_| "".to_string()) + .strdup() } #[no_mangle] @@ -2615,8 +2622,10 @@ pub unsafe extern "C" fn dc_chat_can_send(chat: *mut dc_chat_t) -> libc::c_int { return 0; } let ffi_chat = &*chat; - let cxt = &*ffi_chat.context; - block_on(ffi_chat.chat.can_send(cxt)) as libc::c_int + let ctx = &*ffi_chat.context; + block_on(ffi_chat.chat.can_send(ctx)) + .log_err(ctx, "can_send failed") + .unwrap_or_default() as libc::c_int } #[no_mangle] @@ -3502,7 +3511,9 @@ pub unsafe extern "C" fn dc_contact_is_verified(contact: *mut dc_contact_t) -> l let ffi_contact = &*contact; let ctx = &*ffi_contact.context; - block_on(async move { ffi_contact.contact.is_verified(ctx).await as libc::c_int }) + block_on(ffi_contact.contact.is_verified(ctx)) + .log_err(ctx, "is_verified failed") + .unwrap_or_default() as libc::c_int } // dc_lot_t diff --git a/examples/repl/cmdline.rs b/examples/repl/cmdline.rs index 85cd407cb..23ea29b2b 100644 --- a/examples/repl/cmdline.rs +++ b/examples/repl/cmdline.rs @@ -2,7 +2,7 @@ extern crate dirs; use std::str::FromStr; -use anyhow::{bail, ensure, Error}; +use anyhow::{bail, ensure, Result}; use async_std::path::Path; use deltachat::chat::{ self, Chat, ChatId, ChatItem, ChatVisibility, MuteDuration, ProtectionStatus, @@ -98,7 +98,7 @@ async fn reset_tables(context: &Context, bits: i32) { }); } -async fn poke_eml_file(context: &Context, filename: impl AsRef) -> Result<(), anyhow::Error> { +async fn poke_eml_file(context: &Context, filename: impl AsRef) -> Result<()> { let data = dc_read_file(context, filename).await?; if let Err(err) = dc_receive_imf(context, &data, "import", 0, false).await { @@ -239,7 +239,7 @@ async fn log_msg(context: &Context, prefix: impl AsRef, msg: &Message) { ); } -async fn log_msglist(context: &Context, msglist: &[MsgId]) -> Result<(), Error> { +async fn log_msglist(context: &Context, msglist: &[MsgId]) -> Result<()> { let mut lines_out = 0; for &msg_id in msglist { if msg_id == MsgId::new(DC_MSG_ID_DAYMARKER) { @@ -267,59 +267,59 @@ async fn log_msglist(context: &Context, msglist: &[MsgId]) -> Result<(), Error> Ok(()) } -async fn log_contactlist(context: &Context, contacts: &[u32]) { +async fn log_contactlist(context: &Context, contacts: &[u32]) -> Result<()> { for contact_id in contacts { let line; let mut line2 = "".to_string(); - if let Ok(contact) = Contact::get_by_id(context, *contact_id).await { - let name = contact.get_display_name(); - let addr = contact.get_addr(); - let verified_state = contact.is_verified(context).await; - let verified_str = if VerifiedStatus::Unverified != verified_state { - if verified_state == VerifiedStatus::BidirectVerified { - " √√" - } else { - " √" - } + let contact = Contact::get_by_id(context, *contact_id).await?; + let name = contact.get_display_name(); + let addr = contact.get_addr(); + let verified_state = contact.is_verified(context).await?; + let verified_str = if VerifiedStatus::Unverified != verified_state { + if verified_state == VerifiedStatus::BidirectVerified { + " √√" } else { - "" - }; - line = format!( - "{}{} <{}>", - if !name.is_empty() { - &name - } else { - "" - }, - verified_str, - if !addr.is_empty() { - &addr - } else { - "addr unset" - } - ); - let peerstate = Peerstate::from_addr(context, addr) - .await - .expect("peerstate error"); - if peerstate.is_some() && *contact_id != 1 { - line2 = format!( - ", prefer-encrypt={}", - peerstate.as_ref().unwrap().prefer_encrypt - ); + " √" } - - println!("Contact#{}: {}{}", *contact_id, line, line2); + } else { + "" + }; + line = format!( + "{}{} <{}>", + if !name.is_empty() { + &name + } else { + "" + }, + verified_str, + if !addr.is_empty() { + &addr + } else { + "addr unset" + } + ); + let peerstate = Peerstate::from_addr(context, &addr) + .await + .expect("peerstate error"); + if peerstate.is_some() && *contact_id != 1 { + line2 = format!( + ", prefer-encrypt={}", + peerstate.as_ref().unwrap().prefer_encrypt + ); } + + println!("Contact#{}: {}{}", *contact_id, line, line2); } + Ok(()) } fn chat_prefix(chat: &Chat) -> &'static str { chat.typ.into() } -pub async fn cmdline(context: Context, line: &str, chat_id: &mut ChatId) -> Result<(), Error> { +pub async fn cmdline(context: Context, line: &str, chat_id: &mut ChatId) -> Result<()> { let mut sel_chat = if !chat_id.is_unset() { - Chat::load_from_db(&context, *chat_id).await.ok() + Some(Chat::load_from_db(&context, *chat_id).await?) } else { None }; @@ -726,17 +726,9 @@ pub async fn cmdline(context: Context, line: &str, chat_id: &mut ChatId) -> Resu ensure!(!arg1.is_empty(), "Argument missing."); let contact_id_0: u32 = arg1.parse()?; - if chat::add_contact_to_chat( - &context, - sel_chat.as_ref().unwrap().get_id(), - contact_id_0, - ) - .await - { - println!("Contact added to chat."); - } else { - bail!("Cannot add contact to chat."); - } + chat::add_contact_to_chat(&context, sel_chat.as_ref().unwrap().get_id(), contact_id_0) + .await?; + println!("Contact added to chat."); } "removemember" => { ensure!(sel_chat.is_some(), "No chat selected."); @@ -774,7 +766,7 @@ pub async fn cmdline(context: Context, line: &str, chat_id: &mut ChatId) -> Resu chat::get_chat_contacts(&context, sel_chat.as_ref().unwrap().get_id()).await?; println!("Memberlist:"); - log_contactlist(&context, &contacts).await; + log_contactlist(&context, &contacts).await?; println!( "{} contacts\nLocation streaming: {}", contacts.len(), @@ -1090,7 +1082,7 @@ pub async fn cmdline(context: Context, line: &str, chat_id: &mut ChatId) -> Resu Some(arg1), ) .await?; - log_contactlist(&context, &contacts).await; + log_contactlist(&context, &contacts).await?; println!("{} contacts.", contacts.len()); } "addcontact" => { @@ -1155,7 +1147,7 @@ pub async fn cmdline(context: Context, line: &str, chat_id: &mut ChatId) -> Resu } "listblocked" => { let contacts = Contact::get_all_blocked(&context).await?; - log_contactlist(&context, &contacts).await; + log_contactlist(&context, &contacts).await?; println!("{} blocked contacts.", contacts.len()); } "checkqr" => { diff --git a/examples/repl/main.rs b/examples/repl/main.rs index 6b89842aa..6c7622419 100644 --- a/examples/repl/main.rs +++ b/examples/repl/main.rs @@ -409,20 +409,19 @@ async fn handle_cmd( } "getqr" | "getbadqr" => { ctx.start_io().await; - let group = arg1.parse::().ok().map(ChatId::new); - if let Some(mut qr) = dc_get_securejoin_qr(&ctx, group).await { - if !qr.is_empty() { - if arg0 == "getbadqr" && qr.len() > 40 { - qr.replace_range(12..22, "0000000000") - } - println!("{}", qr); - let output = Command::new("qrencode") - .args(&["-t", "ansiutf8", qr.as_str(), "-o", "-"]) - .output() - .expect("failed to execute process"); - io::stdout().write_all(&output.stdout).unwrap(); - io::stderr().write_all(&output.stderr).unwrap(); + let group = arg1.parse::().ok().map(|id| ChatId::new(id)); + let mut qr = dc_get_securejoin_qr(&ctx, group).await?; + if !qr.is_empty() { + if arg0 == "getbadqr" && qr.len() > 40 { + qr.replace_range(12..22, "0000000000") } + println!("{}", qr); + let output = Command::new("qrencode") + .args(&["-t", "ansiutf8", qr.as_str(), "-o", "-"]) + .output() + .expect("failed to execute process"); + io::stdout().write_all(&output.stdout).unwrap(); + io::stderr().write_all(&output.stderr).unwrap(); } } "joinqr" => { diff --git a/src/aheader.rs b/src/aheader.rs index 798bf6974..7d886c944 100644 --- a/src/aheader.rs +++ b/src/aheader.rs @@ -2,12 +2,12 @@ //! //! Parse and create [Autocrypt-headers](https://autocrypt.org/en/latest/level1.html#the-autocrypt-header). +use anyhow::{bail, format_err, Error, Result}; use std::collections::BTreeMap; use std::str::FromStr; use std::{fmt, str}; use crate::contact::addr_cmp; -use crate::context::Context; use crate::headerdef::{HeaderDef, HeaderDefMap}; use crate::key::{DcKey, SignedPublicKey}; @@ -37,13 +37,13 @@ impl fmt::Display for EncryptPreference { } impl str::FromStr for EncryptPreference { - type Err = (); + type Err = Error; - fn from_str(s: &str) -> Result { + fn from_str(s: &str) -> Result { match s { "mutual" => Ok(EncryptPreference::Mutual), "nopreference" => Ok(EncryptPreference::NoPreference), - _ => Err(()), + _ => bail!("Cannot parse encryption preference {}", s), } } } @@ -70,28 +70,27 @@ impl Aheader { } } + /// Tries to parse Autocrypt header. + /// + /// If there is none, returns None. If the header is present but cannot be parsed, returns an + /// error. pub fn from_headers( - context: &Context, wanted_from: &str, headers: &[mailparse::MailHeader<'_>], - ) -> Option { + ) -> Result> { if let Some(value) = headers.get_header_value(HeaderDef::Autocrypt) { - match Self::from_str(&value) { - Ok(header) => { - if addr_cmp(&header.addr, wanted_from) { - return Some(header); - } - } - Err(err) => { - warn!( - context, - "found invalid autocrypt header {}: {:?}", value, err - ); - } + let header = Self::from_str(&value)?; + if !addr_cmp(&header.addr, wanted_from) { + bail!( + "Autocrypt header address {:?} is not {:?}", + header.addr, + wanted_from + ); } + Ok(Some(header)) + } else { + Ok(None) } - - None } } @@ -120,9 +119,9 @@ impl fmt::Display for Aheader { } impl str::FromStr for Aheader { - type Err = (); + type Err = Error; - fn from_str(s: &str) -> Result { + fn from_str(s: &str) -> Result { let mut attributes: BTreeMap = s .split(';') .filter_map(|a| { @@ -136,15 +135,20 @@ impl str::FromStr for Aheader { let addr = match attributes.remove("addr") { Some(addr) => addr, - None => { - return Err(()); - } + None => bail!("Autocrypt header has no addr"), }; let public_key: SignedPublicKey = attributes .remove("keydata") - .ok_or(()) - .and_then(|raw| SignedPublicKey::from_base64(&raw).or(Err(()))) - .and_then(|key| key.verify().and(Ok(key)).or(Err(())))?; + .ok_or_else(|| format_err!("keydata attribute is not found")) + .and_then(|raw| { + SignedPublicKey::from_base64(&raw) + .map_err(|_| format_err!("Autocrypt key cannot be decoded")) + }) + .and_then(|key| { + key.verify() + .and(Ok(key)) + .map_err(|_| format_err!("Autocrypt key cannot be verified")) + })?; let prefer_encrypt = attributes .remove("prefer-encrypt") @@ -154,7 +158,7 @@ impl str::FromStr for Aheader { // Autocrypt-Level0: unknown attributes starting with an underscore can be safely ignored // Autocrypt-Level0: unknown attribute, treat the header as invalid if attributes.keys().any(|k| !k.starts_with('_')) { - return Err(()); + bail!("Unknown Autocrypt attribute found"); } Ok(Aheader { @@ -172,38 +176,40 @@ mod tests { const RAWKEY: &str = "xsBNBFzG3j0BCAC6iNhT8zydvCXi8LI/gFnkadMbfmSE/rTJskRRra/utGbLyDta/yTrJgWL7O3y/g4HdDW/dN2z26Y6W13IMzx9gLInn1KQZChtqWAcr/ReUucXcymwcfg1mdkBGk3TSLeLihN6CJx8Wsv8ig+kgAzte4f5rqEEAJVQ9WZHuti7UiYs6oRzqTo06CRe9owVXxzdMf0VDQtf7ZFm9dpzKKbhH7Lu8880iiotQ9/yRCkDGp9fNThsrLdZiK6OIAcIBAqi2rI89aS1dAmnRbktQieCx5izzyYkR1KvVL3gTTllHOzfKVEC2asmtWu2e4se/+O4WMIS1eGrn7GeWVb0Vwc5ABEBAAHNETxhQEBiLmV4YW1wbGUuZGU+wsCJBBABCAAzAhkBBQJcxt5FAhsDBAsJCAcGFQgJCgsCAxYCARYhBI4xxYKBgH3ANh5cufaKrc9mtiMLAAoJEPaKrc9mtiML938H/18F+3Wf9/JaAy/8hCO1v4S2PVBhxaKCokaNFtkfaMRne2l087LscCFPiFNyb4mv6Z3YeK8Xpxlp2sI0ecvdiqLUOGfnxS6tQrj+83EjtIrZ/hXOk1h121QFWH9Zg2VNHtODXjAgdLDC0NWUrclR0ZOqEDQHeo0ibTILdokVfXFN25wakPmGaYJP2y729cb1ve7RzvIvwn+Dddfxo3ao72rBfLi7l4NQ4S0KsY4cw+/6l5bRCKYCP77wZtvCwUvfVVosLdT43agtSiBI49+ayqvZ8OCvSJa61i+v81brTiEy9GBod4eAp45Ibsuemkw+gon4ZOvUXHTjwFB+h63MrozOwE0EXMbePQEIAL/vauf1zK8JgCu3V+G+SOX0iWw5xUlCPX+ERpBbWfwu3uAqn4wYXD3JDE/fVAF668xiV4eTPtlSUd5h0mn+G7uXMMOtkb+20SoEt50f8zw8TrL9t+ZsV11GKZWJpCar5AhXWsn6EEi8I2hLL5vn55ZZmHuGgN4jjmkRl3ToKCLhaXwTBjCJem7N5EH7F75wErEITa55v4Lb4Nfca7vnvtYrI1OA446xa8gHra0SINelTD09/JM/Fw4sWVPBaRZmJK/Tnu79N23No9XBUubmFPv1pNexZsQclicnTpt/BEWhiun7d6lfGB63K1aoHRTR1pcrWvBuALuuz0gqar2zlI0AEQEAAcLAdgQYAQgAIAUCXMbeRQIbDBYhBI4xxYKBgH3ANh5cufaKrc9mtiMLAAoJEPaKrc9mtiMLKSEIAIyLCRO2OyZ0IYRvRPpMn4p7E+7Pfcz/0mSkOy+1hshgJnqivXurm8zwGrwdMqeV4eslKR9H1RUdWGUQJNbtwmmjrt5DHpIhYHl5t3FpCBaGbV20Omo00Q38lBl9MtrmZkZw+ktEk6X+0xCKssMF+2MADkSOIufbR5HrDVB89VZOHCO9DeXvCUUAw2hyJiL/LHmLzJ40zYoTmb+F//f0k0j+tRdbkefyRoCmwG7YGiT+2hnCdgcezswnzah5J3ZKlrg7jOGo1LxtbvNUzxNBbC6S/aNgwm6qxo7xegRhmEl5uZ16zwyj4qz+xkjGy25Of5mWfUDoNw7OT7sjUbHOOMc="; #[test] - fn test_from_str() { + fn test_from_str() -> Result<()> { let h: Aheader = format!( "addr=me@mail.com; prefer-encrypt=mutual; keydata={}", RAWKEY ) - .parse() - .expect("failed to parse"); + .parse()?; assert_eq!(h.addr, "me@mail.com"); assert_eq!(h.prefer_encrypt, EncryptPreference::Mutual); + Ok(()) } // EncryptPreference::Reset is an internal value, parser should never return it #[test] - fn test_from_str_reset() { + fn test_from_str_reset() -> Result<()> { let raw = format!( "addr=reset@example.com; prefer-encrypt=reset; keydata={}", RAWKEY ); - let h: Aheader = raw.parse().expect("failed to parse"); + let h: Aheader = raw.parse()?; assert_eq!(h.addr, "reset@example.com"); assert_eq!(h.prefer_encrypt, EncryptPreference::NoPreference); + Ok(()) } #[test] - fn test_from_str_non_critical() { + fn test_from_str_non_critical() -> Result<()> { let raw = format!("addr=me@mail.com; _foo=one; _bar=two; keydata={}", RAWKEY); - let h: Aheader = raw.parse().expect("failed to parse"); + let h: Aheader = raw.parse()?; assert_eq!(h.addr, "me@mail.com"); assert_eq!(h.prefer_encrypt, EncryptPreference::NoPreference); + Ok(()) } #[test] @@ -216,7 +222,7 @@ mod tests { } #[test] - fn test_good_headers() { + fn test_good_headers() -> Result<()> { let fixed_header = concat!( "addr=a@b.example.org; prefer-encrypt=mutual; ", "keydata=xsBNBFzG3j0BCAC6iNhT8zydvCXi8LI/gFnkadMbfmSE/rTJskRRra/utGbLyDta/yTrJg", @@ -242,7 +248,7 @@ mod tests { " wm6qxo7xegRhmEl5uZ16zwyj4qz+xkjGy25Of5mWfUDoNw7OT7sjUbHOOMc=" ); - let ah = Aheader::from_str(fixed_header).expect("failed to parse"); + let ah = Aheader::from_str(fixed_header)?; assert_eq!(ah.addr, "a@b.example.org"); assert_eq!(ah.prefer_encrypt, EncryptPreference::Mutual); assert_eq!(format!("{}", ah), fixed_header); @@ -250,18 +256,17 @@ mod tests { let rendered = ah.to_string(); assert_eq!(rendered, fixed_header); - let ah = Aheader::from_str(&format!(" _foo; __FOO=BAR ;;; addr = a@b.example.org ;\r\n prefer-encrypt = mutual ; keydata = {}", RAWKEY)).expect("failed to parse"); + let ah = Aheader::from_str(&format!(" _foo; __FOO=BAR ;;; addr = a@b.example.org ;\r\n prefer-encrypt = mutual ; keydata = {}", RAWKEY))?; assert_eq!(ah.addr, "a@b.example.org"); assert_eq!(ah.prefer_encrypt, EncryptPreference::Mutual); Aheader::from_str(&format!( "addr=a@b.example.org; prefer-encrypt=ignoreUnknownValues; keydata={}", RAWKEY - )) - .expect("failed to parse"); + ))?; - Aheader::from_str(&format!("addr=a@b.example.org; keydata={}", RAWKEY)) - .expect("failed to parse"); + Aheader::from_str(&format!("addr=a@b.example.org; keydata={}", RAWKEY))?; + Ok(()) } #[test] diff --git a/src/chat.rs b/src/chat.rs index 997b3df6d..bed0d7934 100644 --- a/src/chat.rs +++ b/src/chat.rs @@ -179,7 +179,7 @@ impl ChatId { chat.id } None => { - if Contact::real_exists_by_id(context, contact_id).await + if Contact::real_exists_by_id(context, contact_id).await? || contact_id == DC_CONTACT_ID_SELF { let chat_id = ChatId::get_for_contact(context, contact_id).await?; @@ -324,7 +324,7 @@ impl ChatId { let contact_ids = get_chat_contacts(context, self).await?; for contact_id in contact_ids.into_iter() { let contact = Contact::get_by_id(context, contact_id).await?; - if contact.is_verified(context).await != VerifiedStatus::BidirectVerified { + if contact.is_verified(context).await? != VerifiedStatus::BidirectVerified { bail!("{} is not verified.", contact.get_display_name()); } } @@ -499,7 +499,7 @@ impl ChatId { job::kill_action(context, Action::Housekeeping).await?; let j = job::Job::new(Action::Housekeeping, 0, Params::new(), 10); - job::add(context, j).await; + job::add(context, j).await?; if chat.is_self_talk() { let mut msg = Message::new(Viewtype::Text); @@ -605,7 +605,7 @@ impl ChatId { } let chat = Chat::load_from_db(context, self).await?; - if !chat.can_send(context).await { + if !chat.can_send(context).await? { bail!("Can't set a draft: Can't send"); } @@ -940,13 +940,13 @@ impl Chat { } /// Returns true if user can send messages to this chat. - pub async fn can_send(&self, context: &Context) -> bool { - !self.id.is_special() + pub async fn can_send(&self, context: &Context) -> Result { + Ok(!self.id.is_special() && !self.is_device_talk() && !self.is_mailing_list() && !self.is_contact_request() && (self.typ == Chattype::Single - || is_contact_in_chat(context, self.id, DC_CONTACT_ID_SELF).await) + || is_contact_in_chat(context, self.id, DC_CONTACT_ID_SELF).await?)) } pub async fn update_param(&mut self, context: &Context) -> Result<()> { @@ -1097,7 +1097,7 @@ impl Chat { } if self.typ == Chattype::Group - && !is_contact_in_chat(context, self.id, DC_CONTACT_ID_SELF).await + && !is_contact_in_chat(context, self.id, DC_CONTACT_ID_SELF).await? { context.emit_event(EventType::ErrorSelfNotInGroup( "Cannot send message; self not in group.".into(), @@ -1667,7 +1667,7 @@ async fn prepare_msg_common( chat_id.unarchive(context).await?; let mut chat = Chat::load_from_db(context, chat_id).await?; - ensure!(chat.can_send(context).await, "cannot send to {}", chat_id); + ensure!(chat.can_send(context).await?, "cannot send to {}", chat_id); // The OutPreparing state is set by dc_prepare_msg() before it // calls this function and the message is left in the OutPreparing @@ -1686,20 +1686,24 @@ async fn prepare_msg_common( } /// Returns whether a contact is in a chat or not. -pub async fn is_contact_in_chat(context: &Context, chat_id: ChatId, contact_id: u32) -> bool { +pub async fn is_contact_in_chat( + context: &Context, + chat_id: ChatId, + contact_id: u32, +) -> Result { // this function works for group and for normal chats, however, it is more useful // for group chats. // DC_CONTACT_ID_SELF may be used to check, if the user itself is in a group // chat (DC_CONTACT_ID_SELF is not added to normal chats) - context + let exists = context .sql .exists( "SELECT COUNT(*) FROM chats_contacts WHERE chat_id=? AND contact_id=?;", paramsv![chat_id, contact_id as i32], ) - .await - .unwrap_or_default() + .await?; + Ok(exists) } /// Send a message defined by a dc_msg_t object to a chat. @@ -1766,7 +1770,7 @@ pub async fn send_msg_sync(context: &Context, chat_id: ChatId, msg: &mut Message async fn send_msg_inner(context: &Context, chat_id: ChatId, msg: &mut Message) -> Result { if let Some(send_job) = prepare_send_msg(context, chat_id, msg).await? { - job::add(context, send_job).await; + job::add(context, send_job).await?; context.emit_event(EventType::MsgsChanged { chat_id: msg.chat_id, @@ -2161,7 +2165,8 @@ pub async fn create_group_chat( .await?; let chat_id = ChatId::new(u32::try_from(row_id)?); - if add_to_chat_contacts_table(context, chat_id, DC_CONTACT_ID_SELF).await { + if !is_contact_in_chat(context, chat_id, DC_CONTACT_ID_SELF).await? { + 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).await?; @@ -2186,25 +2191,15 @@ pub(crate) async fn add_to_chat_contacts_table( context: &Context, chat_id: ChatId, contact_id: u32, -) -> bool { - match context +) -> Result<()> { + context .sql .execute( "INSERT INTO chats_contacts (chat_id, contact_id) VALUES(?, ?)", paramsv![chat_id, contact_id as i32], ) - .await - { - Ok(_) => true, - Err(err) => { - error!( - context, - "could not add {} to chat {} table: {}", contact_id, chat_id, err - ); - - false - } - } + .await?; + Ok(()) } /// remove a contact from the chats_contact table @@ -2212,36 +2207,25 @@ pub(crate) async fn remove_from_chat_contacts_table( context: &Context, chat_id: ChatId, contact_id: u32, -) -> bool { - match context +) -> Result<()> { + context .sql .execute( "DELETE FROM chats_contacts WHERE chat_id=? AND contact_id=?", paramsv![chat_id, contact_id as i32], ) - .await - { - Ok(_) => true, - Err(_) => { - warn!( - context, - "could not remove contact {:?} from chat {:?}", contact_id, chat_id - ); - - false - } - } + .await?; + Ok(()) } /// Adds a contact to the chat. -pub async fn add_contact_to_chat(context: &Context, chat_id: ChatId, contact_id: u32) -> bool { - match add_contact_to_chat_ex(context, chat_id, contact_id, false).await { - Ok(res) => res, - Err(err) => { - error!(context, "failed to add contact: {}", err); - false - } - } +pub async fn add_contact_to_chat( + context: &Context, + chat_id: ChatId, + contact_id: u32, +) -> Result<()> { + add_contact_to_chat_ex(context, chat_id, contact_id, false).await?; + Ok(()) } pub(crate) async fn add_contact_to_chat_ex( @@ -2264,13 +2248,13 @@ pub(crate) async fn add_contact_to_chat_ex( chat_id ); ensure!( - Contact::real_exists_by_id(context, contact_id).await || contact_id == DC_CONTACT_ID_SELF, + Contact::real_exists_by_id(context, contact_id).await? || contact_id == DC_CONTACT_ID_SELF, "invalid contact_id {} for adding to group", contact_id ); ensure!(!chat.is_mailing_list(), "Mailing lists can't be changed"); - if !is_contact_in_chat(context, chat_id, DC_CONTACT_ID_SELF).await { + if !is_contact_in_chat(context, chat_id, DC_CONTACT_ID_SELF).await? { /* we should respect this - whatever we send to the group, it gets discarded anyway! */ context.emit_event(EventType::ErrorSelfNotInGroup( "Cannot add contact to group; self not in group.".into(), @@ -2295,14 +2279,14 @@ pub(crate) async fn add_contact_to_chat_ex( return Ok(false); } - if is_contact_in_chat(context, chat_id, contact_id).await { + if is_contact_in_chat(context, chat_id, contact_id).await? { if !from_handshake { return Ok(true); } } else { // else continue and send status mail if chat.is_protected() - && contact.is_verified(context).await != VerifiedStatus::BidirectVerified + && contact.is_verified(context).await? != VerifiedStatus::BidirectVerified { error!( context, @@ -2310,9 +2294,10 @@ pub(crate) async fn add_contact_to_chat_ex( ); return Ok(false); } - if !add_to_chat_contacts_table(context, chat_id, contact_id).await { + if is_contact_in_chat(context, chat_id, contact_id).await? { return Ok(false); } + add_to_chat_contacts_table(context, chat_id, contact_id).await?; } if chat.param.get_int(Param::Unpromoted).unwrap_or_default() == 0 { msg.viewtype = Viewtype::Text; @@ -2480,7 +2465,7 @@ pub async fn remove_contact_from_chat( /* this allows to delete pending references to deleted contacts. Of course, this should _not_ happen. */ if let Ok(chat) = Chat::load_from_db(context, chat_id).await { if chat.typ == Chattype::Group { - if !is_contact_in_chat(context, chat_id, DC_CONTACT_ID_SELF).await { + if !is_contact_in_chat(context, chat_id, DC_CONTACT_ID_SELF).await? { context.emit_event(EventType::ErrorSelfNotInGroup( "Cannot remove contact from chat; self not in group.".into(), )); @@ -2518,7 +2503,9 @@ pub async fn remove_contact_from_chat( // in order to correctly determine encryption so if we // removed it first, it would complicate the // check/encryption logic. - success = remove_from_chat_contacts_table(context, chat_id, contact_id).await; + success = remove_from_chat_contacts_table(context, chat_id, contact_id) + .await + .is_ok(); context.emit_event(EventType::ChatModified(chat_id)); } } @@ -2574,7 +2561,7 @@ pub async fn set_chat_name(context: &Context, chat_id: ChatId, new_name: &str) - if chat.typ == Chattype::Group || chat.typ == Chattype::Mailinglist { if chat.name == new_name { success = true; - } else if !is_contact_in_chat(context, chat_id, DC_CONTACT_ID_SELF).await { + } else if !is_contact_in_chat(context, chat_id, DC_CONTACT_ID_SELF).await? { context.emit_event(EventType::ErrorSelfNotInGroup( "Cannot set chat name; self not in group".into(), )); @@ -2640,7 +2627,7 @@ pub async fn set_chat_profile_image( "Failed to set profile image; group does not exist" ); /* we should respect this - whatever we send to the group, it gets discarded anyway! */ - if !is_contact_in_chat(context, chat_id, DC_CONTACT_ID_SELF).await { + if !is_contact_in_chat(context, chat_id, DC_CONTACT_ID_SELF).await? { context.emit_event(EventType::ErrorSelfNotInGroup( "Cannot set chat profile image; self not in group.".into(), )); @@ -2690,7 +2677,7 @@ pub async fn forward_msgs(context: &Context, msg_ids: &[MsgId], chat_id: ChatId) chat_id.unarchive(context).await?; if let Ok(mut chat) = Chat::load_from_db(context, chat_id).await { - ensure!(chat.can_send(context).await, "cannot send to {}", chat_id); + ensure!(chat.can_send(context).await?, "cannot send to {}", chat_id); curr_timestamp = dc_create_smeared_timestamps(context, msg_ids.len()).await; let ids = context .sql @@ -2755,7 +2742,7 @@ pub async fn forward_msgs(context: &Context, msg_ids: &[MsgId], chat_id: ChatId) curr_timestamp += 1; new_msg_id = chat.prepare_msg_raw(context, &mut msg, fresh10).await?; if let Some(send_job) = job::send_msg_job(context, new_msg_id).await? { - job::add(context, send_job).await; + job::add(context, send_job).await?; } } created_chats.push(chat_id); @@ -3014,12 +3001,8 @@ pub(crate) async fn add_info_msg( chat_id: ChatId, text: impl AsRef, timestamp: i64, -) { - if let Err(e) = - add_info_msg_with_cmd(context, chat_id, text, SystemMessage::Unknown, timestamp).await - { - warn!(context, "Could not add info msg: {}", e); - } +) -> Result { + add_info_msg_with_cmd(context, chat_id, text, SystemMessage::Unknown, timestamp).await } #[cfg(test)] @@ -3141,7 +3124,7 @@ mod tests { } #[async_std::test] - async fn test_self_talk() { + async fn test_self_talk() -> Result<()> { let t = TestContext::new().await; let chat = &t.get_self_chat().await; assert_eq!(DC_CONTACT_ID_SELF, 1); @@ -3149,9 +3132,10 @@ mod tests { assert!(chat.is_self_talk()); assert!(chat.visibility == ChatVisibility::Normal); assert!(!chat.is_device_talk()); - assert!(chat.can_send(&t).await); + assert!(chat.can_send(&t).await?); assert_eq!(chat.name, stock_str::saved_messages(&t).await); - assert!(chat.get_profile_image(&t).await.unwrap().is_some()); + assert!(chat.get_profile_image(&t).await?.is_some()); + Ok(()) } #[async_std::test] @@ -3190,7 +3174,7 @@ mod tests { } #[async_std::test] - async fn test_add_device_msg_labelled() { + async fn test_add_device_msg_labelled() -> Result<()> { let t = TestContext::new().await; // add two device-messages with the same label (second attempt is not added) @@ -3207,9 +3191,7 @@ mod tests { assert!(msg2_id.as_ref().unwrap().is_unset()); // check added message - let msg1 = message::Message::load_from_db(&t, *msg1_id.as_ref().unwrap()).await; - assert!(msg1.is_ok()); - let msg1 = msg1.unwrap(); + let msg1 = message::Message::load_from_db(&t, *msg1_id.as_ref().unwrap()).await?; assert_eq!(msg1_id.as_ref().unwrap(), &msg1.id); assert_eq!(msg1.text.as_ref().unwrap(), "first message"); assert_eq!(msg1.from_id, DC_CONTACT_ID_DEVICE); @@ -3220,28 +3202,25 @@ mod tests { // check device chat let chat_id = msg1.chat_id; - assert_eq!(chat_id.get_msg_cnt(&t).await.unwrap(), 1); + assert_eq!(chat_id.get_msg_cnt(&t).await?, 1); assert!(!chat_id.is_special()); - let chat = Chat::load_from_db(&t, chat_id).await; - assert!(chat.is_ok()); - let chat = chat.unwrap(); + let chat = Chat::load_from_db(&t, chat_id).await?; assert_eq!(chat.get_type(), Chattype::Single); assert!(chat.is_device_talk()); assert!(!chat.is_self_talk()); - assert!(!chat.can_send(&t).await); + assert!(!chat.can_send(&t).await?); assert_eq!(chat.name, stock_str::device_messages(&t).await); - assert!(chat.get_profile_image(&t).await.unwrap().is_some()); + assert!(chat.get_profile_image(&t).await?.is_some()); // delete device message, make sure it is not added again - message::delete_msgs(&t, &[*msg1_id.as_ref().unwrap()]) - .await - .unwrap(); + message::delete_msgs(&t, &[*msg1_id.as_ref().unwrap()]).await?; let msg1 = message::Message::load_from_db(&t, *msg1_id.as_ref().unwrap()).await; assert!(msg1.is_err() || msg1.unwrap().chat_id.is_trash()); let msg3_id = add_device_msg(&t, Some("any-label"), Some(&mut msg2)).await; assert!(msg3_id.is_ok()); assert!(msg2_id.as_ref().unwrap().is_unset()); + Ok(()) } #[async_std::test] @@ -3566,24 +3545,21 @@ mod tests { } #[async_std::test] - async fn test_shall_attach_selfavatar() { + async fn test_shall_attach_selfavatar() -> Result<()> { let t = TestContext::new().await; - let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo") - .await - .unwrap(); - assert!(!shall_attach_selfavatar(&t, chat_id).await.unwrap()); + let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?; + assert!(!shall_attach_selfavatar(&t, chat_id).await?); let (contact_id, _) = - Contact::add_or_lookup(&t, "", "foo@bar.org", Origin::IncomingUnknownTo) - .await - .unwrap(); - add_contact_to_chat(&t, chat_id, contact_id).await; - assert!(!shall_attach_selfavatar(&t, chat_id).await.unwrap()); - t.set_config(Config::Selfavatar, None).await.unwrap(); // setting to None also forces re-sending - assert!(shall_attach_selfavatar(&t, chat_id).await.unwrap()); + Contact::add_or_lookup(&t, "", "foo@bar.org", Origin::IncomingUnknownTo).await?; + add_contact_to_chat(&t, chat_id, contact_id).await?; + assert!(!shall_attach_selfavatar(&t, chat_id).await?); + t.set_config(Config::Selfavatar, None).await?; // setting to None also forces re-sending + assert!(shall_attach_selfavatar(&t, chat_id).await?); - assert!(chat_id.set_selfavatar_timestamp(&t, time()).await.is_ok()); - assert!(!shall_attach_selfavatar(&t, chat_id).await.unwrap()); + chat_id.set_selfavatar_timestamp(&t, time()).await?; + assert!(!shall_attach_selfavatar(&t, chat_id).await?); + Ok(()) } #[async_std::test] @@ -3638,12 +3614,10 @@ mod tests { } #[async_std::test] - async fn test_add_info_msg() { + async fn test_add_info_msg() -> Result<()> { let t = TestContext::new().await; - let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo") - .await - .unwrap(); - add_info_msg(&t, chat_id, "foo info", 200000).await; + let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?; + add_info_msg(&t, chat_id, "foo info", 200000).await?; let msg = t.get_last_msg_in(chat_id).await; assert_eq!(msg.get_chat_id(), chat_id); @@ -3651,6 +3625,7 @@ mod tests { assert_eq!(msg.get_text().unwrap(), "foo info"); assert!(msg.is_info()); assert_eq!(msg.get_info_type(), SystemMessage::Unknown); + Ok(()) } #[async_std::test] @@ -3807,42 +3782,24 @@ mod tests { } #[async_std::test] - async fn test_group_with_removed_message_id() { + async fn test_group_with_removed_message_id() -> Result<()> { // Alice creates a group with Bob, sends a message to bob let alice = TestContext::new_alice().await; let bob = TestContext::new_bob().await; - alice - .set_config(Config::ShowEmails, Some("2")) - .await - .unwrap(); - bob.set_config(Config::ShowEmails, Some("2")).await.unwrap(); + alice.set_config(Config::ShowEmails, Some("2")).await?; + bob.set_config(Config::ShowEmails, Some("2")).await?; let (contact_id, _) = - Contact::add_or_lookup(&alice, "", "bob@example.net", Origin::ManuallyCreated) - .await - .unwrap(); - let alice_chat_id = create_group_chat(&alice, ProtectionStatus::Unprotected, "grp") - .await - .unwrap(); - let alice_chat = Chat::load_from_db(&alice, alice_chat_id).await.unwrap(); + Contact::add_or_lookup(&alice, "", "bob@example.net", Origin::ManuallyCreated).await?; + let alice_chat_id = create_group_chat(&alice, ProtectionStatus::Unprotected, "grp").await?; + let alice_chat = Chat::load_from_db(&alice, alice_chat_id).await?; - add_contact_to_chat(&alice, alice_chat_id, contact_id).await; + add_contact_to_chat(&alice, alice_chat_id, contact_id).await?; + assert_eq!(get_chat_contacts(&alice, alice_chat_id).await?.len(), 2); + send_text_msg(&alice, alice_chat_id, "hi!".to_string()).await?; assert_eq!( - get_chat_contacts(&alice, alice_chat_id) - .await - .unwrap() - .len(), - 2 - ); - send_text_msg(&alice, alice_chat_id, "hi!".to_string()) - .await - .unwrap(); - assert_eq!( - get_chat_msgs(&alice, alice_chat_id, 0, None) - .await - .unwrap() - .len(), + get_chat_msgs(&alice, alice_chat_id, 0, None).await?.len(), 1 ); @@ -3858,17 +3815,15 @@ mod tests { .unwrap(); let msg = bob.get_last_msg().await; - let bob_chat = Chat::load_from_db(&bob, msg.chat_id).await.unwrap(); + let bob_chat = Chat::load_from_db(&bob, msg.chat_id).await?; assert_eq!(bob_chat.grpid, alice_chat.grpid); // Bob accepts contact request. - bob_chat.id.unblock(&bob).await.unwrap(); + bob_chat.id.unblock(&bob).await?; // Bob answers - simulate a normal MUA by not setting `Chat-*`-headers; // moreover, Bob's SMTP-server also replaces the `Message-ID:`-header - send_text_msg(&bob, bob_chat.id, "ho!".to_string()) - .await - .unwrap(); + send_text_msg(&bob, bob_chat.id, "ho!".to_string()).await?; let msg = bob.pop_sent_msg().await.payload(); let msg = msg.replace("Message-ID: Result<()> { ctx, job::Job::new(Action::FetchExistingMsgs, 0, Params::new(), 0), ) - .await; + .await?; progress!(ctx, 940); update_device_chats_handle.await?; diff --git a/src/contact.rs b/src/contact.rs index 80e500867..12e1fc492 100644 --- a/src/contact.rs +++ b/src/contact.rs @@ -2,7 +2,7 @@ use std::convert::{TryFrom, TryInto}; -use anyhow::{bail, ensure, format_err, Result}; +use anyhow::{bail, ensure, format_err, Context as _, Result}; use async_std::path::PathBuf; use deltachat_derive::{FromSql, ToSql}; use itertools::Itertools; @@ -174,6 +174,12 @@ pub enum VerifiedStatus { BidirectVerified = 2, } +impl Default for VerifiedStatus { + fn default() -> Self { + Self::Unverified + } +} + impl Contact { pub async fn load_from_db(context: &Context, contact_id: u32) -> Result { let mut contact = context @@ -229,11 +235,9 @@ impl Contact { } /// Check if a contact is blocked. - pub async fn is_blocked_load(context: &Context, id: u32) -> bool { - Self::load_from_db(context, id) - .await - .map(|contact| contact.blocked) - .unwrap_or_default() + pub async fn is_blocked_load(context: &Context, id: u32) -> Result { + let blocked = Self::load_from_db(context, id).await?.blocked; + Ok(blocked) } /// Block the given contact. @@ -263,7 +267,7 @@ impl Contact { let (contact_id, sth_modified) = Contact::add_or_lookup(context, &name, &addr, Origin::ManuallyCreated).await?; - let blocked = Contact::is_blocked_load(context, contact_id).await; + let blocked = Contact::is_blocked_load(context, contact_id).await?; match sth_modified { Modifier::None => {} Modifier::Modified | Modifier::Created => { @@ -754,12 +758,9 @@ impl Contact { /// Get blocked contacts. pub async fn get_all_blocked(context: &Context) -> Result> { - if let Err(e) = Contact::update_blocked_mailinglist_contacts(context).await { - warn!( - context, - "Cannot update blocked mailinglist contacts: {:?}", e - ); - } + Contact::update_blocked_mailinglist_contacts(context) + .await + .context("cannot update blocked mailinglist contacts")?; let list = context .sql @@ -1018,7 +1019,7 @@ impl Contact { /// /// The UI may draw a checkbox or something like that beside verified contacts. /// - pub async fn is_verified(&self, context: &Context) -> VerifiedStatus { + pub async fn is_verified(&self, context: &Context) -> Result { self.is_verified_ex(context, None).await } @@ -1029,54 +1030,46 @@ impl Contact { &self, context: &Context, peerstate: Option<&Peerstate>, - ) -> VerifiedStatus { + ) -> Result { // We're always sort of secured-verified as we could verify the key on this device any time with the key // on this device if self.id == DC_CONTACT_ID_SELF { - return VerifiedStatus::BidirectVerified; + return Ok(VerifiedStatus::BidirectVerified); } if let Some(peerstate) = peerstate { if peerstate.verified_key.is_some() { - return VerifiedStatus::BidirectVerified; + return Ok(VerifiedStatus::BidirectVerified); } } - let peerstate = match Peerstate::from_addr(context, &self.addr).await { - Ok(peerstate) => peerstate, - Err(err) => { - warn!( - context, - "Failed to load peerstate for address {}: {}", self.addr, err - ); - return VerifiedStatus::Unverified; - } - }; - - if let Some(ps) = peerstate { - if ps.verified_key.is_some() { - return VerifiedStatus::BidirectVerified; + if let Some(peerstate) = Peerstate::from_addr(context, &self.addr).await? { + if peerstate.verified_key.is_some() { + return Ok(VerifiedStatus::BidirectVerified); } } - VerifiedStatus::Unverified + Ok(VerifiedStatus::Unverified) } - pub async fn addr_equals_contact(context: &Context, addr: &str, contact_id: u32) -> bool { + pub async fn addr_equals_contact( + context: &Context, + addr: &str, + contact_id: u32, + ) -> Result { if addr.is_empty() { - return false; + return Ok(false); } - if let Ok(contact) = Contact::load_from_db(context, contact_id).await { - if !contact.addr.is_empty() { - let normalized_addr = addr_normalize(addr); - if contact.addr == normalized_addr { - return true; - } + let contact = Contact::load_from_db(context, contact_id).await?; + if !contact.addr.is_empty() { + let normalized_addr = addr_normalize(addr); + if contact.addr == normalized_addr { + return Ok(true); } } - false + Ok(false) } pub async fn get_real_cnt(context: &Context) -> Result { @@ -1094,19 +1087,19 @@ impl Contact { Ok(count) } - pub async fn real_exists_by_id(context: &Context, contact_id: u32) -> bool { - if !context.sql.is_open().await || contact_id <= DC_CONTACT_ID_LAST_SPECIAL { - return false; + pub async fn real_exists_by_id(context: &Context, contact_id: u32) -> Result { + if contact_id <= DC_CONTACT_ID_LAST_SPECIAL { + return Ok(false); } - context + let exists = context .sql .exists( "SELECT COUNT(*) FROM contacts WHERE id=?;", paramsv![contact_id as i32], ) - .await - .unwrap_or_default() + .await?; + Ok(exists) } pub async fn scaleup_origin_by_id( diff --git a/src/dc_receive_imf.rs b/src/dc_receive_imf.rs index 862b549bd..7e6cb78ab 100644 --- a/src/dc_receive_imf.rs +++ b/src/dc_receive_imf.rs @@ -298,7 +298,7 @@ pub(crate) async fn dc_receive_imf_inner( 0, ), ) - .await; + .await?; } } else if insert_msg_id .needs_move(context, server_folder.as_ref()) @@ -311,7 +311,7 @@ pub(crate) async fn dc_receive_imf_inner( context, job::Job::new(Action::MoveMsg, insert_msg_id.to_u32(), Params::new(), 0), ) - .await; + .await?; } else if !mime_parser.mdn_reports.is_empty() && mime_parser.has_chat_version() { // This is a Delta Chat MDN. Mark as read. job::add( @@ -323,7 +323,7 @@ pub(crate) async fn dc_receive_imf_inner( 0, ), ) - .await; + .await?; } } @@ -550,7 +550,7 @@ async fn add_parts( // In lookup_chat_by_reply() and create_or_lookup_group(), it can happen that the message is put into a chat // but the From-address is not a member of this chat. if let Some(chat_id) = chat_id { - if !chat::is_contact_in_chat(context, chat_id, from_id as u32).await { + if !chat::is_contact_in_chat(context, chat_id, from_id as u32).await? { let chat = Chat::load_from_db(context, chat_id).await?; if chat.is_protected() { let s = stock_str::unknown_sender_for_chat(context).await; @@ -774,7 +774,7 @@ async fn add_parts( } } if chat_id.is_none() && allow_creation { - let create_blocked = if !Contact::is_blocked_load(context, to_id).await { + let create_blocked = if !Contact::is_blocked_load(context, to_id).await? { Blocked::Not } else { Blocked::Request @@ -915,7 +915,7 @@ async fn add_parts( stock_ephemeral_timer_changed(context, ephemeral_timer, from_id).await, sort_timestamp, ) - .await; + .await?; } } } else { @@ -974,7 +974,7 @@ async fn add_parts( format!("Cannot set protection: {}", e), sort_timestamp, ) - .await; + .await?; return Ok(chat_id); // do not return an error as this would result in retrying the message } } @@ -1680,7 +1680,7 @@ async fn create_or_lookup_group( .update_timestamp(context, Param::MemberListTimestamp, sent_timestamp) .await? { - if !chat::is_contact_in_chat(context, chat_id, DC_CONTACT_ID_SELF).await { + if !chat::is_contact_in_chat(context, chat_id, DC_CONTACT_ID_SELF).await? { // Members could have been removed while we were // absent. We can't use existing member list and need to // start from scratch. @@ -1693,20 +1693,20 @@ async fn create_or_lookup_group( .await .ok(); - chat::add_to_chat_contacts_table(context, chat_id, DC_CONTACT_ID_SELF).await; + chat::add_to_chat_contacts_table(context, chat_id, DC_CONTACT_ID_SELF).await?; } if from_id > DC_CONTACT_ID_LAST_SPECIAL - && !Contact::addr_equals_contact(context, &self_addr, from_id).await - && !chat::is_contact_in_chat(context, chat_id, from_id).await + && !Contact::addr_equals_contact(context, &self_addr, from_id).await? + && !chat::is_contact_in_chat(context, chat_id, from_id).await? { - chat::add_to_chat_contacts_table(context, chat_id, from_id).await; + chat::add_to_chat_contacts_table(context, chat_id, from_id).await?; } for &to_id in to_ids.iter() { info!(context, "adding to={:?} to chat id={}", to_id, chat_id); - if !Contact::addr_equals_contact(context, &self_addr, to_id).await - && !chat::is_contact_in_chat(context, chat_id, to_id).await + 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).await; + chat::add_to_chat_contacts_table(context, chat_id, to_id).await?; } } send_EVENT_CHAT_MODIFIED = true; @@ -1716,7 +1716,7 @@ async fn create_or_lookup_group( .update_timestamp(context, Param::MemberListTimestamp, sent_timestamp) .await? { - chat::remove_from_chat_contacts_table(context, chat_id, contact_id).await; + chat::remove_from_chat_contacts_table(context, chat_id, contact_id).await?; send_EVENT_CHAT_MODIFIED = true; } } @@ -1832,7 +1832,7 @@ async fn create_or_lookup_mailinglist( )) })?; - chat::add_to_chat_contacts_table(context, chat_id, DC_CONTACT_ID_SELF).await; + chat::add_to_chat_contacts_table(context, chat_id, DC_CONTACT_ID_SELF).await?; Ok(Some((chat_id, Blocked::Request))) } else { info!(context, "creating list forbidden by caller"); @@ -1926,7 +1926,7 @@ async fn create_adhoc_group( ) .await?; for &member_id in member_ids.iter() { - chat::add_to_chat_contacts_table(context, new_chat_id, member_id).await; + chat::add_to_chat_contacts_table(context, new_chat_id, member_id).await?; } context.emit_event(EventType::ChatModified(new_chat_id)); @@ -2047,7 +2047,7 @@ async fn check_verified_properties( let peerstate = Peerstate::from_addr(context, contact.get_addr()).await?; if peerstate.is_none() - || contact.is_verified_ex(context, peerstate.as_ref()).await + || contact.is_verified_ex(context, peerstate.as_ref()).await? != VerifiedStatus::BidirectVerified { bail!( @@ -2511,24 +2511,22 @@ mod tests { } #[async_std::test] - async fn test_read_receipt_and_unarchive() { + async fn test_read_receipt_and_unarchive() -> Result<()> { // create alice's account let t = TestContext::new_alice().await; - let bob_id = Contact::create(&t, "bob", "bob@example.com").await.unwrap(); - let one2one_id = ChatId::create_for_contact(&t, bob_id).await.unwrap(); + let bob_id = Contact::create(&t, "bob", "bob@example.com").await?; + let one2one_id = ChatId::create_for_contact(&t, bob_id).await?; one2one_id .set_visibility(&t, ChatVisibility::Archived) .await .unwrap(); - let one2one = Chat::load_from_db(&t, one2one_id).await.unwrap(); + let one2one = Chat::load_from_db(&t, one2one_id).await?; assert!(one2one.get_visibility() == ChatVisibility::Archived); // create a group with bob, archive group - let group_id = chat::create_group_chat(&t, ProtectionStatus::Unprotected, "foo") - .await - .unwrap(); - chat::add_contact_to_chat(&t, group_id, bob_id).await; + let group_id = chat::create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?; + chat::add_contact_to_chat(&t, group_id, bob_id).await?; assert_eq!( chat::get_chat_msgs(&t, group_id, 0, None) .await @@ -2538,16 +2536,14 @@ mod tests { ); group_id .set_visibility(&t, ChatVisibility::Archived) - .await - .unwrap(); - let group = Chat::load_from_db(&t, group_id).await.unwrap(); + .await?; + let group = Chat::load_from_db(&t, group_id).await?; assert!(group.get_visibility() == ChatVisibility::Archived); // everything archived, chatlist should be empty assert_eq!( Chatlist::try_load(&t, DC_GCL_NO_SPECIALS, None, None) - .await - .unwrap() + .await? .len(), 0 ); @@ -2575,13 +2571,12 @@ mod tests { 1, false, ) - .await - .unwrap(); + .await?; let msg = get_chat_msg(&t, group_id, 0, 1).await; assert_eq!(msg.is_dc_message, MessengerMessage::Yes); assert_eq!(msg.text.unwrap(), "hello"); assert_eq!(msg.state, MessageState::OutDelivered); - let group = Chat::load_from_db(&t, group_id).await.unwrap(); + let group = Chat::load_from_db(&t, group_id).await?; assert!(group.get_visibility() == ChatVisibility::Normal); // bob sends a read receipt to the group @@ -2622,27 +2617,21 @@ mod tests { 1, false, ) - .await.unwrap(); - assert_eq!( - chat::get_chat_msgs(&t, group_id, 0, None) - .await - .unwrap() - .len(), - 1 - ); - let msg = message::Message::load_from_db(&t, msg.id).await.unwrap(); + .await?; + assert_eq!(chat::get_chat_msgs(&t, group_id, 0, None).await?.len(), 1); + let msg = message::Message::load_from_db(&t, msg.id).await?; assert_eq!(msg.state, MessageState::OutMdnRcvd); // check, the read-receipt has not unarchived the one2one assert_eq!( Chatlist::try_load(&t, DC_GCL_NO_SPECIALS, None, None) - .await - .unwrap() + .await? .len(), 1 ); - let one2one = Chat::load_from_db(&t, one2one_id).await.unwrap(); + let one2one = Chat::load_from_db(&t, one2one_id).await?; assert!(one2one.get_visibility() == ChatVisibility::Archived); + Ok(()) } #[async_std::test] @@ -2937,7 +2926,7 @@ mod tests { } #[async_std::test] - async fn test_parse_ndn_group_msg() { + async fn test_parse_ndn_group_msg() -> Result<()> { let t = TestContext::new().await; t.configure_addr("alice@gmail.com").await; @@ -2959,32 +2948,32 @@ mod tests { 1, false, ) - .await - .unwrap(); + .await?; - let chats = Chatlist::try_load(&t, 0, None, None).await.unwrap(); - let msg_id = chats.get_msg_id(0).unwrap().unwrap(); + let chats = Chatlist::try_load(&t, 0, None, None).await?; + let msg_id = chats.get_msg_id(0)?.unwrap(); let raw = include_bytes!("../test-data/message/gmail_ndn_group.eml"); - dc_receive_imf(&t, raw, "INBOX", 1, false).await.unwrap(); + dc_receive_imf(&t, raw, "INBOX", 1, false).await?; - let msg = Message::load_from_db(&t, msg_id).await.unwrap(); + let msg = Message::load_from_db(&t, msg_id).await?; assert_eq!(msg.state, MessageState::OutFailed); - let msgs = chat::get_chat_msgs(&t, msg.chat_id, 0, None).await.unwrap(); + let msgs = chat::get_chat_msgs(&t, msg.chat_id, 0, None).await?; let msg_id = if let ChatItem::Message { msg_id } = msgs.last().unwrap() { msg_id } else { panic!("Wrong item type"); }; - let last_msg = Message::load_from_db(&t, *msg_id).await.unwrap(); + let last_msg = Message::load_from_db(&t, *msg_id).await?; assert_eq!( last_msg.text, Some(stock_str::failed_sending_to(&t, "assidhfaaspocwaeofi@gmail.com").await,) ); assert_eq!(last_msg.from_id, DC_CONTACT_ID_INFO); + Ok(()) } async fn load_imf_email(context: &Context, imf_raw: &[u8]) -> Message { @@ -3032,57 +3021,43 @@ mod tests { hello back\n"; #[async_std::test] - async fn test_github_mailing_list() { + async fn test_github_mailing_list() -> Result<()> { let t = TestContext::new_alice().await; - t.ctx - .set_config(Config::ShowEmails, Some("2")) - .await - .unwrap(); + t.ctx.set_config(Config::ShowEmails, Some("2")).await?; - dc_receive_imf(&t.ctx, GH_MAILINGLIST, "INBOX", 1, false) - .await - .unwrap(); + dc_receive_imf(&t.ctx, GH_MAILINGLIST, "INBOX", 1, false).await?; - let chats = Chatlist::try_load(&t.ctx, 0, None, None).await.unwrap(); + let chats = Chatlist::try_load(&t.ctx, 0, None, None).await?; assert_eq!(chats.len(), 1); let chat_id = chats.get_chat_id(0); chat_id.accept(&t).await.unwrap(); - let chat = chat::Chat::load_from_db(&t.ctx, chat_id).await.unwrap(); + let chat = chat::Chat::load_from_db(&t.ctx, chat_id).await?; assert!(chat.is_mailing_list()); - assert_eq!(chat.can_send(&t.ctx).await, false); + assert_eq!(chat.can_send(&t.ctx).await?, false); assert_eq!(chat.name, "deltachat/deltachat-core-rust"); - assert_eq!( - chat::get_chat_contacts(&t.ctx, chat_id) - .await - .unwrap() - .len(), - 1 - ); + assert_eq!(chat::get_chat_contacts(&t.ctx, chat_id).await?.len(), 1); - dc_receive_imf(&t.ctx, GH_MAILINGLIST2, "INBOX", 1, false) - .await - .unwrap(); + dc_receive_imf(&t.ctx, GH_MAILINGLIST2, "INBOX", 1, false).await?; - let chats = Chatlist::try_load(&t.ctx, 0, None, None).await.unwrap(); + let chats = Chatlist::try_load(&t.ctx, 0, None, None).await?; assert_eq!(chats.len(), 1); - let contacts = Contact::get_all(&t.ctx, 0, None as Option) - .await - .unwrap(); + let contacts = Contact::get_all(&t.ctx, 0, None as Option).await?; assert_eq!(contacts.len(), 0); // mailing list recipients and senders do not count as "known contacts" let msg1 = get_chat_msg(&t, chat_id, 0, 2).await; - let contact1 = Contact::load_from_db(&t.ctx, msg1.from_id).await.unwrap(); + let contact1 = Contact::load_from_db(&t.ctx, msg1.from_id).await?; assert_eq!(contact1.get_addr(), "notifications@github.com"); assert_eq!(contact1.get_display_name(), "notifications@github.com"); // Make sure this is not "Max Mustermann" or somethinng let msg2 = get_chat_msg(&t, chat_id, 1, 2).await; - let contact2 = Contact::load_from_db(&t.ctx, msg2.from_id).await.unwrap(); + let contact2 = Contact::load_from_db(&t.ctx, msg2.from_id).await?; assert_eq!(contact2.get_addr(), "notifications@github.com"); assert_eq!(msg1.get_override_sender_name().unwrap(), "Max Mustermann"); assert_eq!(msg2.get_override_sender_name().unwrap(), "Github"); + Ok(()) } static DC_MAILINGLIST: &[u8] = b"Received: (Postfix, from userid 1000); Mon, 4 Dec 2006 14:51:39 +0100 (CET)\n\ diff --git a/src/download.rs b/src/download.rs index b49ef0d17..6d6177afc 100644 --- a/src/download.rs +++ b/src/download.rs @@ -85,7 +85,7 @@ impl MsgId { context, Job::new(Action::DownloadMsg, self.to_u32(), Params::new(), 0), ) - .await; + .await?; } } Ok(()) diff --git a/src/e2ee.rs b/src/e2ee.rs index ae7652b6d..a162fa8d6 100644 --- a/src/e2ee.rs +++ b/src/e2ee.rs @@ -161,15 +161,19 @@ pub async fn try_decrypt( let mut peerstate = Peerstate::from_addr(context, &from).await?; // Apply Autocrypt header - if let Some(ref header) = Aheader::from_headers(context, &from, &mail.headers) { - if let Some(ref mut peerstate) = peerstate { - peerstate.apply_header(header, message_time); - peerstate.save_to_db(&context.sql, false).await?; - } else { - let p = Peerstate::from_header(header, message_time); - p.save_to_db(&context.sql, true).await?; - peerstate = Some(p); + match Aheader::from_headers(&from, &mail.headers) { + Ok(Some(ref header)) => { + if let Some(ref mut peerstate) = peerstate { + peerstate.apply_header(header, message_time); + peerstate.save_to_db(&context.sql, false).await?; + } else { + let p = Peerstate::from_header(header, message_time); + p.save_to_db(&context.sql, true).await?; + peerstate = Some(p); + } } + Ok(None) => {} + Err(err) => warn!(context, "Failed to parse Autocrypt header: {}", err), } // Possibly perform decryption diff --git a/src/imap.rs b/src/imap.rs index 38d2e3eaf..2f93e6425 100644 --- a/src/imap.rs +++ b/src/imap.rs @@ -1614,13 +1614,13 @@ async fn precheck_imf( context, job::Job::new(Action::MoveMsg, msg_id.to_u32(), Params::new(), 0), ) - .await; + .await?; } else { job::add( context, job::Job::new(Action::MarkseenMsgOnImap, msg_id.to_u32(), Params::new(), 0), ) - .await; + .await?; } } } else if old_server_folder != server_folder { @@ -1663,7 +1663,7 @@ async fn precheck_imf( context, job::Job::new(Action::MarkseenMsgOnImap, msg_id.to_u32(), Params::new(), 0), ) - .await; + .await?; } } } diff --git a/src/job.rs b/src/job.rs index b60e42439..fa3165f52 100644 --- a/src/job.rs +++ b/src/job.rs @@ -842,15 +842,15 @@ async fn kill_ids(context: &Context, job_ids: &[u32]) -> Result<()> { Ok(()) } -pub async fn action_exists(context: &Context, action: Action) -> bool { - context +pub async fn action_exists(context: &Context, action: Action) -> Result { + let exists = context .sql .exists( "SELECT COUNT(*) FROM jobs WHERE action=?;", paramsv![action], ) - .await - .unwrap_or_default() + .await?; + Ok(exists) } async fn set_delivered(context: &Context, msg_id: MsgId) -> Result<()> { @@ -1185,7 +1185,7 @@ async fn send_mdn(context: &Context, msg: &Message) -> Result<()> { let mut param = Params::new(); param.set(Param::MsgId, msg.id.to_u32().to_string()); - add(context, Job::new(Action::SendMdn, msg.from_id, param, 0)).await; + add(context, Job::new(Action::SendMdn, msg.from_id, param, 0)).await?; Ok(()) } @@ -1196,7 +1196,7 @@ pub(crate) async fn schedule_resync(context: &Context) -> Result<()> { context, Job::new(Action::ResyncFolders, 0, Params::new(), 0), ) - .await; + .await?; Ok(()) } @@ -1211,12 +1211,10 @@ pub fn create(action: Action, foreign_id: u32, param: Params, delay_seconds: i64 } /// Adds a job to the database, scheduling it. -pub async fn add(context: &Context, job: Job) { +pub async fn add(context: &Context, job: Job) -> Result<()> { let action = job.action; let delay_seconds = job.delay_seconds(); - job.save(context).await.unwrap_or_else(|err| { - error!(context, "failed to save job: {}", err); - }); + job.save(context).await.context("failed to save job")?; if delay_seconds == 0 { match action { @@ -1245,6 +1243,7 @@ pub async fn add(context: &Context, job: Job) { } } } + Ok(()) } async fn load_housekeeping_job(context: &Context) -> Result> { diff --git a/src/location.rs b/src/location.rs index 3f739d6d5..1f126be5b 100644 --- a/src/location.rs +++ b/src/location.rs @@ -223,11 +223,11 @@ pub async fn send_locations_to_chat( .unwrap_or_default(); } else if 0 == seconds && is_sending_locations_before { let stock_str = stock_str::msg_location_disabled(context).await; - chat::add_info_msg(context, chat_id, stock_str, now).await; + chat::add_info_msg(context, chat_id, stock_str, now).await?; } context.emit_event(EventType::ChatModified(chat_id)); if 0 != seconds { - schedule_maybe_send_locations(context, false).await; + schedule_maybe_send_locations(context, false).await?; job::add( context, job::Job::new( @@ -237,19 +237,20 @@ pub async fn send_locations_to_chat( seconds + 1, ), ) - .await; + .await?; } Ok(()) } -async fn schedule_maybe_send_locations(context: &Context, force_schedule: bool) { - if force_schedule || !job::action_exists(context, job::Action::MaybeSendLocations).await { +async fn schedule_maybe_send_locations(context: &Context, force_schedule: bool) -> Result<()> { + if force_schedule || !job::action_exists(context, job::Action::MaybeSendLocations).await? { job::add( context, job::Job::new(job::Action::MaybeSendLocations, 0, Params::new(), 60), ) - .await; + .await?; }; + Ok(()) } /// Returns whether `chat_id` or any chat is sending locations. @@ -324,7 +325,7 @@ pub async fn set(context: &Context, latitude: f64, longitude: f64, accuracy: f64 if continue_streaming { context.emit_event(EventType::LocationChanged(Some(DC_CONTACT_ID_SELF))); }; - schedule_maybe_send_locations(context, false).await; + schedule_maybe_send_locations(context, false).await.ok(); } continue_streaming @@ -697,7 +698,7 @@ pub(crate) async fn job_maybe_send_locations(context: &Context, _job: &Job) -> j } if continue_streaming { - schedule_maybe_send_locations(context, true).await; + job_try!(schedule_maybe_send_locations(context, true).await); } job::Status::Finished(Ok(())) } @@ -743,7 +744,7 @@ pub(crate) async fn job_maybe_send_locations_ended( ); let stock_str = stock_str::msg_location_disabled(context).await; - chat::add_info_msg(context, chat_id, stock_str, now).await; + job_try!(chat::add_info_msg(context, chat_id, stock_str, now).await); context.emit_event(EventType::ChatModified(chat_id)); } } diff --git a/src/message.rs b/src/message.rs index 5ebcceab3..f17890531 100644 --- a/src/message.rs +++ b/src/message.rs @@ -1295,7 +1295,7 @@ pub async fn delete_msgs(context: &Context, msg_ids: &[MsgId]) -> Result<()> { context, job::Job::new(Action::DeleteMsgOnImap, msg_id.to_u32(), Params::new(), 0), ) - .await; + .await?; } if !msg_ids.is_empty() { @@ -1308,7 +1308,7 @@ pub async fn delete_msgs(context: &Context, msg_ids: &[MsgId]) -> Result<()> { context, job::Job::new(Action::Housekeeping, 0, Params::new(), 10), ) - .await; + .await?; } Ok(()) } @@ -1383,7 +1383,7 @@ pub async fn markseen_msgs(context: &Context, msg_ids: Vec) -> Result<()> context, job::Job::new(Action::MarkseenMsgOnImap, id.to_u32(), Params::new(), 0), ) - .await; + .await?; updated_chat_ids.insert(curr_chat_id, true); } } @@ -1654,7 +1654,7 @@ async fn ndn_maybe_add_info_msg( text, dc_create_smeared_timestamp(context).await, ) - .await; + .await?; context.emit_event(EventType::ChatModified(chat_id)); } } diff --git a/src/mimefactory.rs b/src/mimefactory.rs index c92584e35..2553f17f0 100644 --- a/src/mimefactory.rs +++ b/src/mimefactory.rs @@ -1605,27 +1605,30 @@ mod tests { } #[async_std::test] - async fn test_subject_in_group() { + async fn test_subject_in_group() -> Result<()> { async fn send_msg_get_subject( t: &TestContext, group_id: ChatId, quote: Option<&Message>, - ) -> String { + ) -> Result { let mut new_msg = Message::new(Viewtype::Text); new_msg.set_text(Some("Hi".to_string())); if let Some(q) = quote { - new_msg.set_quote(t, q).await.unwrap(); + new_msg.set_quote(t, q).await?; } let sent = t.send_msg(group_id, &mut new_msg).await; get_subject(t, sent).await } - async fn get_subject(t: &TestContext, sent: crate::test_utils::SentMessage) -> String { + async fn get_subject( + t: &TestContext, + sent: crate::test_utils::SentMessage, + ) -> Result { let parsed_subject = t.parse_msg(&sent).await.get_subject().unwrap(); - let sent_msg = Message::load_from_db(t, sent.sender_msg_id).await.unwrap(); + let sent_msg = Message::load_from_db(t, sent.sender_msg_id).await?; assert_eq!(parsed_subject, sent_msg.subject); - parsed_subject + Ok(parsed_subject) } // 6. Test that in a group, replies also take the quoted message's subject, while non-replies use the group title as subject @@ -1634,13 +1637,13 @@ mod tests { chat::create_group_chat(&t, chat::ProtectionStatus::Unprotected, "groupname") // TODO encodings, ä .await .unwrap(); - let bob = Contact::create(&t, "", "bob@example.org").await.unwrap(); - chat::add_contact_to_chat(&t, group_id, bob).await; + let bob = Contact::create(&t, "", "bob@example.org").await?; + chat::add_contact_to_chat(&t, group_id, bob).await?; - let subject = send_msg_get_subject(&t, group_id, None).await; + let subject = send_msg_get_subject(&t, group_id, None).await?; assert_eq!(subject, "groupname"); - let subject = send_msg_get_subject(&t, group_id, None).await; + let subject = send_msg_get_subject(&t, group_id, None).await?; assert_eq!(subject, "Re: groupname"); dc_receive_imf( @@ -1662,28 +1665,26 @@ mod tests { 5, false, ) - .await - .unwrap(); + .await?; let message_from_bob = t.get_last_msg().await; - let subject = send_msg_get_subject(&t, group_id, None).await; + let subject = send_msg_get_subject(&t, group_id, None).await?; assert_eq!(subject, "Re: groupname"); - let subject = send_msg_get_subject(&t, group_id, Some(&message_from_bob)).await; + let subject = send_msg_get_subject(&t, group_id, Some(&message_from_bob)).await?; let outgoing_quoting_msg = t.get_last_msg().await; assert_eq!(subject, "Re: Different subject"); - let subject = send_msg_get_subject(&t, group_id, None).await; + let subject = send_msg_get_subject(&t, group_id, None).await?; assert_eq!(subject, "Re: groupname"); - let subject = send_msg_get_subject(&t, group_id, Some(&outgoing_quoting_msg)).await; + let subject = send_msg_get_subject(&t, group_id, Some(&outgoing_quoting_msg)).await?; assert_eq!(subject, "Re: Different subject"); - chat::forward_msgs(&t, &[message_from_bob.id], group_id) - .await - .unwrap(); - let subject = get_subject(&t, t.pop_sent_msg().await).await; + chat::forward_msgs(&t, &[message_from_bob.id], group_id).await?; + let subject = get_subject(&t, t.pop_sent_msg().await).await?; assert_eq!(subject, "Fwd: Different subject"); + Ok(()) } async fn first_subject_str(t: TestContext) -> String { diff --git a/src/peerstate.rs b/src/peerstate.rs index 3e216b126..9e974f471 100644 --- a/src/peerstate.rs +++ b/src/peerstate.rs @@ -277,7 +277,7 @@ impl Peerstate { let msg = stock_str::contact_setup_changed(context, self.addr.clone()).await; - chat::add_info_msg(context, chat_id, msg, timestamp).await; + chat::add_info_msg(context, chat_id, msg, timestamp).await?; context.emit_event(EventType::ChatModified(chat_id)); } else { bail!("contact with peerstate.addr {:?} not found", &self.addr); diff --git a/src/qr.rs b/src/qr.rs index 25432a7e4..6b00a4cbb 100644 --- a/src/qr.rs +++ b/src/qr.rs @@ -167,7 +167,8 @@ async fn decode_openpgp(context: &Context, qr: &str) -> Lot { format!("{} verified.", peerstate.addr), time(), ) - .await; + .await + .ok(); } } else if let Some(addr) = addr { lot.state = LotState::QrFprMismatch; diff --git a/src/quota.rs b/src/quota.rs index 60f690d03..82066a0d8 100644 --- a/src/quota.rs +++ b/src/quota.rs @@ -104,7 +104,7 @@ impl Context { self, job::Job::new(Action::UpdateRecentQuota, 0, Params::new(), 0), ) - .await; + .await?; Ok(()) } diff --git a/src/securejoin.rs b/src/securejoin.rs index 807d1abb4..cab79c356 100644 --- a/src/securejoin.rs +++ b/src/securejoin.rs @@ -160,7 +160,7 @@ impl Bob { /// /// With `group` set to `None` this generates a setup-contact QR code, with `group` set to a /// [`ChatId`] generates a join-group QR code for the given chat. -pub async fn dc_get_securejoin_qr(context: &Context, group: Option) -> Option { +pub async fn dc_get_securejoin_qr(context: &Context, group: Option) -> Result { /*======================================================= ==== Alice - the inviter side ==== ==== Step 1 in "Setup verified contact" protocol ==== @@ -175,28 +175,25 @@ pub async fn dc_get_securejoin_qr(context: &Context, group: Option) -> O let self_addr = match context.get_config(Config::ConfiguredAddr).await { Ok(Some(addr)) => addr, Ok(None) => { - error!(context, "Not configured, cannot generate QR code."); - return None; + bail!("Not configured, cannot generate QR code."); } Err(err) => { - error!( - context, - "Unable to retrieve configuration, cannot generate QR code: {:?}", err + bail!( + "Unable to retrieve configuration, cannot generate QR code: {:?}", + err ); - return None; } }; let self_name = context .get_config(Config::Displayname) - .await - .ok()? + .await? .unwrap_or_default(); let fingerprint: Fingerprint = match get_self_fingerprint(context).await { Some(fp) => fp, None => { - return None; + bail!("No fingerprint, cannot generate QR code."); } }; @@ -207,39 +204,34 @@ pub async fn dc_get_securejoin_qr(context: &Context, group: Option) -> O let qr = if let Some(group) = group { // parameters used: a=g=x=i=s= - if let Ok(chat) = Chat::load_from_db(context, group).await { - let group_name = chat.get_name(); - let group_name_urlencoded = - utf8_percent_encode(group_name, NON_ALPHANUMERIC).to_string(); + let chat = Chat::load_from_db(context, group).await?; + let group_name = chat.get_name(); + let group_name_urlencoded = utf8_percent_encode(group_name, NON_ALPHANUMERIC).to_string(); - Some(format!( - "OPENPGP4FPR:{}#a={}&g={}&x={}&i={}&s={}", - fingerprint.hex(), - self_addr_urlencoded, - &group_name_urlencoded, - &chat.grpid, - &invitenumber, - &auth, - )) - } else { - error!(context, "Cannot get QR-code for chat-id {}", group,); - return None; - } + format!( + "OPENPGP4FPR:{}#a={}&g={}&x={}&i={}&s={}", + fingerprint.hex(), + self_addr_urlencoded, + &group_name_urlencoded, + &chat.grpid, + &invitenumber, + &auth, + ) } else { // parameters used: a=n=i=s= - Some(format!( + format!( "OPENPGP4FPR:{}#a={}&n={}&i={}&s={}", fingerprint.hex(), self_addr_urlencoded, self_name_urlencoded, &invitenumber, &auth, - )) + ) }; - info!(context, "Generated QR code: {}", qr.as_ref().unwrap()); + info!(context, "Generated QR code: {}", qr); - qr + Ok(qr) } async fn get_self_fingerprint(context: &Context) -> Option { @@ -720,7 +712,7 @@ pub(crate) async fn handle_securejoin_handshake( ==========================================================*/ if let Ok(contact) = Contact::get_by_id(context, contact_id).await { - if contact.is_verified(context).await == VerifiedStatus::Unverified { + if contact.is_verified(context).await? == VerifiedStatus::Unverified { warn!(context, "{} invalid.", step); return Ok(HandshakeMessage::Ignore); } @@ -860,7 +852,7 @@ async fn secure_connection_established( "?" }; let msg = stock_str::contact_verified(context, addr).await; - chat::add_info_msg(context, contact_chat_id, msg, time()).await; + chat::add_info_msg(context, contact_chat_id, msg, time()).await?; context.emit_event(EventType::ChatModified(contact_chat_id)); info!(context, "StockMessage::ContactVerified posted to 1:1 chat"); @@ -884,7 +876,7 @@ async fn could_not_establish_secure_connection( ) .await; - chat::add_info_msg(context, contact_chat_id, &msg, time()).await; + chat::add_info_msg(context, contact_chat_id, &msg, time()).await?; error!( context, "StockMessage::ContactNotVerified posted to 1:1 chat ({})", details @@ -958,7 +950,7 @@ mod tests { use crate::test_utils::TestContext; #[async_std::test] - async fn test_setup_contact() { + async fn test_setup_contact() -> Result<()> { let alice = TestContext::new_alice().await; let bob = TestContext::new_bob().await; @@ -975,10 +967,10 @@ mod tests { .await; // Step 1: Generate QR-code, ChatId(0) indicates setup-contact - let qr = dc_get_securejoin_qr(&alice.ctx, None).await.unwrap(); + let qr = dc_get_securejoin_qr(&alice.ctx, None).await?; // Step 2: Bob scans QR-code, sends vc-request - dc_join_securejoin(&bob.ctx, &qr).await.unwrap(); + dc_join_securejoin(&bob.ctx, &qr).await?; let sent = bob.pop_sent_msg().await; assert!(!bob.ctx.has_ongoing().await); @@ -1055,14 +1047,14 @@ mod tests { .await .unwrap(); assert_eq!( - contact_bob.is_verified(&alice.ctx).await, + contact_bob.is_verified(&alice.ctx).await?, VerifiedStatus::Unverified ); // Step 5+6: Alice receives vc-request-with-auth, sends vc-contact-confirm alice.recv_msg(&sent).await; assert_eq!( - contact_bob.is_verified(&alice.ctx).await, + contact_bob.is_verified(&alice.ctx).await?, VerifiedStatus::BidirectVerified ); @@ -1104,14 +1096,14 @@ mod tests { .await .unwrap(); assert_eq!( - contact_bob.is_verified(&bob.ctx).await, + contact_bob.is_verified(&bob.ctx).await?, VerifiedStatus::Unverified ); // Step 7: Bob receives vc-contact-confirm, sends vc-contact-confirm-received bob.recv_msg(&sent).await; assert_eq!( - contact_alice.is_verified(&bob.ctx).await, + contact_alice.is_verified(&bob.ctx).await?, VerifiedStatus::BidirectVerified ); @@ -1142,6 +1134,7 @@ mod tests { msg.get_header(HeaderDef::SecureJoin).unwrap(), "vc-contact-confirm-received" ); + Ok(()) } #[async_std::test] @@ -1152,7 +1145,7 @@ mod tests { } #[async_std::test] - async fn test_setup_contact_bob_knows_alice() { + async fn test_setup_contact_bob_knows_alice() -> Result<()> { let alice = TestContext::new_alice().await; let bob = TestContext::new_bob().await; @@ -1169,7 +1162,7 @@ mod tests { .await; // Ensure Bob knows Alice_FP - let alice_pubkey = SignedPublicKey::load_self(&alice.ctx).await.unwrap(); + let alice_pubkey = SignedPublicKey::load_self(&alice.ctx).await?; let peerstate = Peerstate { addr: "alice@example.com".into(), last_seen: 10, @@ -1185,10 +1178,10 @@ mod tests { to_save: Some(ToSave::All), fingerprint_changed: false, }; - peerstate.save_to_db(&bob.ctx.sql, true).await.unwrap(); + peerstate.save_to_db(&bob.ctx.sql, true).await?; // Step 1: Generate QR-code, ChatId(0) indicates setup-contact - let qr = dc_get_securejoin_qr(&alice.ctx, None).await.unwrap(); + let qr = dc_get_securejoin_qr(&alice.ctx, None).await?; // Step 2+4: Bob scans QR-code, sends vc-request-with-auth, skipping vc-request dc_join_securejoin(&bob.ctx, &qr).await.unwrap(); @@ -1228,10 +1221,7 @@ mod tests { "vc-request-with-auth" ); assert!(msg.get_header(HeaderDef::SecureJoinAuth).is_some()); - let bob_fp = SignedPublicKey::load_self(&bob.ctx) - .await - .unwrap() - .fingerprint(); + let bob_fp = SignedPublicKey::load_self(&bob.ctx).await?.fingerprint(); assert_eq!( *msg.get_header(HeaderDef::SecureJoinFingerprint).unwrap(), bob_fp.hex() @@ -1244,20 +1234,17 @@ mod tests { "bob@example.net", Origin::ManuallyCreated, ) - .await - .unwrap(); - let contact_bob = Contact::load_from_db(&alice.ctx, contact_bob_id) - .await - .unwrap(); + .await?; + let contact_bob = Contact::load_from_db(&alice.ctx, contact_bob_id).await?; assert_eq!( - contact_bob.is_verified(&alice.ctx).await, + contact_bob.is_verified(&alice.ctx).await?, VerifiedStatus::Unverified ); // Step 5+6: Alice receives vc-request-with-auth, sends vc-contact-confirm alice.recv_msg(&sent).await; assert_eq!( - contact_bob.is_verified(&alice.ctx).await, + contact_bob.is_verified(&alice.ctx).await?, VerifiedStatus::BidirectVerified ); @@ -1275,18 +1262,16 @@ mod tests { .await .expect("Error looking up contact") .expect("Contact not found"); - let contact_alice = Contact::load_from_db(&bob.ctx, contact_alice_id) - .await - .unwrap(); + let contact_alice = Contact::load_from_db(&bob.ctx, contact_alice_id).await?; assert_eq!( - contact_bob.is_verified(&bob.ctx).await, + contact_bob.is_verified(&bob.ctx).await?, VerifiedStatus::Unverified ); // Step 7: Bob receives vc-contact-confirm, sends vc-contact-confirm-received bob.recv_msg(&sent).await; assert_eq!( - contact_alice.is_verified(&bob.ctx).await, + contact_alice.is_verified(&bob.ctx).await?, VerifiedStatus::BidirectVerified ); @@ -1297,10 +1282,11 @@ mod tests { msg.get_header(HeaderDef::SecureJoin).unwrap(), "vc-contact-confirm-received" ); + Ok(()) } #[async_std::test] - async fn test_secure_join() { + async fn test_secure_join() -> Result<()> { let alice = TestContext::new_alice().await; let bob = TestContext::new_bob().await; @@ -1316,9 +1302,8 @@ mod tests { }) .await; - let chatid = chat::create_group_chat(&alice.ctx, ProtectionStatus::Protected, "the chat") - .await - .unwrap(); + let chatid = + chat::create_group_chat(&alice.ctx, ProtectionStatus::Protected, "the chat").await?; // Step 1: Generate QR-code, secure-join implied by chatid let qr = dc_get_securejoin_qr(&alice.ctx, Some(chatid)) @@ -1388,10 +1373,7 @@ mod tests { "vg-request-with-auth" ); assert!(msg.get_header(HeaderDef::SecureJoinAuth).is_some()); - let bob_fp = SignedPublicKey::load_self(&bob.ctx) - .await - .unwrap() - .fingerprint(); + let bob_fp = SignedPublicKey::load_self(&bob.ctx).await?.fingerprint(); assert_eq!( *msg.get_header(HeaderDef::SecureJoinFingerprint).unwrap(), bob_fp.hex() @@ -1400,21 +1382,18 @@ mod tests { // Alice should not yet have Bob verified let contact_bob_id = Contact::lookup_id_by_addr(&alice.ctx, "bob@example.net", Origin::Unknown) - .await - .expect("Error looking up contact") + .await? .expect("Contact not found"); - let contact_bob = Contact::load_from_db(&alice.ctx, contact_bob_id) - .await - .unwrap(); + let contact_bob = Contact::load_from_db(&alice.ctx, contact_bob_id).await?; assert_eq!( - contact_bob.is_verified(&alice.ctx).await, + contact_bob.is_verified(&alice.ctx).await?, VerifiedStatus::Unverified ); // Step 5+6: Alice receives vg-request-with-auth, sends vg-member-added alice.recv_msg(&sent).await; assert_eq!( - contact_bob.is_verified(&alice.ctx).await, + contact_bob.is_verified(&alice.ctx).await?, VerifiedStatus::BidirectVerified ); @@ -1432,18 +1411,16 @@ mod tests { .await .expect("Error looking up contact") .expect("Contact not found"); - let contact_alice = Contact::load_from_db(&bob.ctx, contact_alice_id) - .await - .unwrap(); + let contact_alice = Contact::load_from_db(&bob.ctx, contact_alice_id).await?; assert_eq!( - contact_bob.is_verified(&bob.ctx).await, + contact_bob.is_verified(&bob.ctx).await?, VerifiedStatus::Unverified ); // Step 7: Bob receives vg-member-added, sends vg-member-added-received bob.recv_msg(&sent).await; assert_eq!( - contact_alice.is_verified(&bob.ctx).await, + contact_alice.is_verified(&bob.ctx).await?, VerifiedStatus::BidirectVerified ); @@ -1456,8 +1433,9 @@ mod tests { ); let bob_chatid = joiner.await; - let bob_chat = Chat::load_from_db(&bob.ctx, bob_chatid).await.unwrap(); + let bob_chat = Chat::load_from_db(&bob.ctx, bob_chatid).await?; assert!(bob_chat.is_protected()); - assert!(!bob.ctx.has_ongoing().await) + assert!(!bob.ctx.has_ongoing().await); + Ok(()) } }