mirror of
https://github.com/chatmail/core.git
synced 2026-05-02 21:06:31 +03:00
Resultification
This commit is contained in:
@@ -1319,8 +1319,13 @@ pub unsafe extern "C" fn dc_is_contact_in_chat(
|
|||||||
}
|
}
|
||||||
let ctx = &*context;
|
let ctx = &*context;
|
||||||
|
|
||||||
block_on(async move { chat::is_contact_in_chat(ctx, ChatId::new(chat_id), contact_id).await })
|
block_on(chat::is_contact_in_chat(
|
||||||
.into()
|
ctx,
|
||||||
|
ChatId::new(chat_id),
|
||||||
|
contact_id,
|
||||||
|
))
|
||||||
|
.log_err(ctx, "is_contact_in_chat failed")
|
||||||
|
.unwrap_or_default() as libc::c_int
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
@@ -1335,9 +1340,13 @@ pub unsafe extern "C" fn dc_add_contact_to_chat(
|
|||||||
}
|
}
|
||||||
let ctx = &*context;
|
let ctx = &*context;
|
||||||
|
|
||||||
block_on(async move {
|
block_on(chat::add_contact_to_chat(
|
||||||
chat::add_contact_to_chat(ctx, ChatId::new(chat_id), contact_id).await as libc::c_int
|
ctx,
|
||||||
})
|
ChatId::new(chat_id),
|
||||||
|
contact_id,
|
||||||
|
))
|
||||||
|
.log_err(ctx, "Failed to add contact")
|
||||||
|
.is_ok() as libc::c_int
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
@@ -1352,12 +1361,13 @@ pub unsafe extern "C" fn dc_remove_contact_from_chat(
|
|||||||
}
|
}
|
||||||
let ctx = &*context;
|
let ctx = &*context;
|
||||||
|
|
||||||
block_on(async move {
|
block_on(chat::remove_contact_from_chat(
|
||||||
chat::remove_contact_from_chat(ctx, ChatId::new(chat_id), contact_id)
|
ctx,
|
||||||
.await
|
ChatId::new(chat_id),
|
||||||
.map(|_| 1)
|
contact_id,
|
||||||
.unwrap_or_log_default(ctx, "Failed to remove contact")
|
))
|
||||||
})
|
.log_err(ctx, "Failed to remove contact")
|
||||||
|
.is_ok() as libc::c_int
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
@@ -2019,12 +2029,9 @@ pub unsafe extern "C" fn dc_get_securejoin_qr(
|
|||||||
Some(ChatId::new(chat_id))
|
Some(ChatId::new(chat_id))
|
||||||
};
|
};
|
||||||
|
|
||||||
block_on(async move {
|
block_on(securejoin::dc_get_securejoin_qr(ctx, chat_id))
|
||||||
securejoin::dc_get_securejoin_qr(ctx, chat_id)
|
.unwrap_or_else(|_| "".to_string())
|
||||||
.await
|
|
||||||
.unwrap_or_else(|| "".to_string())
|
|
||||||
.strdup()
|
.strdup()
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[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;
|
return 0;
|
||||||
}
|
}
|
||||||
let ffi_chat = &*chat;
|
let ffi_chat = &*chat;
|
||||||
let cxt = &*ffi_chat.context;
|
let ctx = &*ffi_chat.context;
|
||||||
block_on(ffi_chat.chat.can_send(cxt)) as libc::c_int
|
block_on(ffi_chat.chat.can_send(ctx))
|
||||||
|
.log_err(ctx, "can_send failed")
|
||||||
|
.unwrap_or_default() as libc::c_int
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[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 ffi_contact = &*contact;
|
||||||
let ctx = &*ffi_contact.context;
|
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
|
// dc_lot_t
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ extern crate dirs;
|
|||||||
|
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use anyhow::{bail, ensure, Error};
|
use anyhow::{bail, ensure, Result};
|
||||||
use async_std::path::Path;
|
use async_std::path::Path;
|
||||||
use deltachat::chat::{
|
use deltachat::chat::{
|
||||||
self, Chat, ChatId, ChatItem, ChatVisibility, MuteDuration, ProtectionStatus,
|
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<Path>) -> Result<(), anyhow::Error> {
|
async fn poke_eml_file(context: &Context, filename: impl AsRef<Path>) -> Result<()> {
|
||||||
let data = dc_read_file(context, filename).await?;
|
let data = dc_read_file(context, filename).await?;
|
||||||
|
|
||||||
if let Err(err) = dc_receive_imf(context, &data, "import", 0, false).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<str>, 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;
|
let mut lines_out = 0;
|
||||||
for &msg_id in msglist {
|
for &msg_id in msglist {
|
||||||
if msg_id == MsgId::new(DC_MSG_ID_DAYMARKER) {
|
if msg_id == MsgId::new(DC_MSG_ID_DAYMARKER) {
|
||||||
@@ -267,14 +267,14 @@ async fn log_msglist(context: &Context, msglist: &[MsgId]) -> Result<(), Error>
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn log_contactlist(context: &Context, contacts: &[u32]) {
|
async fn log_contactlist(context: &Context, contacts: &[u32]) -> Result<()> {
|
||||||
for contact_id in contacts {
|
for contact_id in contacts {
|
||||||
let line;
|
let line;
|
||||||
let mut line2 = "".to_string();
|
let mut line2 = "".to_string();
|
||||||
if let Ok(contact) = Contact::get_by_id(context, *contact_id).await {
|
let contact = Contact::get_by_id(context, *contact_id).await?;
|
||||||
let name = contact.get_display_name();
|
let name = contact.get_display_name();
|
||||||
let addr = contact.get_addr();
|
let addr = contact.get_addr();
|
||||||
let verified_state = contact.is_verified(context).await;
|
let verified_state = contact.is_verified(context).await?;
|
||||||
let verified_str = if VerifiedStatus::Unverified != verified_state {
|
let verified_str = if VerifiedStatus::Unverified != verified_state {
|
||||||
if verified_state == VerifiedStatus::BidirectVerified {
|
if verified_state == VerifiedStatus::BidirectVerified {
|
||||||
" √√"
|
" √√"
|
||||||
@@ -298,7 +298,7 @@ async fn log_contactlist(context: &Context, contacts: &[u32]) {
|
|||||||
"addr unset"
|
"addr unset"
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
let peerstate = Peerstate::from_addr(context, addr)
|
let peerstate = Peerstate::from_addr(context, &addr)
|
||||||
.await
|
.await
|
||||||
.expect("peerstate error");
|
.expect("peerstate error");
|
||||||
if peerstate.is_some() && *contact_id != 1 {
|
if peerstate.is_some() && *contact_id != 1 {
|
||||||
@@ -310,16 +310,16 @@ async fn log_contactlist(context: &Context, contacts: &[u32]) {
|
|||||||
|
|
||||||
println!("Contact#{}: {}{}", *contact_id, line, line2);
|
println!("Contact#{}: {}{}", *contact_id, line, line2);
|
||||||
}
|
}
|
||||||
}
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn chat_prefix(chat: &Chat) -> &'static str {
|
fn chat_prefix(chat: &Chat) -> &'static str {
|
||||||
chat.typ.into()
|
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() {
|
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 {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
@@ -726,17 +726,9 @@ pub async fn cmdline(context: Context, line: &str, chat_id: &mut ChatId) -> Resu
|
|||||||
ensure!(!arg1.is_empty(), "Argument <contact-id> missing.");
|
ensure!(!arg1.is_empty(), "Argument <contact-id> missing.");
|
||||||
|
|
||||||
let contact_id_0: u32 = arg1.parse()?;
|
let contact_id_0: u32 = arg1.parse()?;
|
||||||
if chat::add_contact_to_chat(
|
chat::add_contact_to_chat(&context, sel_chat.as_ref().unwrap().get_id(), contact_id_0)
|
||||||
&context,
|
.await?;
|
||||||
sel_chat.as_ref().unwrap().get_id(),
|
|
||||||
contact_id_0,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
println!("Contact added to chat.");
|
println!("Contact added to chat.");
|
||||||
} else {
|
|
||||||
bail!("Cannot add contact to chat.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
"removemember" => {
|
"removemember" => {
|
||||||
ensure!(sel_chat.is_some(), "No chat selected.");
|
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?;
|
chat::get_chat_contacts(&context, sel_chat.as_ref().unwrap().get_id()).await?;
|
||||||
println!("Memberlist:");
|
println!("Memberlist:");
|
||||||
|
|
||||||
log_contactlist(&context, &contacts).await;
|
log_contactlist(&context, &contacts).await?;
|
||||||
println!(
|
println!(
|
||||||
"{} contacts\nLocation streaming: {}",
|
"{} contacts\nLocation streaming: {}",
|
||||||
contacts.len(),
|
contacts.len(),
|
||||||
@@ -1090,7 +1082,7 @@ pub async fn cmdline(context: Context, line: &str, chat_id: &mut ChatId) -> Resu
|
|||||||
Some(arg1),
|
Some(arg1),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
log_contactlist(&context, &contacts).await;
|
log_contactlist(&context, &contacts).await?;
|
||||||
println!("{} contacts.", contacts.len());
|
println!("{} contacts.", contacts.len());
|
||||||
}
|
}
|
||||||
"addcontact" => {
|
"addcontact" => {
|
||||||
@@ -1155,7 +1147,7 @@ pub async fn cmdline(context: Context, line: &str, chat_id: &mut ChatId) -> Resu
|
|||||||
}
|
}
|
||||||
"listblocked" => {
|
"listblocked" => {
|
||||||
let contacts = Contact::get_all_blocked(&context).await?;
|
let contacts = Contact::get_all_blocked(&context).await?;
|
||||||
log_contactlist(&context, &contacts).await;
|
log_contactlist(&context, &contacts).await?;
|
||||||
println!("{} blocked contacts.", contacts.len());
|
println!("{} blocked contacts.", contacts.len());
|
||||||
}
|
}
|
||||||
"checkqr" => {
|
"checkqr" => {
|
||||||
|
|||||||
@@ -409,8 +409,8 @@ async fn handle_cmd(
|
|||||||
}
|
}
|
||||||
"getqr" | "getbadqr" => {
|
"getqr" | "getbadqr" => {
|
||||||
ctx.start_io().await;
|
ctx.start_io().await;
|
||||||
let group = arg1.parse::<u32>().ok().map(ChatId::new);
|
let group = arg1.parse::<u32>().ok().map(|id| ChatId::new(id));
|
||||||
if let Some(mut qr) = dc_get_securejoin_qr(&ctx, group).await {
|
let mut qr = dc_get_securejoin_qr(&ctx, group).await?;
|
||||||
if !qr.is_empty() {
|
if !qr.is_empty() {
|
||||||
if arg0 == "getbadqr" && qr.len() > 40 {
|
if arg0 == "getbadqr" && qr.len() > 40 {
|
||||||
qr.replace_range(12..22, "0000000000")
|
qr.replace_range(12..22, "0000000000")
|
||||||
@@ -424,7 +424,6 @@ async fn handle_cmd(
|
|||||||
io::stderr().write_all(&output.stderr).unwrap();
|
io::stderr().write_all(&output.stderr).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
"joinqr" => {
|
"joinqr" => {
|
||||||
ctx.start_io().await;
|
ctx.start_io().await;
|
||||||
if !arg0.is_empty() {
|
if !arg0.is_empty() {
|
||||||
|
|||||||
@@ -2,12 +2,12 @@
|
|||||||
//!
|
//!
|
||||||
//! Parse and create [Autocrypt-headers](https://autocrypt.org/en/latest/level1.html#the-autocrypt-header).
|
//! 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::collections::BTreeMap;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::{fmt, str};
|
use std::{fmt, str};
|
||||||
|
|
||||||
use crate::contact::addr_cmp;
|
use crate::contact::addr_cmp;
|
||||||
use crate::context::Context;
|
|
||||||
use crate::headerdef::{HeaderDef, HeaderDefMap};
|
use crate::headerdef::{HeaderDef, HeaderDefMap};
|
||||||
use crate::key::{DcKey, SignedPublicKey};
|
use crate::key::{DcKey, SignedPublicKey};
|
||||||
|
|
||||||
@@ -37,13 +37,13 @@ impl fmt::Display for EncryptPreference {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl str::FromStr for EncryptPreference {
|
impl str::FromStr for EncryptPreference {
|
||||||
type Err = ();
|
type Err = Error;
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
fn from_str(s: &str) -> Result<Self> {
|
||||||
match s {
|
match s {
|
||||||
"mutual" => Ok(EncryptPreference::Mutual),
|
"mutual" => Ok(EncryptPreference::Mutual),
|
||||||
"nopreference" => Ok(EncryptPreference::NoPreference),
|
"nopreference" => Ok(EncryptPreference::NoPreference),
|
||||||
_ => Err(()),
|
_ => bail!("Cannot parse encryption preference {}", s),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -70,29 +70,28 @@ 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(
|
pub fn from_headers(
|
||||||
context: &Context,
|
|
||||||
wanted_from: &str,
|
wanted_from: &str,
|
||||||
headers: &[mailparse::MailHeader<'_>],
|
headers: &[mailparse::MailHeader<'_>],
|
||||||
) -> Option<Self> {
|
) -> Result<Option<Self>> {
|
||||||
if let Some(value) = headers.get_header_value(HeaderDef::Autocrypt) {
|
if let Some(value) = headers.get_header_value(HeaderDef::Autocrypt) {
|
||||||
match Self::from_str(&value) {
|
let header = Self::from_str(&value)?;
|
||||||
Ok(header) => {
|
if !addr_cmp(&header.addr, wanted_from) {
|
||||||
if addr_cmp(&header.addr, wanted_from) {
|
bail!(
|
||||||
return Some(header);
|
"Autocrypt header address {:?} is not {:?}",
|
||||||
}
|
header.addr,
|
||||||
}
|
wanted_from
|
||||||
Err(err) => {
|
|
||||||
warn!(
|
|
||||||
context,
|
|
||||||
"found invalid autocrypt header {}: {:?}", value, err
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Ok(Some(header))
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Aheader {
|
impl fmt::Display for Aheader {
|
||||||
@@ -120,9 +119,9 @@ impl fmt::Display for Aheader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl str::FromStr for Aheader {
|
impl str::FromStr for Aheader {
|
||||||
type Err = ();
|
type Err = Error;
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
fn from_str(s: &str) -> Result<Self> {
|
||||||
let mut attributes: BTreeMap<String, String> = s
|
let mut attributes: BTreeMap<String, String> = s
|
||||||
.split(';')
|
.split(';')
|
||||||
.filter_map(|a| {
|
.filter_map(|a| {
|
||||||
@@ -136,15 +135,20 @@ impl str::FromStr for Aheader {
|
|||||||
|
|
||||||
let addr = match attributes.remove("addr") {
|
let addr = match attributes.remove("addr") {
|
||||||
Some(addr) => addr,
|
Some(addr) => addr,
|
||||||
None => {
|
None => bail!("Autocrypt header has no addr"),
|
||||||
return Err(());
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
let public_key: SignedPublicKey = attributes
|
let public_key: SignedPublicKey = attributes
|
||||||
.remove("keydata")
|
.remove("keydata")
|
||||||
.ok_or(())
|
.ok_or_else(|| format_err!("keydata attribute is not found"))
|
||||||
.and_then(|raw| SignedPublicKey::from_base64(&raw).or(Err(())))
|
.and_then(|raw| {
|
||||||
.and_then(|key| key.verify().and(Ok(key)).or(Err(())))?;
|
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
|
let prefer_encrypt = attributes
|
||||||
.remove("prefer-encrypt")
|
.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 attributes starting with an underscore can be safely ignored
|
||||||
// Autocrypt-Level0: unknown attribute, treat the header as invalid
|
// Autocrypt-Level0: unknown attribute, treat the header as invalid
|
||||||
if attributes.keys().any(|k| !k.starts_with('_')) {
|
if attributes.keys().any(|k| !k.starts_with('_')) {
|
||||||
return Err(());
|
bail!("Unknown Autocrypt attribute found");
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Aheader {
|
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=";
|
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]
|
#[test]
|
||||||
fn test_from_str() {
|
fn test_from_str() -> Result<()> {
|
||||||
let h: Aheader = format!(
|
let h: Aheader = format!(
|
||||||
"addr=me@mail.com; prefer-encrypt=mutual; keydata={}",
|
"addr=me@mail.com; prefer-encrypt=mutual; keydata={}",
|
||||||
RAWKEY
|
RAWKEY
|
||||||
)
|
)
|
||||||
.parse()
|
.parse()?;
|
||||||
.expect("failed to parse");
|
|
||||||
|
|
||||||
assert_eq!(h.addr, "me@mail.com");
|
assert_eq!(h.addr, "me@mail.com");
|
||||||
assert_eq!(h.prefer_encrypt, EncryptPreference::Mutual);
|
assert_eq!(h.prefer_encrypt, EncryptPreference::Mutual);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
// EncryptPreference::Reset is an internal value, parser should never return it
|
// EncryptPreference::Reset is an internal value, parser should never return it
|
||||||
#[test]
|
#[test]
|
||||||
fn test_from_str_reset() {
|
fn test_from_str_reset() -> Result<()> {
|
||||||
let raw = format!(
|
let raw = format!(
|
||||||
"addr=reset@example.com; prefer-encrypt=reset; keydata={}",
|
"addr=reset@example.com; prefer-encrypt=reset; keydata={}",
|
||||||
RAWKEY
|
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.addr, "reset@example.com");
|
||||||
assert_eq!(h.prefer_encrypt, EncryptPreference::NoPreference);
|
assert_eq!(h.prefer_encrypt, EncryptPreference::NoPreference);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[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 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.addr, "me@mail.com");
|
||||||
assert_eq!(h.prefer_encrypt, EncryptPreference::NoPreference);
|
assert_eq!(h.prefer_encrypt, EncryptPreference::NoPreference);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -216,7 +222,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_good_headers() {
|
fn test_good_headers() -> Result<()> {
|
||||||
let fixed_header = concat!(
|
let fixed_header = concat!(
|
||||||
"addr=a@b.example.org; prefer-encrypt=mutual; ",
|
"addr=a@b.example.org; prefer-encrypt=mutual; ",
|
||||||
"keydata=xsBNBFzG3j0BCAC6iNhT8zydvCXi8LI/gFnkadMbfmSE/rTJskRRra/utGbLyDta/yTrJg",
|
"keydata=xsBNBFzG3j0BCAC6iNhT8zydvCXi8LI/gFnkadMbfmSE/rTJskRRra/utGbLyDta/yTrJg",
|
||||||
@@ -242,7 +248,7 @@ mod tests {
|
|||||||
" wm6qxo7xegRhmEl5uZ16zwyj4qz+xkjGy25Of5mWfUDoNw7OT7sjUbHOOMc="
|
" 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.addr, "a@b.example.org");
|
||||||
assert_eq!(ah.prefer_encrypt, EncryptPreference::Mutual);
|
assert_eq!(ah.prefer_encrypt, EncryptPreference::Mutual);
|
||||||
assert_eq!(format!("{}", ah), fixed_header);
|
assert_eq!(format!("{}", ah), fixed_header);
|
||||||
@@ -250,18 +256,17 @@ mod tests {
|
|||||||
let rendered = ah.to_string();
|
let rendered = ah.to_string();
|
||||||
assert_eq!(rendered, fixed_header);
|
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.addr, "a@b.example.org");
|
||||||
assert_eq!(ah.prefer_encrypt, EncryptPreference::Mutual);
|
assert_eq!(ah.prefer_encrypt, EncryptPreference::Mutual);
|
||||||
|
|
||||||
Aheader::from_str(&format!(
|
Aheader::from_str(&format!(
|
||||||
"addr=a@b.example.org; prefer-encrypt=ignoreUnknownValues; keydata={}",
|
"addr=a@b.example.org; prefer-encrypt=ignoreUnknownValues; keydata={}",
|
||||||
RAWKEY
|
RAWKEY
|
||||||
))
|
))?;
|
||||||
.expect("failed to parse");
|
|
||||||
|
|
||||||
Aheader::from_str(&format!("addr=a@b.example.org; keydata={}", RAWKEY))
|
Aheader::from_str(&format!("addr=a@b.example.org; keydata={}", RAWKEY))?;
|
||||||
.expect("failed to parse");
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
237
src/chat.rs
237
src/chat.rs
@@ -179,7 +179,7 @@ impl ChatId {
|
|||||||
chat.id
|
chat.id
|
||||||
}
|
}
|
||||||
None => {
|
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
|
|| contact_id == DC_CONTACT_ID_SELF
|
||||||
{
|
{
|
||||||
let chat_id = ChatId::get_for_contact(context, contact_id).await?;
|
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?;
|
let contact_ids = get_chat_contacts(context, self).await?;
|
||||||
for contact_id in contact_ids.into_iter() {
|
for contact_id in contact_ids.into_iter() {
|
||||||
let contact = Contact::get_by_id(context, contact_id).await?;
|
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());
|
bail!("{} is not verified.", contact.get_display_name());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -499,7 +499,7 @@ impl ChatId {
|
|||||||
|
|
||||||
job::kill_action(context, Action::Housekeeping).await?;
|
job::kill_action(context, Action::Housekeeping).await?;
|
||||||
let j = job::Job::new(Action::Housekeeping, 0, Params::new(), 10);
|
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() {
|
if chat.is_self_talk() {
|
||||||
let mut msg = Message::new(Viewtype::Text);
|
let mut msg = Message::new(Viewtype::Text);
|
||||||
@@ -605,7 +605,7 @@ impl ChatId {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let chat = Chat::load_from_db(context, self).await?;
|
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");
|
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.
|
/// Returns true if user can send messages to this chat.
|
||||||
pub async fn can_send(&self, context: &Context) -> bool {
|
pub async fn can_send(&self, context: &Context) -> Result<bool> {
|
||||||
!self.id.is_special()
|
Ok(!self.id.is_special()
|
||||||
&& !self.is_device_talk()
|
&& !self.is_device_talk()
|
||||||
&& !self.is_mailing_list()
|
&& !self.is_mailing_list()
|
||||||
&& !self.is_contact_request()
|
&& !self.is_contact_request()
|
||||||
&& (self.typ == Chattype::Single
|
&& (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<()> {
|
pub async fn update_param(&mut self, context: &Context) -> Result<()> {
|
||||||
@@ -1097,7 +1097,7 @@ impl Chat {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if self.typ == Chattype::Group
|
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(
|
context.emit_event(EventType::ErrorSelfNotInGroup(
|
||||||
"Cannot send message; self not in group.".into(),
|
"Cannot send message; self not in group.".into(),
|
||||||
@@ -1667,7 +1667,7 @@ async fn prepare_msg_common(
|
|||||||
chat_id.unarchive(context).await?;
|
chat_id.unarchive(context).await?;
|
||||||
|
|
||||||
let mut chat = Chat::load_from_db(context, chat_id).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
|
// The OutPreparing state is set by dc_prepare_msg() before it
|
||||||
// calls this function and the message is left in the OutPreparing
|
// 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.
|
/// 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<bool> {
|
||||||
// this function works for group and for normal chats, however, it is more useful
|
// this function works for group and for normal chats, however, it is more useful
|
||||||
// for group chats.
|
// for group chats.
|
||||||
// DC_CONTACT_ID_SELF may be used to check, if the user itself is in a group
|
// 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)
|
// chat (DC_CONTACT_ID_SELF is not added to normal chats)
|
||||||
|
|
||||||
context
|
let exists = context
|
||||||
.sql
|
.sql
|
||||||
.exists(
|
.exists(
|
||||||
"SELECT COUNT(*) FROM chats_contacts WHERE chat_id=? AND contact_id=?;",
|
"SELECT COUNT(*) FROM chats_contacts WHERE chat_id=? AND contact_id=?;",
|
||||||
paramsv![chat_id, contact_id as i32],
|
paramsv![chat_id, contact_id as i32],
|
||||||
)
|
)
|
||||||
.await
|
.await?;
|
||||||
.unwrap_or_default()
|
Ok(exists)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Send a message defined by a dc_msg_t object to a chat.
|
/// 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<MsgId> {
|
async fn send_msg_inner(context: &Context, chat_id: ChatId, msg: &mut Message) -> Result<MsgId> {
|
||||||
if let Some(send_job) = prepare_send_msg(context, chat_id, msg).await? {
|
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 {
|
context.emit_event(EventType::MsgsChanged {
|
||||||
chat_id: msg.chat_id,
|
chat_id: msg.chat_id,
|
||||||
@@ -2161,7 +2165,8 @@ pub async fn create_group_chat(
|
|||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let chat_id = ChatId::new(u32::try_from(row_id)?);
|
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);
|
let mut draft_msg = Message::new(Viewtype::Text);
|
||||||
draft_msg.set_text(Some(draft_txt));
|
draft_msg.set_text(Some(draft_txt));
|
||||||
chat_id.set_draft_raw(context, &mut draft_msg).await?;
|
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,
|
context: &Context,
|
||||||
chat_id: ChatId,
|
chat_id: ChatId,
|
||||||
contact_id: u32,
|
contact_id: u32,
|
||||||
) -> bool {
|
) -> Result<()> {
|
||||||
match context
|
context
|
||||||
.sql
|
.sql
|
||||||
.execute(
|
.execute(
|
||||||
"INSERT INTO chats_contacts (chat_id, contact_id) VALUES(?, ?)",
|
"INSERT INTO chats_contacts (chat_id, contact_id) VALUES(?, ?)",
|
||||||
paramsv![chat_id, contact_id as i32],
|
paramsv![chat_id, contact_id as i32],
|
||||||
)
|
)
|
||||||
.await
|
.await?;
|
||||||
{
|
Ok(())
|
||||||
Ok(_) => true,
|
|
||||||
Err(err) => {
|
|
||||||
error!(
|
|
||||||
context,
|
|
||||||
"could not add {} to chat {} table: {}", contact_id, chat_id, err
|
|
||||||
);
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// remove a contact from the chats_contact table
|
/// remove a contact from the chats_contact table
|
||||||
@@ -2212,36 +2207,25 @@ pub(crate) async fn remove_from_chat_contacts_table(
|
|||||||
context: &Context,
|
context: &Context,
|
||||||
chat_id: ChatId,
|
chat_id: ChatId,
|
||||||
contact_id: u32,
|
contact_id: u32,
|
||||||
) -> bool {
|
) -> Result<()> {
|
||||||
match context
|
context
|
||||||
.sql
|
.sql
|
||||||
.execute(
|
.execute(
|
||||||
"DELETE FROM chats_contacts WHERE chat_id=? AND contact_id=?",
|
"DELETE FROM chats_contacts WHERE chat_id=? AND contact_id=?",
|
||||||
paramsv![chat_id, contact_id as i32],
|
paramsv![chat_id, contact_id as i32],
|
||||||
)
|
)
|
||||||
.await
|
.await?;
|
||||||
{
|
Ok(())
|
||||||
Ok(_) => true,
|
|
||||||
Err(_) => {
|
|
||||||
warn!(
|
|
||||||
context,
|
|
||||||
"could not remove contact {:?} from chat {:?}", contact_id, chat_id
|
|
||||||
);
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds a contact to the chat.
|
/// Adds a contact to the chat.
|
||||||
pub async fn add_contact_to_chat(context: &Context, chat_id: ChatId, contact_id: u32) -> bool {
|
pub async fn add_contact_to_chat(
|
||||||
match add_contact_to_chat_ex(context, chat_id, contact_id, false).await {
|
context: &Context,
|
||||||
Ok(res) => res,
|
chat_id: ChatId,
|
||||||
Err(err) => {
|
contact_id: u32,
|
||||||
error!(context, "failed to add contact: {}", err);
|
) -> Result<()> {
|
||||||
false
|
add_contact_to_chat_ex(context, chat_id, contact_id, false).await?;
|
||||||
}
|
Ok(())
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn add_contact_to_chat_ex(
|
pub(crate) async fn add_contact_to_chat_ex(
|
||||||
@@ -2264,13 +2248,13 @@ pub(crate) async fn add_contact_to_chat_ex(
|
|||||||
chat_id
|
chat_id
|
||||||
);
|
);
|
||||||
ensure!(
|
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",
|
"invalid contact_id {} for adding to group",
|
||||||
contact_id
|
contact_id
|
||||||
);
|
);
|
||||||
ensure!(!chat.is_mailing_list(), "Mailing lists can't be changed");
|
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! */
|
/* we should respect this - whatever we send to the group, it gets discarded anyway! */
|
||||||
context.emit_event(EventType::ErrorSelfNotInGroup(
|
context.emit_event(EventType::ErrorSelfNotInGroup(
|
||||||
"Cannot add contact to group; self not in group.".into(),
|
"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);
|
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 {
|
if !from_handshake {
|
||||||
return Ok(true);
|
return Ok(true);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// else continue and send status mail
|
// else continue and send status mail
|
||||||
if chat.is_protected()
|
if chat.is_protected()
|
||||||
&& contact.is_verified(context).await != VerifiedStatus::BidirectVerified
|
&& contact.is_verified(context).await? != VerifiedStatus::BidirectVerified
|
||||||
{
|
{
|
||||||
error!(
|
error!(
|
||||||
context,
|
context,
|
||||||
@@ -2310,9 +2294,10 @@ pub(crate) async fn add_contact_to_chat_ex(
|
|||||||
);
|
);
|
||||||
return Ok(false);
|
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);
|
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 {
|
if chat.param.get_int(Param::Unpromoted).unwrap_or_default() == 0 {
|
||||||
msg.viewtype = Viewtype::Text;
|
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. */
|
/* 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 let Ok(chat) = Chat::load_from_db(context, chat_id).await {
|
||||||
if chat.typ == Chattype::Group {
|
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(
|
context.emit_event(EventType::ErrorSelfNotInGroup(
|
||||||
"Cannot remove contact from chat; self not in group.".into(),
|
"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
|
// in order to correctly determine encryption so if we
|
||||||
// removed it first, it would complicate the
|
// removed it first, it would complicate the
|
||||||
// check/encryption logic.
|
// 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));
|
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.typ == Chattype::Group || chat.typ == Chattype::Mailinglist {
|
||||||
if chat.name == new_name {
|
if chat.name == new_name {
|
||||||
success = true;
|
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(
|
context.emit_event(EventType::ErrorSelfNotInGroup(
|
||||||
"Cannot set chat name; self not in group".into(),
|
"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"
|
"Failed to set profile image; group does not exist"
|
||||||
);
|
);
|
||||||
/* we should respect this - whatever we send to the group, it gets discarded anyway! */
|
/* 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(
|
context.emit_event(EventType::ErrorSelfNotInGroup(
|
||||||
"Cannot set chat profile image; self not in group.".into(),
|
"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?;
|
chat_id.unarchive(context).await?;
|
||||||
if let Ok(mut chat) = Chat::load_from_db(context, chat_id).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;
|
curr_timestamp = dc_create_smeared_timestamps(context, msg_ids.len()).await;
|
||||||
let ids = context
|
let ids = context
|
||||||
.sql
|
.sql
|
||||||
@@ -2755,7 +2742,7 @@ pub async fn forward_msgs(context: &Context, msg_ids: &[MsgId], chat_id: ChatId)
|
|||||||
curr_timestamp += 1;
|
curr_timestamp += 1;
|
||||||
new_msg_id = chat.prepare_msg_raw(context, &mut msg, fresh10).await?;
|
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? {
|
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);
|
created_chats.push(chat_id);
|
||||||
@@ -3014,12 +3001,8 @@ pub(crate) async fn add_info_msg(
|
|||||||
chat_id: ChatId,
|
chat_id: ChatId,
|
||||||
text: impl AsRef<str>,
|
text: impl AsRef<str>,
|
||||||
timestamp: i64,
|
timestamp: i64,
|
||||||
) {
|
) -> Result<MsgId> {
|
||||||
if let Err(e) =
|
|
||||||
add_info_msg_with_cmd(context, chat_id, text, SystemMessage::Unknown, timestamp).await
|
add_info_msg_with_cmd(context, chat_id, text, SystemMessage::Unknown, timestamp).await
|
||||||
{
|
|
||||||
warn!(context, "Could not add info msg: {}", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -3141,7 +3124,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[async_std::test]
|
#[async_std::test]
|
||||||
async fn test_self_talk() {
|
async fn test_self_talk() -> Result<()> {
|
||||||
let t = TestContext::new().await;
|
let t = TestContext::new().await;
|
||||||
let chat = &t.get_self_chat().await;
|
let chat = &t.get_self_chat().await;
|
||||||
assert_eq!(DC_CONTACT_ID_SELF, 1);
|
assert_eq!(DC_CONTACT_ID_SELF, 1);
|
||||||
@@ -3149,9 +3132,10 @@ mod tests {
|
|||||||
assert!(chat.is_self_talk());
|
assert!(chat.is_self_talk());
|
||||||
assert!(chat.visibility == ChatVisibility::Normal);
|
assert!(chat.visibility == ChatVisibility::Normal);
|
||||||
assert!(!chat.is_device_talk());
|
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_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]
|
#[async_std::test]
|
||||||
@@ -3190,7 +3174,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[async_std::test]
|
#[async_std::test]
|
||||||
async fn test_add_device_msg_labelled() {
|
async fn test_add_device_msg_labelled() -> Result<()> {
|
||||||
let t = TestContext::new().await;
|
let t = TestContext::new().await;
|
||||||
|
|
||||||
// add two device-messages with the same label (second attempt is not added)
|
// 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());
|
assert!(msg2_id.as_ref().unwrap().is_unset());
|
||||||
|
|
||||||
// check added message
|
// check added message
|
||||||
let msg1 = message::Message::load_from_db(&t, *msg1_id.as_ref().unwrap()).await;
|
let msg1 = message::Message::load_from_db(&t, *msg1_id.as_ref().unwrap()).await?;
|
||||||
assert!(msg1.is_ok());
|
|
||||||
let msg1 = msg1.unwrap();
|
|
||||||
assert_eq!(msg1_id.as_ref().unwrap(), &msg1.id);
|
assert_eq!(msg1_id.as_ref().unwrap(), &msg1.id);
|
||||||
assert_eq!(msg1.text.as_ref().unwrap(), "first message");
|
assert_eq!(msg1.text.as_ref().unwrap(), "first message");
|
||||||
assert_eq!(msg1.from_id, DC_CONTACT_ID_DEVICE);
|
assert_eq!(msg1.from_id, DC_CONTACT_ID_DEVICE);
|
||||||
@@ -3220,28 +3202,25 @@ mod tests {
|
|||||||
// check device chat
|
// check device chat
|
||||||
let chat_id = msg1.chat_id;
|
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());
|
assert!(!chat_id.is_special());
|
||||||
let chat = Chat::load_from_db(&t, chat_id).await;
|
let chat = Chat::load_from_db(&t, chat_id).await?;
|
||||||
assert!(chat.is_ok());
|
|
||||||
let chat = chat.unwrap();
|
|
||||||
assert_eq!(chat.get_type(), Chattype::Single);
|
assert_eq!(chat.get_type(), Chattype::Single);
|
||||||
assert!(chat.is_device_talk());
|
assert!(chat.is_device_talk());
|
||||||
assert!(!chat.is_self_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_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
|
// delete device message, make sure it is not added again
|
||||||
message::delete_msgs(&t, &[*msg1_id.as_ref().unwrap()])
|
message::delete_msgs(&t, &[*msg1_id.as_ref().unwrap()]).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
let msg1 = message::Message::load_from_db(&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());
|
assert!(msg1.is_err() || msg1.unwrap().chat_id.is_trash());
|
||||||
let msg3_id = add_device_msg(&t, Some("any-label"), Some(&mut msg2)).await;
|
let msg3_id = add_device_msg(&t, Some("any-label"), Some(&mut msg2)).await;
|
||||||
assert!(msg3_id.is_ok());
|
assert!(msg3_id.is_ok());
|
||||||
assert!(msg2_id.as_ref().unwrap().is_unset());
|
assert!(msg2_id.as_ref().unwrap().is_unset());
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_std::test]
|
#[async_std::test]
|
||||||
@@ -3566,24 +3545,21 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[async_std::test]
|
#[async_std::test]
|
||||||
async fn test_shall_attach_selfavatar() {
|
async fn test_shall_attach_selfavatar() -> Result<()> {
|
||||||
let t = TestContext::new().await;
|
let t = TestContext::new().await;
|
||||||
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo")
|
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?;
|
||||||
.await
|
assert!(!shall_attach_selfavatar(&t, chat_id).await?);
|
||||||
.unwrap();
|
|
||||||
assert!(!shall_attach_selfavatar(&t, chat_id).await.unwrap());
|
|
||||||
|
|
||||||
let (contact_id, _) =
|
let (contact_id, _) =
|
||||||
Contact::add_or_lookup(&t, "", "foo@bar.org", Origin::IncomingUnknownTo)
|
Contact::add_or_lookup(&t, "", "foo@bar.org", Origin::IncomingUnknownTo).await?;
|
||||||
.await
|
add_contact_to_chat(&t, chat_id, contact_id).await?;
|
||||||
.unwrap();
|
assert!(!shall_attach_selfavatar(&t, chat_id).await?);
|
||||||
add_contact_to_chat(&t, chat_id, contact_id).await;
|
t.set_config(Config::Selfavatar, None).await?; // setting to None also forces re-sending
|
||||||
assert!(!shall_attach_selfavatar(&t, chat_id).await.unwrap());
|
assert!(shall_attach_selfavatar(&t, chat_id).await?);
|
||||||
t.set_config(Config::Selfavatar, None).await.unwrap(); // setting to None also forces re-sending
|
|
||||||
assert!(shall_attach_selfavatar(&t, chat_id).await.unwrap());
|
|
||||||
|
|
||||||
assert!(chat_id.set_selfavatar_timestamp(&t, time()).await.is_ok());
|
chat_id.set_selfavatar_timestamp(&t, time()).await?;
|
||||||
assert!(!shall_attach_selfavatar(&t, chat_id).await.unwrap());
|
assert!(!shall_attach_selfavatar(&t, chat_id).await?);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_std::test]
|
#[async_std::test]
|
||||||
@@ -3638,12 +3614,10 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[async_std::test]
|
#[async_std::test]
|
||||||
async fn test_add_info_msg() {
|
async fn test_add_info_msg() -> Result<()> {
|
||||||
let t = TestContext::new().await;
|
let t = TestContext::new().await;
|
||||||
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo")
|
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?;
|
||||||
.await
|
add_info_msg(&t, chat_id, "foo info", 200000).await?;
|
||||||
.unwrap();
|
|
||||||
add_info_msg(&t, chat_id, "foo info", 200000).await;
|
|
||||||
|
|
||||||
let msg = t.get_last_msg_in(chat_id).await;
|
let msg = t.get_last_msg_in(chat_id).await;
|
||||||
assert_eq!(msg.get_chat_id(), chat_id);
|
assert_eq!(msg.get_chat_id(), chat_id);
|
||||||
@@ -3651,6 +3625,7 @@ mod tests {
|
|||||||
assert_eq!(msg.get_text().unwrap(), "foo info");
|
assert_eq!(msg.get_text().unwrap(), "foo info");
|
||||||
assert!(msg.is_info());
|
assert!(msg.is_info());
|
||||||
assert_eq!(msg.get_info_type(), SystemMessage::Unknown);
|
assert_eq!(msg.get_info_type(), SystemMessage::Unknown);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_std::test]
|
#[async_std::test]
|
||||||
@@ -3807,42 +3782,24 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[async_std::test]
|
#[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
|
// Alice creates a group with Bob, sends a message to bob
|
||||||
let alice = TestContext::new_alice().await;
|
let alice = TestContext::new_alice().await;
|
||||||
let bob = TestContext::new_bob().await;
|
let bob = TestContext::new_bob().await;
|
||||||
|
|
||||||
alice
|
alice.set_config(Config::ShowEmails, Some("2")).await?;
|
||||||
.set_config(Config::ShowEmails, Some("2"))
|
bob.set_config(Config::ShowEmails, Some("2")).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
bob.set_config(Config::ShowEmails, Some("2")).await.unwrap();
|
|
||||||
|
|
||||||
let (contact_id, _) =
|
let (contact_id, _) =
|
||||||
Contact::add_or_lookup(&alice, "", "bob@example.net", Origin::ManuallyCreated)
|
Contact::add_or_lookup(&alice, "", "bob@example.net", Origin::ManuallyCreated).await?;
|
||||||
.await
|
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?;
|
||||||
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();
|
|
||||||
|
|
||||||
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!(
|
assert_eq!(
|
||||||
get_chat_contacts(&alice, alice_chat_id)
|
get_chat_msgs(&alice, alice_chat_id, 0, None).await?.len(),
|
||||||
.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(),
|
|
||||||
1
|
1
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -3858,17 +3815,15 @@ mod tests {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
let msg = bob.get_last_msg().await;
|
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);
|
assert_eq!(bob_chat.grpid, alice_chat.grpid);
|
||||||
|
|
||||||
// Bob accepts contact request.
|
// 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;
|
// Bob answers - simulate a normal MUA by not setting `Chat-*`-headers;
|
||||||
// moreover, Bob's SMTP-server also replaces the `Message-ID:`-header
|
// moreover, Bob's SMTP-server also replaces the `Message-ID:`-header
|
||||||
send_text_msg(&bob, bob_chat.id, "ho!".to_string())
|
send_text_msg(&bob, bob_chat.id, "ho!".to_string()).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
let msg = bob.pop_sent_msg().await.payload();
|
let msg = bob.pop_sent_msg().await.payload();
|
||||||
let msg = msg.replace("Message-ID: <Gr.", "Message-ID: <XXX");
|
let msg = msg.replace("Message-ID: <Gr.", "Message-ID: <XXX");
|
||||||
let msg = msg.replace("Chat-", "XXXX-");
|
let msg = msg.replace("Chat-", "XXXX-");
|
||||||
@@ -3882,12 +3837,10 @@ mod tests {
|
|||||||
assert_eq!(msg.chat_id, alice_chat_id);
|
assert_eq!(msg.chat_id, alice_chat_id);
|
||||||
assert_eq!(msg.text, Some("ho!".to_string()));
|
assert_eq!(msg.text, Some("ho!".to_string()));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
get_chat_msgs(&alice, alice_chat_id, 0, None)
|
get_chat_msgs(&alice, alice_chat_id, 0, None).await?.len(),
|
||||||
.await
|
|
||||||
.unwrap()
|
|
||||||
.len(),
|
|
||||||
2
|
2
|
||||||
);
|
);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_std::test]
|
#[async_std::test]
|
||||||
@@ -4202,13 +4155,13 @@ mod tests {
|
|||||||
let bob = Contact::create(&alice, "", "bob@f.br").await?;
|
let bob = Contact::create(&alice, "", "bob@f.br").await?;
|
||||||
let chat_id = ChatId::create_for_contact(&alice, bob).await?;
|
let chat_id = ChatId::create_for_contact(&alice, bob).await?;
|
||||||
let chat = Chat::load_from_db(&alice, chat_id).await?;
|
let chat = Chat::load_from_db(&alice, chat_id).await?;
|
||||||
assert!(chat.can_send(&alice).await);
|
assert!(chat.can_send(&alice).await?);
|
||||||
let chat_id = create_group_chat(&alice, ProtectionStatus::Unprotected, "foo").await?;
|
let chat_id = create_group_chat(&alice, ProtectionStatus::Unprotected, "foo").await?;
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Chat::load_from_db(&alice, chat_id)
|
Chat::load_from_db(&alice, chat_id)
|
||||||
.await?
|
.await?
|
||||||
.can_send(&alice)
|
.can_send(&alice)
|
||||||
.await,
|
.await?,
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
remove_contact_from_chat(&alice, chat_id, DC_CONTACT_ID_SELF).await?;
|
remove_contact_from_chat(&alice, chat_id, DC_CONTACT_ID_SELF).await?;
|
||||||
@@ -4216,7 +4169,7 @@ mod tests {
|
|||||||
Chat::load_from_db(&alice, chat_id)
|
Chat::load_from_db(&alice, chat_id)
|
||||||
.await?
|
.await?
|
||||||
.can_send(&alice)
|
.can_send(&alice)
|
||||||
.await,
|
.await?,
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -444,7 +444,7 @@ async fn configure(ctx: &Context, param: &mut LoginParam) -> Result<()> {
|
|||||||
ctx,
|
ctx,
|
||||||
job::Job::new(Action::FetchExistingMsgs, 0, Params::new(), 0),
|
job::Job::new(Action::FetchExistingMsgs, 0, Params::new(), 0),
|
||||||
)
|
)
|
||||||
.await;
|
.await?;
|
||||||
|
|
||||||
progress!(ctx, 940);
|
progress!(ctx, 940);
|
||||||
update_device_chats_handle.await?;
|
update_device_chats_handle.await?;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
use std::convert::{TryFrom, TryInto};
|
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 async_std::path::PathBuf;
|
||||||
use deltachat_derive::{FromSql, ToSql};
|
use deltachat_derive::{FromSql, ToSql};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
@@ -174,6 +174,12 @@ pub enum VerifiedStatus {
|
|||||||
BidirectVerified = 2,
|
BidirectVerified = 2,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for VerifiedStatus {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::Unverified
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Contact {
|
impl Contact {
|
||||||
pub async fn load_from_db(context: &Context, contact_id: u32) -> Result<Self> {
|
pub async fn load_from_db(context: &Context, contact_id: u32) -> Result<Self> {
|
||||||
let mut contact = context
|
let mut contact = context
|
||||||
@@ -229,11 +235,9 @@ impl Contact {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Check if a contact is blocked.
|
/// Check if a contact is blocked.
|
||||||
pub async fn is_blocked_load(context: &Context, id: u32) -> bool {
|
pub async fn is_blocked_load(context: &Context, id: u32) -> Result<bool> {
|
||||||
Self::load_from_db(context, id)
|
let blocked = Self::load_from_db(context, id).await?.blocked;
|
||||||
.await
|
Ok(blocked)
|
||||||
.map(|contact| contact.blocked)
|
|
||||||
.unwrap_or_default()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Block the given contact.
|
/// Block the given contact.
|
||||||
@@ -263,7 +267,7 @@ impl Contact {
|
|||||||
|
|
||||||
let (contact_id, sth_modified) =
|
let (contact_id, sth_modified) =
|
||||||
Contact::add_or_lookup(context, &name, &addr, Origin::ManuallyCreated).await?;
|
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 {
|
match sth_modified {
|
||||||
Modifier::None => {}
|
Modifier::None => {}
|
||||||
Modifier::Modified | Modifier::Created => {
|
Modifier::Modified | Modifier::Created => {
|
||||||
@@ -754,12 +758,9 @@ impl Contact {
|
|||||||
|
|
||||||
/// Get blocked contacts.
|
/// Get blocked contacts.
|
||||||
pub async fn get_all_blocked(context: &Context) -> Result<Vec<u32>> {
|
pub async fn get_all_blocked(context: &Context) -> Result<Vec<u32>> {
|
||||||
if let Err(e) = Contact::update_blocked_mailinglist_contacts(context).await {
|
Contact::update_blocked_mailinglist_contacts(context)
|
||||||
warn!(
|
.await
|
||||||
context,
|
.context("cannot update blocked mailinglist contacts")?;
|
||||||
"Cannot update blocked mailinglist contacts: {:?}", e
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let list = context
|
let list = context
|
||||||
.sql
|
.sql
|
||||||
@@ -1018,7 +1019,7 @@ impl Contact {
|
|||||||
///
|
///
|
||||||
/// The UI may draw a checkbox or something like that beside verified contacts.
|
/// 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<VerifiedStatus> {
|
||||||
self.is_verified_ex(context, None).await
|
self.is_verified_ex(context, None).await
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1029,54 +1030,46 @@ impl Contact {
|
|||||||
&self,
|
&self,
|
||||||
context: &Context,
|
context: &Context,
|
||||||
peerstate: Option<&Peerstate>,
|
peerstate: Option<&Peerstate>,
|
||||||
) -> VerifiedStatus {
|
) -> Result<VerifiedStatus> {
|
||||||
// We're always sort of secured-verified as we could verify the key on this device any time with the key
|
// We're always sort of secured-verified as we could verify the key on this device any time with the key
|
||||||
// on this device
|
// on this device
|
||||||
if self.id == DC_CONTACT_ID_SELF {
|
if self.id == DC_CONTACT_ID_SELF {
|
||||||
return VerifiedStatus::BidirectVerified;
|
return Ok(VerifiedStatus::BidirectVerified);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(peerstate) = peerstate {
|
if let Some(peerstate) = peerstate {
|
||||||
if peerstate.verified_key.is_some() {
|
if peerstate.verified_key.is_some() {
|
||||||
return VerifiedStatus::BidirectVerified;
|
return Ok(VerifiedStatus::BidirectVerified);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let peerstate = match Peerstate::from_addr(context, &self.addr).await {
|
if let Some(peerstate) = Peerstate::from_addr(context, &self.addr).await? {
|
||||||
Ok(peerstate) => peerstate,
|
if peerstate.verified_key.is_some() {
|
||||||
Err(err) => {
|
return Ok(VerifiedStatus::BidirectVerified);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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<bool> {
|
||||||
if addr.is_empty() {
|
if addr.is_empty() {
|
||||||
return false;
|
return Ok(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(contact) = Contact::load_from_db(context, contact_id).await {
|
let contact = Contact::load_from_db(context, contact_id).await?;
|
||||||
if !contact.addr.is_empty() {
|
if !contact.addr.is_empty() {
|
||||||
let normalized_addr = addr_normalize(addr);
|
let normalized_addr = addr_normalize(addr);
|
||||||
if contact.addr == normalized_addr {
|
if contact.addr == normalized_addr {
|
||||||
return true;
|
return Ok(true);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
false
|
Ok(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_real_cnt(context: &Context) -> Result<usize> {
|
pub async fn get_real_cnt(context: &Context) -> Result<usize> {
|
||||||
@@ -1094,19 +1087,19 @@ impl Contact {
|
|||||||
Ok(count)
|
Ok(count)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn real_exists_by_id(context: &Context, contact_id: u32) -> bool {
|
pub async fn real_exists_by_id(context: &Context, contact_id: u32) -> Result<bool> {
|
||||||
if !context.sql.is_open().await || contact_id <= DC_CONTACT_ID_LAST_SPECIAL {
|
if contact_id <= DC_CONTACT_ID_LAST_SPECIAL {
|
||||||
return false;
|
return Ok(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
context
|
let exists = context
|
||||||
.sql
|
.sql
|
||||||
.exists(
|
.exists(
|
||||||
"SELECT COUNT(*) FROM contacts WHERE id=?;",
|
"SELECT COUNT(*) FROM contacts WHERE id=?;",
|
||||||
paramsv![contact_id as i32],
|
paramsv![contact_id as i32],
|
||||||
)
|
)
|
||||||
.await
|
.await?;
|
||||||
.unwrap_or_default()
|
Ok(exists)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn scaleup_origin_by_id(
|
pub async fn scaleup_origin_by_id(
|
||||||
|
|||||||
@@ -298,7 +298,7 @@ pub(crate) async fn dc_receive_imf_inner(
|
|||||||
0,
|
0,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.await;
|
.await?;
|
||||||
}
|
}
|
||||||
} else if insert_msg_id
|
} else if insert_msg_id
|
||||||
.needs_move(context, server_folder.as_ref())
|
.needs_move(context, server_folder.as_ref())
|
||||||
@@ -311,7 +311,7 @@ pub(crate) async fn dc_receive_imf_inner(
|
|||||||
context,
|
context,
|
||||||
job::Job::new(Action::MoveMsg, insert_msg_id.to_u32(), Params::new(), 0),
|
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() {
|
} else if !mime_parser.mdn_reports.is_empty() && mime_parser.has_chat_version() {
|
||||||
// This is a Delta Chat MDN. Mark as read.
|
// This is a Delta Chat MDN. Mark as read.
|
||||||
job::add(
|
job::add(
|
||||||
@@ -323,7 +323,7 @@ pub(crate) async fn dc_receive_imf_inner(
|
|||||||
0,
|
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
|
// 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.
|
// but the From-address is not a member of this chat.
|
||||||
if let Some(chat_id) = chat_id {
|
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?;
|
let chat = Chat::load_from_db(context, chat_id).await?;
|
||||||
if chat.is_protected() {
|
if chat.is_protected() {
|
||||||
let s = stock_str::unknown_sender_for_chat(context).await;
|
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 {
|
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
|
Blocked::Not
|
||||||
} else {
|
} else {
|
||||||
Blocked::Request
|
Blocked::Request
|
||||||
@@ -915,7 +915,7 @@ async fn add_parts(
|
|||||||
stock_ephemeral_timer_changed(context, ephemeral_timer, from_id).await,
|
stock_ephemeral_timer_changed(context, ephemeral_timer, from_id).await,
|
||||||
sort_timestamp,
|
sort_timestamp,
|
||||||
)
|
)
|
||||||
.await;
|
.await?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -974,7 +974,7 @@ async fn add_parts(
|
|||||||
format!("Cannot set protection: {}", e),
|
format!("Cannot set protection: {}", e),
|
||||||
sort_timestamp,
|
sort_timestamp,
|
||||||
)
|
)
|
||||||
.await;
|
.await?;
|
||||||
return Ok(chat_id); // do not return an error as this would result in retrying the message
|
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)
|
.update_timestamp(context, Param::MemberListTimestamp, sent_timestamp)
|
||||||
.await?
|
.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
|
// Members could have been removed while we were
|
||||||
// absent. We can't use existing member list and need to
|
// absent. We can't use existing member list and need to
|
||||||
// start from scratch.
|
// start from scratch.
|
||||||
@@ -1693,20 +1693,20 @@ async fn create_or_lookup_group(
|
|||||||
.await
|
.await
|
||||||
.ok();
|
.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
|
if from_id > DC_CONTACT_ID_LAST_SPECIAL
|
||||||
&& !Contact::addr_equals_contact(context, &self_addr, 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::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() {
|
for &to_id in to_ids.iter() {
|
||||||
info!(context, "adding to={:?} to chat id={}", to_id, chat_id);
|
info!(context, "adding to={:?} to chat id={}", to_id, chat_id);
|
||||||
if !Contact::addr_equals_contact(context, &self_addr, 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::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;
|
send_EVENT_CHAT_MODIFIED = true;
|
||||||
@@ -1716,7 +1716,7 @@ async fn create_or_lookup_group(
|
|||||||
.update_timestamp(context, Param::MemberListTimestamp, sent_timestamp)
|
.update_timestamp(context, Param::MemberListTimestamp, sent_timestamp)
|
||||||
.await?
|
.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;
|
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)))
|
Ok(Some((chat_id, Blocked::Request)))
|
||||||
} else {
|
} else {
|
||||||
info!(context, "creating list forbidden by caller");
|
info!(context, "creating list forbidden by caller");
|
||||||
@@ -1926,7 +1926,7 @@ async fn create_adhoc_group(
|
|||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
for &member_id in member_ids.iter() {
|
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));
|
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?;
|
let peerstate = Peerstate::from_addr(context, contact.get_addr()).await?;
|
||||||
|
|
||||||
if peerstate.is_none()
|
if peerstate.is_none()
|
||||||
|| contact.is_verified_ex(context, peerstate.as_ref()).await
|
|| contact.is_verified_ex(context, peerstate.as_ref()).await?
|
||||||
!= VerifiedStatus::BidirectVerified
|
!= VerifiedStatus::BidirectVerified
|
||||||
{
|
{
|
||||||
bail!(
|
bail!(
|
||||||
@@ -2511,24 +2511,22 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[async_std::test]
|
#[async_std::test]
|
||||||
async fn test_read_receipt_and_unarchive() {
|
async fn test_read_receipt_and_unarchive() -> Result<()> {
|
||||||
// create alice's account
|
// create alice's account
|
||||||
let t = TestContext::new_alice().await;
|
let t = TestContext::new_alice().await;
|
||||||
|
|
||||||
let bob_id = Contact::create(&t, "bob", "bob@example.com").await.unwrap();
|
let bob_id = Contact::create(&t, "bob", "bob@example.com").await?;
|
||||||
let one2one_id = ChatId::create_for_contact(&t, bob_id).await.unwrap();
|
let one2one_id = ChatId::create_for_contact(&t, bob_id).await?;
|
||||||
one2one_id
|
one2one_id
|
||||||
.set_visibility(&t, ChatVisibility::Archived)
|
.set_visibility(&t, ChatVisibility::Archived)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.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);
|
assert!(one2one.get_visibility() == ChatVisibility::Archived);
|
||||||
|
|
||||||
// create a group with bob, archive group
|
// create a group with bob, archive group
|
||||||
let group_id = chat::create_group_chat(&t, ProtectionStatus::Unprotected, "foo")
|
let group_id = chat::create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?;
|
||||||
.await
|
chat::add_contact_to_chat(&t, group_id, bob_id).await?;
|
||||||
.unwrap();
|
|
||||||
chat::add_contact_to_chat(&t, group_id, bob_id).await;
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
chat::get_chat_msgs(&t, group_id, 0, None)
|
chat::get_chat_msgs(&t, group_id, 0, None)
|
||||||
.await
|
.await
|
||||||
@@ -2538,16 +2536,14 @@ mod tests {
|
|||||||
);
|
);
|
||||||
group_id
|
group_id
|
||||||
.set_visibility(&t, ChatVisibility::Archived)
|
.set_visibility(&t, ChatVisibility::Archived)
|
||||||
.await
|
.await?;
|
||||||
.unwrap();
|
let group = Chat::load_from_db(&t, group_id).await?;
|
||||||
let group = Chat::load_from_db(&t, group_id).await.unwrap();
|
|
||||||
assert!(group.get_visibility() == ChatVisibility::Archived);
|
assert!(group.get_visibility() == ChatVisibility::Archived);
|
||||||
|
|
||||||
// everything archived, chatlist should be empty
|
// everything archived, chatlist should be empty
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Chatlist::try_load(&t, DC_GCL_NO_SPECIALS, None, None)
|
Chatlist::try_load(&t, DC_GCL_NO_SPECIALS, None, None)
|
||||||
.await
|
.await?
|
||||||
.unwrap()
|
|
||||||
.len(),
|
.len(),
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
@@ -2575,13 +2571,12 @@ mod tests {
|
|||||||
1,
|
1,
|
||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
.await
|
.await?;
|
||||||
.unwrap();
|
|
||||||
let msg = get_chat_msg(&t, group_id, 0, 1).await;
|
let msg = get_chat_msg(&t, group_id, 0, 1).await;
|
||||||
assert_eq!(msg.is_dc_message, MessengerMessage::Yes);
|
assert_eq!(msg.is_dc_message, MessengerMessage::Yes);
|
||||||
assert_eq!(msg.text.unwrap(), "hello");
|
assert_eq!(msg.text.unwrap(), "hello");
|
||||||
assert_eq!(msg.state, MessageState::OutDelivered);
|
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);
|
assert!(group.get_visibility() == ChatVisibility::Normal);
|
||||||
|
|
||||||
// bob sends a read receipt to the group
|
// bob sends a read receipt to the group
|
||||||
@@ -2622,27 +2617,21 @@ mod tests {
|
|||||||
1,
|
1,
|
||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
.await.unwrap();
|
.await?;
|
||||||
assert_eq!(
|
assert_eq!(chat::get_chat_msgs(&t, group_id, 0, None).await?.len(), 1);
|
||||||
chat::get_chat_msgs(&t, group_id, 0, None)
|
let msg = message::Message::load_from_db(&t, msg.id).await?;
|
||||||
.await
|
|
||||||
.unwrap()
|
|
||||||
.len(),
|
|
||||||
1
|
|
||||||
);
|
|
||||||
let msg = message::Message::load_from_db(&t, msg.id).await.unwrap();
|
|
||||||
assert_eq!(msg.state, MessageState::OutMdnRcvd);
|
assert_eq!(msg.state, MessageState::OutMdnRcvd);
|
||||||
|
|
||||||
// check, the read-receipt has not unarchived the one2one
|
// check, the read-receipt has not unarchived the one2one
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Chatlist::try_load(&t, DC_GCL_NO_SPECIALS, None, None)
|
Chatlist::try_load(&t, DC_GCL_NO_SPECIALS, None, None)
|
||||||
.await
|
.await?
|
||||||
.unwrap()
|
|
||||||
.len(),
|
.len(),
|
||||||
1
|
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);
|
assert!(one2one.get_visibility() == ChatVisibility::Archived);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_std::test]
|
#[async_std::test]
|
||||||
@@ -2937,7 +2926,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[async_std::test]
|
#[async_std::test]
|
||||||
async fn test_parse_ndn_group_msg() {
|
async fn test_parse_ndn_group_msg() -> Result<()> {
|
||||||
let t = TestContext::new().await;
|
let t = TestContext::new().await;
|
||||||
t.configure_addr("alice@gmail.com").await;
|
t.configure_addr("alice@gmail.com").await;
|
||||||
|
|
||||||
@@ -2959,32 +2948,32 @@ mod tests {
|
|||||||
1,
|
1,
|
||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
.await
|
.await?;
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let chats = Chatlist::try_load(&t, 0, None, None).await.unwrap();
|
let chats = Chatlist::try_load(&t, 0, None, None).await?;
|
||||||
let msg_id = chats.get_msg_id(0).unwrap().unwrap();
|
let msg_id = chats.get_msg_id(0)?.unwrap();
|
||||||
|
|
||||||
let raw = include_bytes!("../test-data/message/gmail_ndn_group.eml");
|
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);
|
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() {
|
let msg_id = if let ChatItem::Message { msg_id } = msgs.last().unwrap() {
|
||||||
msg_id
|
msg_id
|
||||||
} else {
|
} else {
|
||||||
panic!("Wrong item type");
|
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!(
|
assert_eq!(
|
||||||
last_msg.text,
|
last_msg.text,
|
||||||
Some(stock_str::failed_sending_to(&t, "assidhfaaspocwaeofi@gmail.com").await,)
|
Some(stock_str::failed_sending_to(&t, "assidhfaaspocwaeofi@gmail.com").await,)
|
||||||
);
|
);
|
||||||
assert_eq!(last_msg.from_id, DC_CONTACT_ID_INFO);
|
assert_eq!(last_msg.from_id, DC_CONTACT_ID_INFO);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn load_imf_email(context: &Context, imf_raw: &[u8]) -> Message {
|
async fn load_imf_email(context: &Context, imf_raw: &[u8]) -> Message {
|
||||||
@@ -3032,57 +3021,43 @@ mod tests {
|
|||||||
hello back\n";
|
hello back\n";
|
||||||
|
|
||||||
#[async_std::test]
|
#[async_std::test]
|
||||||
async fn test_github_mailing_list() {
|
async fn test_github_mailing_list() -> Result<()> {
|
||||||
let t = TestContext::new_alice().await;
|
let t = TestContext::new_alice().await;
|
||||||
t.ctx
|
t.ctx.set_config(Config::ShowEmails, Some("2")).await?;
|
||||||
.set_config(Config::ShowEmails, Some("2"))
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
dc_receive_imf(&t.ctx, GH_MAILINGLIST, "INBOX", 1, false)
|
dc_receive_imf(&t.ctx, GH_MAILINGLIST, "INBOX", 1, false).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
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);
|
assert_eq!(chats.len(), 1);
|
||||||
|
|
||||||
let chat_id = chats.get_chat_id(0);
|
let chat_id = chats.get_chat_id(0);
|
||||||
chat_id.accept(&t).await.unwrap();
|
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!(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.name, "deltachat/deltachat-core-rust");
|
||||||
assert_eq!(
|
assert_eq!(chat::get_chat_contacts(&t.ctx, chat_id).await?.len(), 1);
|
||||||
chat::get_chat_contacts(&t.ctx, chat_id)
|
|
||||||
.await
|
|
||||||
.unwrap()
|
|
||||||
.len(),
|
|
||||||
1
|
|
||||||
);
|
|
||||||
|
|
||||||
dc_receive_imf(&t.ctx, GH_MAILINGLIST2, "INBOX", 1, false)
|
dc_receive_imf(&t.ctx, GH_MAILINGLIST2, "INBOX", 1, false).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
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);
|
assert_eq!(chats.len(), 1);
|
||||||
let contacts = Contact::get_all(&t.ctx, 0, None as Option<String>)
|
let contacts = Contact::get_all(&t.ctx, 0, None as Option<String>).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(contacts.len(), 0); // mailing list recipients and senders do not count as "known contacts"
|
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 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_addr(), "notifications@github.com");
|
||||||
assert_eq!(contact1.get_display_name(), "notifications@github.com"); // Make sure this is not "Max Mustermann" or somethinng
|
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 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!(contact2.get_addr(), "notifications@github.com");
|
||||||
|
|
||||||
assert_eq!(msg1.get_override_sender_name().unwrap(), "Max Mustermann");
|
assert_eq!(msg1.get_override_sender_name().unwrap(), "Max Mustermann");
|
||||||
assert_eq!(msg2.get_override_sender_name().unwrap(), "Github");
|
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\
|
static DC_MAILINGLIST: &[u8] = b"Received: (Postfix, from userid 1000); Mon, 4 Dec 2006 14:51:39 +0100 (CET)\n\
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ impl MsgId {
|
|||||||
context,
|
context,
|
||||||
Job::new(Action::DownloadMsg, self.to_u32(), Params::new(), 0),
|
Job::new(Action::DownloadMsg, self.to_u32(), Params::new(), 0),
|
||||||
)
|
)
|
||||||
.await;
|
.await?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -161,7 +161,8 @@ pub async fn try_decrypt(
|
|||||||
let mut peerstate = Peerstate::from_addr(context, &from).await?;
|
let mut peerstate = Peerstate::from_addr(context, &from).await?;
|
||||||
|
|
||||||
// Apply Autocrypt header
|
// Apply Autocrypt header
|
||||||
if let Some(ref header) = Aheader::from_headers(context, &from, &mail.headers) {
|
match Aheader::from_headers(&from, &mail.headers) {
|
||||||
|
Ok(Some(ref header)) => {
|
||||||
if let Some(ref mut peerstate) = peerstate {
|
if let Some(ref mut peerstate) = peerstate {
|
||||||
peerstate.apply_header(header, message_time);
|
peerstate.apply_header(header, message_time);
|
||||||
peerstate.save_to_db(&context.sql, false).await?;
|
peerstate.save_to_db(&context.sql, false).await?;
|
||||||
@@ -171,6 +172,9 @@ pub async fn try_decrypt(
|
|||||||
peerstate = Some(p);
|
peerstate = Some(p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Ok(None) => {}
|
||||||
|
Err(err) => warn!(context, "Failed to parse Autocrypt header: {}", err),
|
||||||
|
}
|
||||||
|
|
||||||
// Possibly perform decryption
|
// Possibly perform decryption
|
||||||
let private_keyring: Keyring<SignedSecretKey> = Keyring::new_self(context).await?;
|
let private_keyring: Keyring<SignedSecretKey> = Keyring::new_self(context).await?;
|
||||||
|
|||||||
@@ -1614,13 +1614,13 @@ async fn precheck_imf(
|
|||||||
context,
|
context,
|
||||||
job::Job::new(Action::MoveMsg, msg_id.to_u32(), Params::new(), 0),
|
job::Job::new(Action::MoveMsg, msg_id.to_u32(), Params::new(), 0),
|
||||||
)
|
)
|
||||||
.await;
|
.await?;
|
||||||
} else {
|
} else {
|
||||||
job::add(
|
job::add(
|
||||||
context,
|
context,
|
||||||
job::Job::new(Action::MarkseenMsgOnImap, msg_id.to_u32(), Params::new(), 0),
|
job::Job::new(Action::MarkseenMsgOnImap, msg_id.to_u32(), Params::new(), 0),
|
||||||
)
|
)
|
||||||
.await;
|
.await?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if old_server_folder != server_folder {
|
} else if old_server_folder != server_folder {
|
||||||
@@ -1663,7 +1663,7 @@ async fn precheck_imf(
|
|||||||
context,
|
context,
|
||||||
job::Job::new(Action::MarkseenMsgOnImap, msg_id.to_u32(), Params::new(), 0),
|
job::Job::new(Action::MarkseenMsgOnImap, msg_id.to_u32(), Params::new(), 0),
|
||||||
)
|
)
|
||||||
.await;
|
.await?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
19
src/job.rs
19
src/job.rs
@@ -842,15 +842,15 @@ async fn kill_ids(context: &Context, job_ids: &[u32]) -> Result<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn action_exists(context: &Context, action: Action) -> bool {
|
pub async fn action_exists(context: &Context, action: Action) -> Result<bool> {
|
||||||
context
|
let exists = context
|
||||||
.sql
|
.sql
|
||||||
.exists(
|
.exists(
|
||||||
"SELECT COUNT(*) FROM jobs WHERE action=?;",
|
"SELECT COUNT(*) FROM jobs WHERE action=?;",
|
||||||
paramsv![action],
|
paramsv![action],
|
||||||
)
|
)
|
||||||
.await
|
.await?;
|
||||||
.unwrap_or_default()
|
Ok(exists)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn set_delivered(context: &Context, msg_id: MsgId) -> Result<()> {
|
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();
|
let mut param = Params::new();
|
||||||
param.set(Param::MsgId, msg.id.to_u32().to_string());
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -1196,7 +1196,7 @@ pub(crate) async fn schedule_resync(context: &Context) -> Result<()> {
|
|||||||
context,
|
context,
|
||||||
Job::new(Action::ResyncFolders, 0, Params::new(), 0),
|
Job::new(Action::ResyncFolders, 0, Params::new(), 0),
|
||||||
)
|
)
|
||||||
.await;
|
.await?;
|
||||||
Ok(())
|
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.
|
/// 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 action = job.action;
|
||||||
let delay_seconds = job.delay_seconds();
|
let delay_seconds = job.delay_seconds();
|
||||||
job.save(context).await.unwrap_or_else(|err| {
|
job.save(context).await.context("failed to save job")?;
|
||||||
error!(context, "failed to save job: {}", err);
|
|
||||||
});
|
|
||||||
|
|
||||||
if delay_seconds == 0 {
|
if delay_seconds == 0 {
|
||||||
match action {
|
match action {
|
||||||
@@ -1245,6 +1243,7 @@ pub async fn add(context: &Context, job: Job) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn load_housekeeping_job(context: &Context) -> Result<Option<Job>> {
|
async fn load_housekeeping_job(context: &Context) -> Result<Option<Job>> {
|
||||||
|
|||||||
@@ -223,11 +223,11 @@ pub async fn send_locations_to_chat(
|
|||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
} else if 0 == seconds && is_sending_locations_before {
|
} else if 0 == seconds && is_sending_locations_before {
|
||||||
let stock_str = stock_str::msg_location_disabled(context).await;
|
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));
|
context.emit_event(EventType::ChatModified(chat_id));
|
||||||
if 0 != seconds {
|
if 0 != seconds {
|
||||||
schedule_maybe_send_locations(context, false).await;
|
schedule_maybe_send_locations(context, false).await?;
|
||||||
job::add(
|
job::add(
|
||||||
context,
|
context,
|
||||||
job::Job::new(
|
job::Job::new(
|
||||||
@@ -237,19 +237,20 @@ pub async fn send_locations_to_chat(
|
|||||||
seconds + 1,
|
seconds + 1,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.await;
|
.await?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn schedule_maybe_send_locations(context: &Context, force_schedule: bool) {
|
async fn schedule_maybe_send_locations(context: &Context, force_schedule: bool) -> Result<()> {
|
||||||
if force_schedule || !job::action_exists(context, job::Action::MaybeSendLocations).await {
|
if force_schedule || !job::action_exists(context, job::Action::MaybeSendLocations).await? {
|
||||||
job::add(
|
job::add(
|
||||||
context,
|
context,
|
||||||
job::Job::new(job::Action::MaybeSendLocations, 0, Params::new(), 60),
|
job::Job::new(job::Action::MaybeSendLocations, 0, Params::new(), 60),
|
||||||
)
|
)
|
||||||
.await;
|
.await?;
|
||||||
};
|
};
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns whether `chat_id` or any chat is sending locations.
|
/// 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 {
|
if continue_streaming {
|
||||||
context.emit_event(EventType::LocationChanged(Some(DC_CONTACT_ID_SELF)));
|
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
|
continue_streaming
|
||||||
@@ -697,7 +698,7 @@ pub(crate) async fn job_maybe_send_locations(context: &Context, _job: &Job) -> j
|
|||||||
}
|
}
|
||||||
|
|
||||||
if continue_streaming {
|
if continue_streaming {
|
||||||
schedule_maybe_send_locations(context, true).await;
|
job_try!(schedule_maybe_send_locations(context, true).await);
|
||||||
}
|
}
|
||||||
job::Status::Finished(Ok(()))
|
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;
|
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));
|
context.emit_event(EventType::ChatModified(chat_id));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1295,7 +1295,7 @@ pub async fn delete_msgs(context: &Context, msg_ids: &[MsgId]) -> Result<()> {
|
|||||||
context,
|
context,
|
||||||
job::Job::new(Action::DeleteMsgOnImap, msg_id.to_u32(), Params::new(), 0),
|
job::Job::new(Action::DeleteMsgOnImap, msg_id.to_u32(), Params::new(), 0),
|
||||||
)
|
)
|
||||||
.await;
|
.await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if !msg_ids.is_empty() {
|
if !msg_ids.is_empty() {
|
||||||
@@ -1308,7 +1308,7 @@ pub async fn delete_msgs(context: &Context, msg_ids: &[MsgId]) -> Result<()> {
|
|||||||
context,
|
context,
|
||||||
job::Job::new(Action::Housekeeping, 0, Params::new(), 10),
|
job::Job::new(Action::Housekeeping, 0, Params::new(), 10),
|
||||||
)
|
)
|
||||||
.await;
|
.await?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -1383,7 +1383,7 @@ pub async fn markseen_msgs(context: &Context, msg_ids: Vec<MsgId>) -> Result<()>
|
|||||||
context,
|
context,
|
||||||
job::Job::new(Action::MarkseenMsgOnImap, id.to_u32(), Params::new(), 0),
|
job::Job::new(Action::MarkseenMsgOnImap, id.to_u32(), Params::new(), 0),
|
||||||
)
|
)
|
||||||
.await;
|
.await?;
|
||||||
updated_chat_ids.insert(curr_chat_id, true);
|
updated_chat_ids.insert(curr_chat_id, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1654,7 +1654,7 @@ async fn ndn_maybe_add_info_msg(
|
|||||||
text,
|
text,
|
||||||
dc_create_smeared_timestamp(context).await,
|
dc_create_smeared_timestamp(context).await,
|
||||||
)
|
)
|
||||||
.await;
|
.await?;
|
||||||
context.emit_event(EventType::ChatModified(chat_id));
|
context.emit_event(EventType::ChatModified(chat_id));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1605,27 +1605,30 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[async_std::test]
|
#[async_std::test]
|
||||||
async fn test_subject_in_group() {
|
async fn test_subject_in_group() -> Result<()> {
|
||||||
async fn send_msg_get_subject(
|
async fn send_msg_get_subject(
|
||||||
t: &TestContext,
|
t: &TestContext,
|
||||||
group_id: ChatId,
|
group_id: ChatId,
|
||||||
quote: Option<&Message>,
|
quote: Option<&Message>,
|
||||||
) -> String {
|
) -> Result<String> {
|
||||||
let mut new_msg = Message::new(Viewtype::Text);
|
let mut new_msg = Message::new(Viewtype::Text);
|
||||||
new_msg.set_text(Some("Hi".to_string()));
|
new_msg.set_text(Some("Hi".to_string()));
|
||||||
if let Some(q) = quote {
|
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;
|
let sent = t.send_msg(group_id, &mut new_msg).await;
|
||||||
get_subject(t, sent).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<String> {
|
||||||
let parsed_subject = t.parse_msg(&sent).await.get_subject().unwrap();
|
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);
|
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
|
// 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, ä
|
chat::create_group_chat(&t, chat::ProtectionStatus::Unprotected, "groupname") // TODO encodings, ä
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let bob = Contact::create(&t, "", "bob@example.org").await.unwrap();
|
let bob = Contact::create(&t, "", "bob@example.org").await?;
|
||||||
chat::add_contact_to_chat(&t, group_id, bob).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");
|
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");
|
assert_eq!(subject, "Re: groupname");
|
||||||
|
|
||||||
dc_receive_imf(
|
dc_receive_imf(
|
||||||
@@ -1662,28 +1665,26 @@ mod tests {
|
|||||||
5,
|
5,
|
||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
.await
|
.await?;
|
||||||
.unwrap();
|
|
||||||
let message_from_bob = t.get_last_msg().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");
|
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;
|
let outgoing_quoting_msg = t.get_last_msg().await;
|
||||||
assert_eq!(subject, "Re: Different subject");
|
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");
|
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");
|
assert_eq!(subject, "Re: Different subject");
|
||||||
|
|
||||||
chat::forward_msgs(&t, &[message_from_bob.id], group_id)
|
chat::forward_msgs(&t, &[message_from_bob.id], group_id).await?;
|
||||||
.await
|
let subject = get_subject(&t, t.pop_sent_msg().await).await?;
|
||||||
.unwrap();
|
|
||||||
let subject = get_subject(&t, t.pop_sent_msg().await).await;
|
|
||||||
assert_eq!(subject, "Fwd: Different subject");
|
assert_eq!(subject, "Fwd: Different subject");
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn first_subject_str(t: TestContext) -> String {
|
async fn first_subject_str(t: TestContext) -> String {
|
||||||
|
|||||||
@@ -277,7 +277,7 @@ impl Peerstate {
|
|||||||
|
|
||||||
let msg = stock_str::contact_setup_changed(context, self.addr.clone()).await;
|
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));
|
context.emit_event(EventType::ChatModified(chat_id));
|
||||||
} else {
|
} else {
|
||||||
bail!("contact with peerstate.addr {:?} not found", &self.addr);
|
bail!("contact with peerstate.addr {:?} not found", &self.addr);
|
||||||
|
|||||||
@@ -167,7 +167,8 @@ async fn decode_openpgp(context: &Context, qr: &str) -> Lot {
|
|||||||
format!("{} verified.", peerstate.addr),
|
format!("{} verified.", peerstate.addr),
|
||||||
time(),
|
time(),
|
||||||
)
|
)
|
||||||
.await;
|
.await
|
||||||
|
.ok();
|
||||||
}
|
}
|
||||||
} else if let Some(addr) = addr {
|
} else if let Some(addr) = addr {
|
||||||
lot.state = LotState::QrFprMismatch;
|
lot.state = LotState::QrFprMismatch;
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ impl Context {
|
|||||||
self,
|
self,
|
||||||
job::Job::new(Action::UpdateRecentQuota, 0, Params::new(), 0),
|
job::Job::new(Action::UpdateRecentQuota, 0, Params::new(), 0),
|
||||||
)
|
)
|
||||||
.await;
|
.await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -160,7 +160,7 @@ impl Bob {
|
|||||||
///
|
///
|
||||||
/// With `group` set to `None` this generates a setup-contact QR code, with `group` set to a
|
/// 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.
|
/// [`ChatId`] generates a join-group QR code for the given chat.
|
||||||
pub async fn dc_get_securejoin_qr(context: &Context, group: Option<ChatId>) -> Option<String> {
|
pub async fn dc_get_securejoin_qr(context: &Context, group: Option<ChatId>) -> Result<String> {
|
||||||
/*=======================================================
|
/*=======================================================
|
||||||
==== Alice - the inviter side ====
|
==== Alice - the inviter side ====
|
||||||
==== Step 1 in "Setup verified contact" protocol ====
|
==== Step 1 in "Setup verified contact" protocol ====
|
||||||
@@ -175,28 +175,25 @@ pub async fn dc_get_securejoin_qr(context: &Context, group: Option<ChatId>) -> O
|
|||||||
let self_addr = match context.get_config(Config::ConfiguredAddr).await {
|
let self_addr = match context.get_config(Config::ConfiguredAddr).await {
|
||||||
Ok(Some(addr)) => addr,
|
Ok(Some(addr)) => addr,
|
||||||
Ok(None) => {
|
Ok(None) => {
|
||||||
error!(context, "Not configured, cannot generate QR code.");
|
bail!("Not configured, cannot generate QR code.");
|
||||||
return None;
|
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!(
|
bail!(
|
||||||
context,
|
"Unable to retrieve configuration, cannot generate QR code: {:?}",
|
||||||
"Unable to retrieve configuration, cannot generate QR code: {:?}", err
|
err
|
||||||
);
|
);
|
||||||
return None;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let self_name = context
|
let self_name = context
|
||||||
.get_config(Config::Displayname)
|
.get_config(Config::Displayname)
|
||||||
.await
|
.await?
|
||||||
.ok()?
|
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
let fingerprint: Fingerprint = match get_self_fingerprint(context).await {
|
let fingerprint: Fingerprint = match get_self_fingerprint(context).await {
|
||||||
Some(fp) => fp,
|
Some(fp) => fp,
|
||||||
None => {
|
None => {
|
||||||
return None;
|
bail!("No fingerprint, cannot generate QR code.");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -207,12 +204,11 @@ pub async fn dc_get_securejoin_qr(context: &Context, group: Option<ChatId>) -> O
|
|||||||
|
|
||||||
let qr = if let Some(group) = group {
|
let qr = if let Some(group) = group {
|
||||||
// parameters used: a=g=x=i=s=
|
// parameters used: a=g=x=i=s=
|
||||||
if let Ok(chat) = Chat::load_from_db(context, group).await {
|
let chat = Chat::load_from_db(context, group).await?;
|
||||||
let group_name = chat.get_name();
|
let group_name = chat.get_name();
|
||||||
let group_name_urlencoded =
|
let group_name_urlencoded = utf8_percent_encode(group_name, NON_ALPHANUMERIC).to_string();
|
||||||
utf8_percent_encode(group_name, NON_ALPHANUMERIC).to_string();
|
|
||||||
|
|
||||||
Some(format!(
|
format!(
|
||||||
"OPENPGP4FPR:{}#a={}&g={}&x={}&i={}&s={}",
|
"OPENPGP4FPR:{}#a={}&g={}&x={}&i={}&s={}",
|
||||||
fingerprint.hex(),
|
fingerprint.hex(),
|
||||||
self_addr_urlencoded,
|
self_addr_urlencoded,
|
||||||
@@ -220,26 +216,22 @@ pub async fn dc_get_securejoin_qr(context: &Context, group: Option<ChatId>) -> O
|
|||||||
&chat.grpid,
|
&chat.grpid,
|
||||||
&invitenumber,
|
&invitenumber,
|
||||||
&auth,
|
&auth,
|
||||||
))
|
)
|
||||||
} else {
|
|
||||||
error!(context, "Cannot get QR-code for chat-id {}", group,);
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// parameters used: a=n=i=s=
|
// parameters used: a=n=i=s=
|
||||||
Some(format!(
|
format!(
|
||||||
"OPENPGP4FPR:{}#a={}&n={}&i={}&s={}",
|
"OPENPGP4FPR:{}#a={}&n={}&i={}&s={}",
|
||||||
fingerprint.hex(),
|
fingerprint.hex(),
|
||||||
self_addr_urlencoded,
|
self_addr_urlencoded,
|
||||||
self_name_urlencoded,
|
self_name_urlencoded,
|
||||||
&invitenumber,
|
&invitenumber,
|
||||||
&auth,
|
&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<Fingerprint> {
|
async fn get_self_fingerprint(context: &Context) -> Option<Fingerprint> {
|
||||||
@@ -720,7 +712,7 @@ pub(crate) async fn handle_securejoin_handshake(
|
|||||||
==========================================================*/
|
==========================================================*/
|
||||||
|
|
||||||
if let Ok(contact) = Contact::get_by_id(context, contact_id).await {
|
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);
|
warn!(context, "{} invalid.", step);
|
||||||
return Ok(HandshakeMessage::Ignore);
|
return Ok(HandshakeMessage::Ignore);
|
||||||
}
|
}
|
||||||
@@ -860,7 +852,7 @@ async fn secure_connection_established(
|
|||||||
"?"
|
"?"
|
||||||
};
|
};
|
||||||
let msg = stock_str::contact_verified(context, addr).await;
|
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));
|
context.emit_event(EventType::ChatModified(contact_chat_id));
|
||||||
info!(context, "StockMessage::ContactVerified posted to 1:1 chat");
|
info!(context, "StockMessage::ContactVerified posted to 1:1 chat");
|
||||||
|
|
||||||
@@ -884,7 +876,7 @@ async fn could_not_establish_secure_connection(
|
|||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
chat::add_info_msg(context, contact_chat_id, &msg, time()).await;
|
chat::add_info_msg(context, contact_chat_id, &msg, time()).await?;
|
||||||
error!(
|
error!(
|
||||||
context,
|
context,
|
||||||
"StockMessage::ContactNotVerified posted to 1:1 chat ({})", details
|
"StockMessage::ContactNotVerified posted to 1:1 chat ({})", details
|
||||||
@@ -958,7 +950,7 @@ mod tests {
|
|||||||
use crate::test_utils::TestContext;
|
use crate::test_utils::TestContext;
|
||||||
|
|
||||||
#[async_std::test]
|
#[async_std::test]
|
||||||
async fn test_setup_contact() {
|
async fn test_setup_contact() -> Result<()> {
|
||||||
let alice = TestContext::new_alice().await;
|
let alice = TestContext::new_alice().await;
|
||||||
let bob = TestContext::new_bob().await;
|
let bob = TestContext::new_bob().await;
|
||||||
|
|
||||||
@@ -975,10 +967,10 @@ mod tests {
|
|||||||
.await;
|
.await;
|
||||||
|
|
||||||
// Step 1: Generate QR-code, ChatId(0) indicates setup-contact
|
// 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
|
// 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;
|
let sent = bob.pop_sent_msg().await;
|
||||||
assert!(!bob.ctx.has_ongoing().await);
|
assert!(!bob.ctx.has_ongoing().await);
|
||||||
@@ -1055,14 +1047,14 @@ mod tests {
|
|||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
contact_bob.is_verified(&alice.ctx).await,
|
contact_bob.is_verified(&alice.ctx).await?,
|
||||||
VerifiedStatus::Unverified
|
VerifiedStatus::Unverified
|
||||||
);
|
);
|
||||||
|
|
||||||
// Step 5+6: Alice receives vc-request-with-auth, sends vc-contact-confirm
|
// Step 5+6: Alice receives vc-request-with-auth, sends vc-contact-confirm
|
||||||
alice.recv_msg(&sent).await;
|
alice.recv_msg(&sent).await;
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
contact_bob.is_verified(&alice.ctx).await,
|
contact_bob.is_verified(&alice.ctx).await?,
|
||||||
VerifiedStatus::BidirectVerified
|
VerifiedStatus::BidirectVerified
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -1104,14 +1096,14 @@ mod tests {
|
|||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
contact_bob.is_verified(&bob.ctx).await,
|
contact_bob.is_verified(&bob.ctx).await?,
|
||||||
VerifiedStatus::Unverified
|
VerifiedStatus::Unverified
|
||||||
);
|
);
|
||||||
|
|
||||||
// Step 7: Bob receives vc-contact-confirm, sends vc-contact-confirm-received
|
// Step 7: Bob receives vc-contact-confirm, sends vc-contact-confirm-received
|
||||||
bob.recv_msg(&sent).await;
|
bob.recv_msg(&sent).await;
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
contact_alice.is_verified(&bob.ctx).await,
|
contact_alice.is_verified(&bob.ctx).await?,
|
||||||
VerifiedStatus::BidirectVerified
|
VerifiedStatus::BidirectVerified
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -1142,6 +1134,7 @@ mod tests {
|
|||||||
msg.get_header(HeaderDef::SecureJoin).unwrap(),
|
msg.get_header(HeaderDef::SecureJoin).unwrap(),
|
||||||
"vc-contact-confirm-received"
|
"vc-contact-confirm-received"
|
||||||
);
|
);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_std::test]
|
#[async_std::test]
|
||||||
@@ -1152,7 +1145,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[async_std::test]
|
#[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 alice = TestContext::new_alice().await;
|
||||||
let bob = TestContext::new_bob().await;
|
let bob = TestContext::new_bob().await;
|
||||||
|
|
||||||
@@ -1169,7 +1162,7 @@ mod tests {
|
|||||||
.await;
|
.await;
|
||||||
|
|
||||||
// Ensure Bob knows Alice_FP
|
// 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 {
|
let peerstate = Peerstate {
|
||||||
addr: "alice@example.com".into(),
|
addr: "alice@example.com".into(),
|
||||||
last_seen: 10,
|
last_seen: 10,
|
||||||
@@ -1185,10 +1178,10 @@ mod tests {
|
|||||||
to_save: Some(ToSave::All),
|
to_save: Some(ToSave::All),
|
||||||
fingerprint_changed: false,
|
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
|
// 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
|
// Step 2+4: Bob scans QR-code, sends vc-request-with-auth, skipping vc-request
|
||||||
dc_join_securejoin(&bob.ctx, &qr).await.unwrap();
|
dc_join_securejoin(&bob.ctx, &qr).await.unwrap();
|
||||||
@@ -1228,10 +1221,7 @@ mod tests {
|
|||||||
"vc-request-with-auth"
|
"vc-request-with-auth"
|
||||||
);
|
);
|
||||||
assert!(msg.get_header(HeaderDef::SecureJoinAuth).is_some());
|
assert!(msg.get_header(HeaderDef::SecureJoinAuth).is_some());
|
||||||
let bob_fp = SignedPublicKey::load_self(&bob.ctx)
|
let bob_fp = SignedPublicKey::load_self(&bob.ctx).await?.fingerprint();
|
||||||
.await
|
|
||||||
.unwrap()
|
|
||||||
.fingerprint();
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*msg.get_header(HeaderDef::SecureJoinFingerprint).unwrap(),
|
*msg.get_header(HeaderDef::SecureJoinFingerprint).unwrap(),
|
||||||
bob_fp.hex()
|
bob_fp.hex()
|
||||||
@@ -1244,20 +1234,17 @@ mod tests {
|
|||||||
"bob@example.net",
|
"bob@example.net",
|
||||||
Origin::ManuallyCreated,
|
Origin::ManuallyCreated,
|
||||||
)
|
)
|
||||||
.await
|
.await?;
|
||||||
.unwrap();
|
let contact_bob = Contact::load_from_db(&alice.ctx, contact_bob_id).await?;
|
||||||
let contact_bob = Contact::load_from_db(&alice.ctx, contact_bob_id)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
contact_bob.is_verified(&alice.ctx).await,
|
contact_bob.is_verified(&alice.ctx).await?,
|
||||||
VerifiedStatus::Unverified
|
VerifiedStatus::Unverified
|
||||||
);
|
);
|
||||||
|
|
||||||
// Step 5+6: Alice receives vc-request-with-auth, sends vc-contact-confirm
|
// Step 5+6: Alice receives vc-request-with-auth, sends vc-contact-confirm
|
||||||
alice.recv_msg(&sent).await;
|
alice.recv_msg(&sent).await;
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
contact_bob.is_verified(&alice.ctx).await,
|
contact_bob.is_verified(&alice.ctx).await?,
|
||||||
VerifiedStatus::BidirectVerified
|
VerifiedStatus::BidirectVerified
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -1275,18 +1262,16 @@ mod tests {
|
|||||||
.await
|
.await
|
||||||
.expect("Error looking up contact")
|
.expect("Error looking up contact")
|
||||||
.expect("Contact not found");
|
.expect("Contact not found");
|
||||||
let contact_alice = Contact::load_from_db(&bob.ctx, contact_alice_id)
|
let contact_alice = Contact::load_from_db(&bob.ctx, contact_alice_id).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
contact_bob.is_verified(&bob.ctx).await,
|
contact_bob.is_verified(&bob.ctx).await?,
|
||||||
VerifiedStatus::Unverified
|
VerifiedStatus::Unverified
|
||||||
);
|
);
|
||||||
|
|
||||||
// Step 7: Bob receives vc-contact-confirm, sends vc-contact-confirm-received
|
// Step 7: Bob receives vc-contact-confirm, sends vc-contact-confirm-received
|
||||||
bob.recv_msg(&sent).await;
|
bob.recv_msg(&sent).await;
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
contact_alice.is_verified(&bob.ctx).await,
|
contact_alice.is_verified(&bob.ctx).await?,
|
||||||
VerifiedStatus::BidirectVerified
|
VerifiedStatus::BidirectVerified
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -1297,10 +1282,11 @@ mod tests {
|
|||||||
msg.get_header(HeaderDef::SecureJoin).unwrap(),
|
msg.get_header(HeaderDef::SecureJoin).unwrap(),
|
||||||
"vc-contact-confirm-received"
|
"vc-contact-confirm-received"
|
||||||
);
|
);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_std::test]
|
#[async_std::test]
|
||||||
async fn test_secure_join() {
|
async fn test_secure_join() -> Result<()> {
|
||||||
let alice = TestContext::new_alice().await;
|
let alice = TestContext::new_alice().await;
|
||||||
let bob = TestContext::new_bob().await;
|
let bob = TestContext::new_bob().await;
|
||||||
|
|
||||||
@@ -1316,9 +1302,8 @@ mod tests {
|
|||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let chatid = chat::create_group_chat(&alice.ctx, ProtectionStatus::Protected, "the chat")
|
let chatid =
|
||||||
.await
|
chat::create_group_chat(&alice.ctx, ProtectionStatus::Protected, "the chat").await?;
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// Step 1: Generate QR-code, secure-join implied by chatid
|
// Step 1: Generate QR-code, secure-join implied by chatid
|
||||||
let qr = dc_get_securejoin_qr(&alice.ctx, Some(chatid))
|
let qr = dc_get_securejoin_qr(&alice.ctx, Some(chatid))
|
||||||
@@ -1388,10 +1373,7 @@ mod tests {
|
|||||||
"vg-request-with-auth"
|
"vg-request-with-auth"
|
||||||
);
|
);
|
||||||
assert!(msg.get_header(HeaderDef::SecureJoinAuth).is_some());
|
assert!(msg.get_header(HeaderDef::SecureJoinAuth).is_some());
|
||||||
let bob_fp = SignedPublicKey::load_self(&bob.ctx)
|
let bob_fp = SignedPublicKey::load_self(&bob.ctx).await?.fingerprint();
|
||||||
.await
|
|
||||||
.unwrap()
|
|
||||||
.fingerprint();
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*msg.get_header(HeaderDef::SecureJoinFingerprint).unwrap(),
|
*msg.get_header(HeaderDef::SecureJoinFingerprint).unwrap(),
|
||||||
bob_fp.hex()
|
bob_fp.hex()
|
||||||
@@ -1400,21 +1382,18 @@ mod tests {
|
|||||||
// Alice should not yet have Bob verified
|
// Alice should not yet have Bob verified
|
||||||
let contact_bob_id =
|
let contact_bob_id =
|
||||||
Contact::lookup_id_by_addr(&alice.ctx, "bob@example.net", Origin::Unknown)
|
Contact::lookup_id_by_addr(&alice.ctx, "bob@example.net", Origin::Unknown)
|
||||||
.await
|
.await?
|
||||||
.expect("Error looking up contact")
|
|
||||||
.expect("Contact not found");
|
.expect("Contact not found");
|
||||||
let contact_bob = Contact::load_from_db(&alice.ctx, contact_bob_id)
|
let contact_bob = Contact::load_from_db(&alice.ctx, contact_bob_id).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
contact_bob.is_verified(&alice.ctx).await,
|
contact_bob.is_verified(&alice.ctx).await?,
|
||||||
VerifiedStatus::Unverified
|
VerifiedStatus::Unverified
|
||||||
);
|
);
|
||||||
|
|
||||||
// Step 5+6: Alice receives vg-request-with-auth, sends vg-member-added
|
// Step 5+6: Alice receives vg-request-with-auth, sends vg-member-added
|
||||||
alice.recv_msg(&sent).await;
|
alice.recv_msg(&sent).await;
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
contact_bob.is_verified(&alice.ctx).await,
|
contact_bob.is_verified(&alice.ctx).await?,
|
||||||
VerifiedStatus::BidirectVerified
|
VerifiedStatus::BidirectVerified
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -1432,18 +1411,16 @@ mod tests {
|
|||||||
.await
|
.await
|
||||||
.expect("Error looking up contact")
|
.expect("Error looking up contact")
|
||||||
.expect("Contact not found");
|
.expect("Contact not found");
|
||||||
let contact_alice = Contact::load_from_db(&bob.ctx, contact_alice_id)
|
let contact_alice = Contact::load_from_db(&bob.ctx, contact_alice_id).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
contact_bob.is_verified(&bob.ctx).await,
|
contact_bob.is_verified(&bob.ctx).await?,
|
||||||
VerifiedStatus::Unverified
|
VerifiedStatus::Unverified
|
||||||
);
|
);
|
||||||
|
|
||||||
// Step 7: Bob receives vg-member-added, sends vg-member-added-received
|
// Step 7: Bob receives vg-member-added, sends vg-member-added-received
|
||||||
bob.recv_msg(&sent).await;
|
bob.recv_msg(&sent).await;
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
contact_alice.is_verified(&bob.ctx).await,
|
contact_alice.is_verified(&bob.ctx).await?,
|
||||||
VerifiedStatus::BidirectVerified
|
VerifiedStatus::BidirectVerified
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -1456,8 +1433,9 @@ mod tests {
|
|||||||
);
|
);
|
||||||
|
|
||||||
let bob_chatid = joiner.await;
|
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_chat.is_protected());
|
||||||
assert!(!bob.ctx.has_ongoing().await)
|
assert!(!bob.ctx.has_ongoing().await);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user