Merge remote-tracking branch 'origin/master' into feat/async-jobs

This commit is contained in:
dignifiedquire
2020-03-26 16:52:07 +01:00
10 changed files with 248 additions and 18 deletions

View File

@@ -1,5 +1,19 @@
# Changelog # Changelog
## 1.28.0
- new flag DC_GCL_FOR_FORWARDING for dc_get_chatlist()
that will sort the "saved messages" chat to the top of the chatlist #1336
- mark mails as being deleted from server in dc_empty_server() #1333
- fix interaction with servers that do not allow folder creation on root-level;
use path separator as defined by the email server #1359
- fix group creation if group was created by non-delta clients #1357
- fix showing replies from non-delta clients #1353
- fix member list on rejoining left groups #1343
- fix crash when using empty groups #1354
- fix potential crash on special names #1350
## 1.27.0 ## 1.27.0
- handle keys reliably on armv7 #1327 - handle keys reliably on armv7 #1327

6
Cargo.lock generated
View File

@@ -650,7 +650,7 @@ dependencies = [
[[package]] [[package]]
name = "deltachat" name = "deltachat"
version = "1.27.0" version = "1.28.0"
dependencies = [ dependencies = [
"ansi_term 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", "ansi_term 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)",
"async-imap 0.2.0 (git+https://github.com/async-email/async-imap?branch=feat/send)", "async-imap 0.2.0 (git+https://github.com/async-email/async-imap?branch=feat/send)",
@@ -720,10 +720,10 @@ dependencies = [
[[package]] [[package]]
name = "deltachat_ffi" name = "deltachat_ffi"
version = "1.27.0" version = "1.28.0"
dependencies = [ dependencies = [
"async-std 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "async-std 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"deltachat 1.27.0", "deltachat 1.28.0",
"failure 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", "failure 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
"human-panic 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "human-panic 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)",

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "deltachat" name = "deltachat"
version = "1.27.0" version = "1.28.0"
authors = ["Delta Chat Developers (ML) <delta@codespeak.net>"] authors = ["Delta Chat Developers (ML) <delta@codespeak.net>"]
edition = "2018" edition = "2018"
license = "MPL-2.0" license = "MPL-2.0"

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "deltachat_ffi" name = "deltachat_ffi"
version = "1.27.0" version = "1.28.0"
description = "Deltachat FFI" description = "Deltachat FFI"
authors = ["Delta Chat Developers (ML) <delta@codespeak.net>"] authors = ["Delta Chat Developers (ML) <delta@codespeak.net>"]
edition = "2018" edition = "2018"

View File

@@ -76,6 +76,20 @@ class TestOfflineAccountBasic:
with pytest.raises(KeyError): with pytest.raises(KeyError):
ac1.get_config("123123") ac1.get_config("123123")
def test_empty_group_bcc_self_enabled(self, acfactory):
ac1 = acfactory.get_configured_offline_account()
ac1.set_config("bcc_self", "1")
chat = ac1.create_group_chat(name="group1")
msg = chat.send_text("msg1")
assert msg in chat.get_messages()
def test_empty_group_bcc_self_disabled(self, acfactory):
ac1 = acfactory.get_configured_offline_account()
ac1.set_config("bcc_self", "0")
chat = ac1.create_group_chat(name="group1")
msg = chat.send_text("msg1")
assert msg in chat.get_messages()
class TestOfflineContact: class TestOfflineContact:
def test_contact_attr(self, acfactory): def test_contact_attr(self, acfactory):

View File

@@ -412,9 +412,11 @@ async fn exec_step(
} }
16 => { 16 => {
progress!(ctx, 900); progress!(ctx, 900);
let create_mvbox = ctx.get_config_bool(Config::MvboxWatch).await let create_mvbox = ctx.get_config_bool(Config::MvboxWatch).await
|| ctx.get_config_bool(Config::MvboxMove).await; || ctx.get_config_bool(Config::MvboxMove).await;
if let Err(err) = imap.ensure_configured_folders(ctx, create_mvbox).await {
if let Err(err) = imap.configure_folders(ctx, create_mvbox).await {
bail!("configuring folders failed: {:?}", err); bail!("configuring folders failed: {:?}", err);
} }

View File

@@ -214,6 +214,9 @@ pub const DC_BOB_SUCCESS: i32 = 1;
// max. width/height of an avatar // max. width/height of an avatar
pub const AVATAR_SIZE: u32 = 192; pub const AVATAR_SIZE: u32 = 192;
// this value can be increased if the folder configuration is changed and must be redone on next program start
pub const DC_FOLDERS_CONFIGURED_VERSION: i32 = 3;
#[derive( #[derive(
Debug, Debug,
Display, Display,

View File

@@ -402,7 +402,11 @@ async fn add_parts(
let (new_chat_id, new_chat_id_blocked) = create_or_lookup_group( let (new_chat_id, new_chat_id_blocked) = create_or_lookup_group(
context, context,
&mut mime_parser, &mut mime_parser,
allow_creation, if test_normal_chat_id.is_unset() {
allow_creation
} else {
true
},
create_blocked, create_blocked,
from_id, from_id,
to_ids, to_ids,
@@ -1715,8 +1719,9 @@ fn dc_create_incoming_rfc724_mid(
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::chatlist::Chatlist;
use crate::message::Message; use crate::message::Message;
use crate::test_utils::dummy_context; use crate::test_utils::{dummy_context, TestContext};
#[test] #[test]
fn test_hex_hash() { fn test_hex_hash() {
@@ -1811,4 +1816,179 @@ mod tests {
assert!(is_msgrmsg_rfc724_mid(&t.ctx, &msg.rfc724_mid).await); assert!(is_msgrmsg_rfc724_mid(&t.ctx, &msg.rfc724_mid).await);
assert!(!is_msgrmsg_rfc724_mid(&t.ctx, "nonexistant@message.id").await); assert!(!is_msgrmsg_rfc724_mid(&t.ctx, "nonexistant@message.id").await);
} }
async fn configured_offline_context() -> TestContext {
let t = dummy_context().await;
t.ctx
.set_config(Config::Addr, Some("alice@example.org"))
.await
.unwrap();
t.ctx
.set_config(Config::ConfiguredAddr, Some("alice@example.org"))
.await
.unwrap();
t.ctx
.set_config(Config::Configured, Some("1"))
.await
.unwrap();
t
}
static MSGRMSG: &[u8] = b"From: Bob <bob@example.org>\n\
To: alice@example.org\n\
Chat-Version: 1.0\n\
Subject: Chat: hello\n\
Message-ID: <Mr.1111@example.org>\n\
Date: Sun, 22 Mar 2020 22:37:55 +0000\n\
\n\
hello\n";
static ONETOONE_NOREPLY_MAIL: &[u8] = b"From: Bob <bob@example.org>\n\
To: alice@example.org\n\
Subject: Chat: hello\n\
Message-ID: <2222@example.org>\n\
Date: Sun, 22 Mar 2020 22:37:56 +0000\n\
\n\
hello\n";
static GRP_MAIL: &[u8] = b"From: bob@example.org\n\
To: alice@example.org, claire@example.org\n\
Subject: group with Alice, Bob and Claire\n\
Message-ID: <3333@example.org>\n\
Date: Sun, 22 Mar 2020 22:37:57 +0000\n\
\n\
hello\n";
#[async_std::test]
async fn test_adhoc_group_show_chats_only() {
let t = configured_offline_context().await;
assert_eq!(t.ctx.get_config_int(Config::ShowEmails).await, 0);
let chats = Chatlist::try_load(&t.ctx, 0, None, None).await.unwrap();
assert_eq!(chats.len(), 0);
dc_receive_imf(&t.ctx, MSGRMSG, "INBOX", 1, false)
.await
.unwrap();
let chats = Chatlist::try_load(&t.ctx, 0, None, None).await.unwrap();
assert_eq!(chats.len(), 1);
dc_receive_imf(&t.ctx, ONETOONE_NOREPLY_MAIL, "INBOX", 1, false)
.await
.unwrap();
let chats = Chatlist::try_load(&t.ctx, 0, None, None).await.unwrap();
assert_eq!(chats.len(), 1);
dc_receive_imf(&t.ctx, GRP_MAIL, "INBOX", 1, false)
.await
.unwrap();
let chats = Chatlist::try_load(&t.ctx, 0, None, None).await.unwrap();
assert_eq!(chats.len(), 1);
}
#[async_std::test]
async fn test_adhoc_group_show_accepted_contact_unknown() {
let t = configured_offline_context().await;
t.ctx
.set_config(Config::ShowEmails, Some("1"))
.await
.unwrap();
dc_receive_imf(&t.ctx, GRP_MAIL, "INBOX", 1, false)
.await
.unwrap();
// adhoc-group with unknown contacts with show_emails=accepted is ignored for unknown contacts
let chats = Chatlist::try_load(&t.ctx, 0, None, None).await.unwrap();
assert_eq!(chats.len(), 0);
}
#[async_std::test]
async fn test_adhoc_group_show_accepted_contact_known() {
let t = configured_offline_context().await;
t.ctx
.set_config(Config::ShowEmails, Some("1"))
.await
.unwrap();
Contact::create(&t.ctx, "Bob", "bob@example.org")
.await
.unwrap();
dc_receive_imf(&t.ctx, GRP_MAIL, "INBOX", 1, false)
.await
.unwrap();
// adhoc-group with known contacts with show_emails=accepted is still ignored for known contacts
// (and existent chat is required)
let chats = Chatlist::try_load(&t.ctx, 0, None, None).await.unwrap();
assert_eq!(chats.len(), 0);
}
#[async_std::test]
async fn test_adhoc_group_show_accepted_contact_accepted() {
let t = configured_offline_context().await;
t.ctx
.set_config(Config::ShowEmails, Some("1"))
.await
.unwrap();
// accept Bob by accepting a delta-message from Bob
dc_receive_imf(&t.ctx, MSGRMSG, "INBOX", 1, false)
.await
.unwrap();
let chats = Chatlist::try_load(&t.ctx, 0, None, None).await.unwrap();
assert_eq!(chats.len(), 1);
assert!(chats.get_chat_id(0).is_deaddrop());
let chat_id = chat::create_by_msg_id(&t.ctx, chats.get_msg_id(0).unwrap())
.await
.unwrap();
assert!(!chat_id.is_special());
let chat = chat::Chat::load_from_db(&t.ctx, chat_id).await.unwrap();
assert_eq!(chat.typ, Chattype::Single);
assert_eq!(chat.name, "Bob");
assert_eq!(chat::get_chat_contacts(&t.ctx, chat_id).await.len(), 1);
assert_eq!(chat::get_chat_msgs(&t.ctx, chat_id, 0, None).await.len(), 1);
// receive a non-delta-message from Bob, shows up because of the show_emails setting
dc_receive_imf(&t.ctx, ONETOONE_NOREPLY_MAIL, "INBOX", 2, false)
.await
.unwrap();
assert_eq!(chat::get_chat_msgs(&t.ctx, chat_id, 0, None).await.len(), 2);
// let Bob create an adhoc-group by a non-delta-message, shows up because of the show_emails setting
dc_receive_imf(&t.ctx, GRP_MAIL, "INBOX", 3, false)
.await
.unwrap();
let chats = Chatlist::try_load(&t.ctx, 0, None, None).await.unwrap();
assert_eq!(chats.len(), 2);
let chat_id = chat::create_by_msg_id(&t.ctx, chats.get_msg_id(0).unwrap())
.await
.unwrap();
let chat = chat::Chat::load_from_db(&t.ctx, chat_id).await.unwrap();
assert_eq!(chat.typ, Chattype::Group);
assert_eq!(chat.name, "group with Alice, Bob and Claire");
assert_eq!(chat::get_chat_contacts(&t.ctx, chat_id).await.len(), 3);
}
#[async_std::test]
async fn test_adhoc_group_show_all() {
let t = configured_offline_context().await;
t.ctx
.set_config(Config::ShowEmails, Some("2"))
.await
.unwrap();
dc_receive_imf(&t.ctx, GRP_MAIL, "INBOX", 1, false)
.await
.unwrap();
// adhoc-group with unknown contacts with show_emails=all will show up in the deaddrop
let chats = Chatlist::try_load(&t.ctx, 0, None, None).await.unwrap();
assert_eq!(chats.len(), 1);
assert!(chats.get_chat_id(0).is_deaddrop());
let chat_id = chat::create_by_msg_id(&t.ctx, chats.get_msg_id(0).unwrap())
.await
.unwrap();
let chat = chat::Chat::load_from_db(&t.ctx, chat_id).await.unwrap();
assert_eq!(chat.typ, Chattype::Group);
assert_eq!(chat.name, "group with Alice, Bob and Claire");
assert_eq!(chat::get_chat_contacts(&t.ctx, chat_id).await.len(), 3);
}
} }

View File

@@ -187,7 +187,6 @@ struct ImapConfig {
/// True if the server has MOVE capability as defined in /// True if the server has MOVE capability as defined in
/// https://tools.ietf.org/html/rfc6851 /// https://tools.ietf.org/html/rfc6851
pub can_move: bool, pub can_move: bool,
pub imap_delimiter: char,
} }
impl Default for ImapConfig { impl Default for ImapConfig {
@@ -205,7 +204,6 @@ impl Default for ImapConfig {
selected_folder_needs_expunge: false, selected_folder_needs_expunge: false,
can_idle: false, can_idle: false,
can_move: false, can_move: false,
imap_delimiter: '.',
} }
} }
} }
@@ -1111,17 +1109,17 @@ impl Imap {
.sql .sql
.get_raw_config_int(context, "folders_configured") .get_raw_config_int(context, "folders_configured")
.await; .await;
if folders_configured.unwrap_or_default() >= 3 { if folders_configured.unwrap_or_default() >= DC_FOLDERS_CONFIGURED_VERSION {
info!(context, "IMAP-folders already configured");
// the "3" here we increase if we have future updates to
// to folder configuration
return Ok(()); return Ok(());
} }
self.configure_folders(context, create_mvbox).await
}
pub async fn configure_folders(&mut self, context: &Context, create_mvbox: bool) -> Result<()> {
if !self.is_connected() { if !self.is_connected() {
return Err(Error::NoConnection); return Err(Error::NoConnection);
} }
info!(context, "Configuring IMAP-folders.");
if let Some(ref mut session) = &mut self.session { if let Some(ref mut session) = &mut self.session {
let mut folders = match session.list(Some(""), Some("*")).await { let mut folders = match session.list(Some(""), Some("*")).await {
@@ -1133,7 +1131,17 @@ impl Imap {
let mut sentbox_folder = None; let mut sentbox_folder = None;
let mut mvbox_folder = None; let mut mvbox_folder = None;
let delimiter = self.config.imap_delimiter;
let mut delimiter = ".".to_string();
if let Some(folder) = folders.next().await {
let folder = folder.map_err(|err| Error::Other(err.to_string()))?;
if let Some(d) = folder.delimiter() {
if !d.is_empty() {
delimiter = d.to_string();
}
}
}
info!(context, "Using \"{}\" as folder-delimiter.", delimiter);
let fallback_folder = format!("INBOX{}DeltaChat", delimiter); let fallback_folder = format!("INBOX{}DeltaChat", delimiter);
while let Some(folder) = folders.next().await { while let Some(folder) = folders.next().await {
@@ -1221,9 +1229,14 @@ impl Imap {
} }
context context
.sql .sql
.set_raw_config_int(context, "folders_configured", 3) .set_raw_config_int(context, "folders_configured", DC_FOLDERS_CONFIGURED_VERSION)
.await?; .await?;
} }
context
.sql
.set_raw_config_int(context, "folders_configured", 3)
.await?;
info!(context, "FINISHED configuring IMAP-folders."); info!(context, "FINISHED configuring IMAP-folders.");
Ok(()) Ok(())
} }

View File

@@ -408,7 +408,7 @@ impl<'a, 'b> MimeFactory<'a, 'b> {
self.from_addr.clone(), self.from_addr.clone(),
); );
let mut to = Vec::with_capacity(self.recipients.len()); let mut to = Vec::new();
for (name, addr) in self.recipients.iter() { for (name, addr) in self.recipients.iter() {
if name.is_empty() { if name.is_empty() {
to.push(Address::new_mailbox(addr.clone())); to.push(Address::new_mailbox(addr.clone()));
@@ -420,6 +420,10 @@ impl<'a, 'b> MimeFactory<'a, 'b> {
} }
} }
if to.is_empty() {
to.push(from.clone());
}
if !self.references.is_empty() { if !self.references.is_empty() {
unprotected_headers.push(Header::new("References".into(), self.references.clone())); unprotected_headers.push(Header::new("References".into(), self.references.clone()));
} }