From da5d844ec4fb903b94fcce36420299d04c6b7d47 Mon Sep 17 00:00:00 2001 From: link2xt Date: Sat, 14 Oct 2023 04:25:13 +0000 Subject: [PATCH 01/22] chore: rustfmt --- src/smtp.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/smtp.rs b/src/smtp.rs index de9b8915b..f2998b8cc 100644 --- a/src/smtp.rs +++ b/src/smtp.rs @@ -790,7 +790,10 @@ async fn send_mdn(context: &Context, smtp: &mut Smtp) -> Result { if let Err(ref err) = res { // If there is an error, for example there is no message corresponding to the msg_id in the // database, do not try to send this MDN again. - warn!(context, "Error sending MDN for {msg_id}, removing it: {err:#}."); + warn!( + context, + "Error sending MDN for {msg_id}, removing it: {err:#}." + ); context .sql .execute("DELETE FROM smtp_mdns WHERE msg_id = ?", (msg_id,)) From b2395359649d7d827fed65429af95dfab226baf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Kl=C3=A4hn?= <39526136+Septias@users.noreply.github.com> Date: Sat, 14 Oct 2023 11:12:53 +0200 Subject: [PATCH 02/22] api: allow to filter by unread in chatlist:try_load (#4824) close #4738 --- deltachat-ffi/deltachat.h | 3 ++- src/chatlist.rs | 47 ++++++++++++++++++++++++++++++++++----- 2 files changed, 43 insertions(+), 7 deletions(-) diff --git a/deltachat-ffi/deltachat.h b/deltachat-ffi/deltachat.h index 4c235adf3..5cd96d8b4 100644 --- a/deltachat-ffi/deltachat.h +++ b/deltachat-ffi/deltachat.h @@ -884,7 +884,8 @@ int dc_preconfigure_keypair (dc_context_t* context, const cha * - if the flag DC_GCL_ADD_ALLDONE_HINT is set, DC_CHAT_ID_ALLDONE_HINT * is added as needed. * @param query_str An optional query for filtering the list. Only chats matching this query - * are returned. Give NULL for no filtering. + * are returned. Give NULL for no filtering. When `is:unread` is contained in the query, + * the chatlist is filtered such that only chats with unread messages show up. * @param query_id An optional contact ID for filtering the list. Only chats including this contact ID * are returned. Give 0 for no filtering. * @return A chatlist as an dc_chatlist_t object. diff --git a/src/chatlist.rs b/src/chatlist.rs index 2cb14c012..70b07ba73 100644 --- a/src/chatlist.rs +++ b/src/chatlist.rs @@ -1,6 +1,7 @@ //! # Chat list module. use anyhow::{ensure, Context as _, Result}; +use once_cell::sync::Lazy; use crate::chat::{update_special_chat_names, Chat, ChatId, ChatVisibility}; use crate::constants::{ @@ -15,6 +16,10 @@ use crate::stock_str; use crate::summary::Summary; use crate::tools::IsNoneOrEmpty; +/// Regex to find out if a query should filter by unread messages. +pub static IS_UNREAD_FILTER: Lazy = + Lazy::new(|| regex::Regex::new(r"\bis:unread\b").unwrap()); + /// An object representing a single chatlist in memory. /// /// Chatlist objects contain chat IDs and, if possible, message IDs belonging to them. @@ -78,7 +83,8 @@ impl Chatlist { /// - if the flag DC_GCL_ADD_ALLDONE_HINT is set, DC_CHAT_ID_ALLDONE_HINT /// is added as needed. /// `query`: An optional query for filtering the list. Only chats matching this query - /// are returned. + /// are returned. When `is:unread` is contained in the query, the chatlist is + /// filtered such that only chats with unread messages show up. /// `query_contact_id`: An optional contact ID for filtering the list. Only chats including this contact ID /// are returned. pub async fn try_load( @@ -172,8 +178,10 @@ impl Chatlist { ) .await? } else if let Some(query) = query { - let query = query.trim().to_string(); - ensure!(!query.is_empty(), "missing query"); + let mut query = query.trim().to_string(); + ensure!(!query.is_empty(), "query mustn't be empty"); + let only_unread = IS_UNREAD_FILTER.find(&query).is_some(); + query = IS_UNREAD_FILTER.replace(&query, "").trim().to_string(); // allow searching over special names that may change at any time // when the ui calls set_stock_translation() @@ -198,9 +206,10 @@ impl Chatlist { WHERE c.id>9 AND c.id!=?2 AND c.blocked!=1 AND c.name LIKE ?3 + AND (NOT ?4 OR EXISTS (SELECT 1 FROM msgs m WHERE m.chat_id = c.id AND m.state == ?5 AND hidden=0)) GROUP BY c.id ORDER BY IFNULL(m.timestamp,c.created_timestamp) DESC, m.id DESC;", - (MessageState::OutDraft, skip_id, str_like_cmd), + (MessageState::OutDraft, skip_id, str_like_cmd, only_unread, MessageState::InFresh), process_row, process_rows, ) @@ -462,7 +471,8 @@ pub async fn get_last_message_for_chat( mod tests { use super::*; use crate::chat::{ - create_group_chat, get_chat_contacts, remove_contact_from_chat, ProtectionStatus, + add_contact_to_chat, create_group_chat, get_chat_contacts, remove_contact_from_chat, + send_text_msg, ProtectionStatus, }; use crate::message::Viewtype; use crate::receive_imf::receive_imf; @@ -471,7 +481,7 @@ mod tests { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_try_load() { - let t = TestContext::new().await; + let t = TestContext::new_bob().await; let chat_id1 = create_group_chat(&t, ProtectionStatus::Unprotected, "a chat") .await .unwrap(); @@ -510,6 +520,31 @@ mod tests { let chats = Chatlist::try_load(&t, 0, Some("b"), None).await.unwrap(); assert_eq!(chats.len(), 1); + // receive a message from alice + let alice = TestContext::new_alice().await; + let alice_chat_id = create_group_chat(&alice, ProtectionStatus::Unprotected, "alice chat") + .await + .unwrap(); + add_contact_to_chat( + &alice, + alice_chat_id, + Contact::create(&alice, "bob", "bob@example.net") + .await + .unwrap(), + ) + .await + .unwrap(); + send_text_msg(&alice, alice_chat_id, "hi".into()) + .await + .unwrap(); + let sent_msg = alice.pop_sent_msg().await; + + t.recv_msg(&sent_msg).await; + let chats = Chatlist::try_load(&t, 0, Some("is:unread"), None) + .await + .unwrap(); + assert!(chats.len() == 1); + let chats = Chatlist::try_load(&t, DC_GCL_ARCHIVED_ONLY, None, None) .await .unwrap(); From 54ea3ec5d6f53c177655c4b3d6e7fde92dba30ec Mon Sep 17 00:00:00 2001 From: link2xt Date: Sun, 15 Oct 2023 22:57:46 +0000 Subject: [PATCH 03/22] build: workaround OpenSSL crate expecting libatomic to be available --- scripts/zig-cc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/scripts/zig-cc b/scripts/zig-cc index 4b1293dde..b6d9ebc68 100755 --- a/scripts/zig-cc +++ b/scripts/zig-cc @@ -5,6 +5,10 @@ import os def flag_filter(flag: str) -> bool: + # Workaround for . + if flag == "-latomic": + return False + if flag == "-lc": return False if flag == "-Wl,-melf_i386": @@ -24,6 +28,11 @@ def main(): else: zig_cpu_args = [] + # Disable atomics and use locks instead in OpenSSL. + # Zig toolchains do not provide atomics. + # This is a workaround for + args += ["-DBROKEN_CLANG_ATOMICS"] + subprocess.run( ["zig", "cc", "-target", zig_target, *zig_cpu_args, *args], check=True ) From 52c46c6dca58a8404e38303b6c81d90fb5eb735d Mon Sep 17 00:00:00 2001 From: link2xt Date: Mon, 16 Oct 2023 03:36:31 +0000 Subject: [PATCH 04/22] build: add script to build deltachat-rpc-server wheels --- scripts/wheel-rpc-server.py | 73 +++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100755 scripts/wheel-rpc-server.py diff --git a/scripts/wheel-rpc-server.py b/scripts/wheel-rpc-server.py new file mode 100755 index 000000000..8809852dd --- /dev/null +++ b/scripts/wheel-rpc-server.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python3 +"""Build Python wheels for deltachat-rpc-server. +Run scripts/zig-rpc-server.sh first.""" +from pathlib import Path +from wheel.wheelfile import WheelFile +import tomllib + + +def build_wheel(version, binary, tag): + filename = f"dist/deltachat_rpc_server-{version}-{tag}.whl" + + with WheelFile(filename, "w") as wheel: + wheel.write("LICENSE", "deltachat_rpc_server/LICENSE") + wheel.write("deltachat-rpc-server/README.md", "deltachat_rpc_server/README.md") + wheel.writestr( + "deltachat_rpc_server/__init__.py", + """import os, sys +def main(): + argv = [os.path.join(os.path.dirname(__file__), "deltachat-rpc-server"), *sys.argv[1:]] + os.execv(argv[0], argv) +""", + ) + + wheel.write( + binary, + "deltachat_rpc_server/deltachat-rpc-server", + ) + wheel.writestr( + f"deltachat_rpc_server-{version}.dist-info/METADATA", + f"""Metadata-Version: 2.1 +Name: deltachat-rpc-server +Version: {version} +Summary: Delta Chat JSON-RPC server +""", + ) + wheel.writestr( + f"deltachat_rpc_server-{version}.dist-info/WHEEL", + "Wheel-Version: 1.0\nRoot-Is-Purelib: false\nTag: {tag}", + ) + wheel.writestr( + f"deltachat_rpc_server-{version}.dist-info/entry_points.txt", + "[console_scripts]\ndeltachat-rpc-server = deltachat_rpc_server:main", + ) + + +def main(): + with open("deltachat-rpc-server/Cargo.toml", "rb") as f: + cargo_toml = tomllib.load(f) + version = cargo_toml["package"]["version"] + Path("dist").mkdir(exist_ok=True) + build_wheel( + version, + "target/x86_64-unknown-linux-musl/release/deltachat-rpc-server", + "py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.musllinux_1_1_x86_64", + ) + build_wheel( + version, + "target/armv7-unknown-linux-musleabihf/release/deltachat-rpc-server", + "py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.musllinux_1_1_armv7l", + ) + build_wheel( + version, + "target/aarch64-unknown-linux-musl/release/deltachat-rpc-server", + "py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64", + ) + build_wheel( + version, + "target/i686-unknown-linux-musl/release/deltachat-rpc-server", + "py3-none-manylinux_2_12_i686.manylinux2010_i686.musllinux_1_1_i686", + ) + + +main() From 8573649bf737fb22380074efb4acf302a2dac4ee Mon Sep 17 00:00:00 2001 From: Hocuri Date: Tue, 17 Oct 2023 10:40:47 +0200 Subject: [PATCH 05/22] feat: Make broadcast lists create their own chat (#4644) feat: Make broadcast lists create their own chat - UIs need to ask for the name when creating broadcast lists now (see https://github.com/deltachat/deltachat-android/pull/2653) That's quite a minimal approach: Add a List-ID header to outgoing broadcast lists, so that the receiving Delta Chat shows them as a separate chat, as talked about with @r10s and @hpk42. Done: - [x] Fix an existing bug that the chat name isn't updated when the broadcast/mailing list name changes (I already started this locally) To be done in other PRs: - [ ] Right now the receiving side shows "Mailing list" in the subtitle of such a chat, it would be nicer if it showed "Broadcast list" (or alternatively, rename "Broadcast list" to "Mailing list", too) - [ ] The UIs should probably ask for a name before creating the broadcast list, since it will actually be sent over the wire. (Android PR: https://github.com/deltachat/deltachat-android/pull/2653) Fixes https://github.com/deltachat/deltachat-core-rust/issues/4597 BREAKING CHANGE: This means that UIs need to ask for the name when creating a broadcast list, similar to https://github.com/deltachat/deltachat-android/pull/2653. --- deltachat-ffi/deltachat.h | 22 +--- deltachat-jsonrpc/src/api/mod.rs | 22 +--- src/chat.rs | 46 +++++--- src/mimefactory.rs | 29 +++-- src/mimeparser.rs | 39 ++----- src/receive_imf.rs | 185 ++++++++++++++++--------------- 6 files changed, 170 insertions(+), 173 deletions(-) diff --git a/deltachat-ffi/deltachat.h b/deltachat-ffi/deltachat.h index 5cd96d8b4..d021f1c5b 100644 --- a/deltachat-ffi/deltachat.h +++ b/deltachat-ffi/deltachat.h @@ -1718,24 +1718,12 @@ uint32_t dc_create_group_chat (dc_context_t* context, int protect * Create a new broadcast list. * * Broadcast lists are similar to groups on the sending device, - * however, recipients get the messages in normal one-to-one chats - * and will not be aware of other members. + * however, recipients get the messages in a read-only chat + * and will see who the other members are. * - * Replies to broadcasts go only to the sender - * and not to all broadcast recipients. - * Moreover, replies will not appear in the broadcast list - * but in the one-to-one chat with the person answering. - * - * The name and the image of the broadcast list is set automatically - * and is visible to the sender only. - * Not asking for these data allows more focused creation - * and we bypass the question who will get which data. - * Also, many users will have at most one broadcast list - * so, a generic name and image is sufficient at the first place. - * - * Later on, however, the name can be changed using dc_set_chat_name(). - * The image cannot be changed to have a unique, recognizable icon in the chat lists. - * All in all, this is also what other messengers are doing here. + * For historical reasons, this function does not take a name directly, + * instead you have to set the name using dc_set_chat_name() + * after creating the broadcast list. * * @memberof dc_context_t * @param context The context object. diff --git a/deltachat-jsonrpc/src/api/mod.rs b/deltachat-jsonrpc/src/api/mod.rs index bbbf1fdf4..bcdc642f0 100644 --- a/deltachat-jsonrpc/src/api/mod.rs +++ b/deltachat-jsonrpc/src/api/mod.rs @@ -812,24 +812,12 @@ impl CommandApi { /// Create a new broadcast list. /// /// Broadcast lists are similar to groups on the sending device, - /// however, recipients get the messages in normal one-to-one chats - /// and will not be aware of other members. + /// however, recipients get the messages in a read-only chat + /// and will see who the other members are. /// - /// Replies to broadcasts go only to the sender - /// and not to all broadcast recipients. - /// Moreover, replies will not appear in the broadcast list - /// but in the one-to-one chat with the person answering. - /// - /// The name and the image of the broadcast list is set automatically - /// and is visible to the sender only. - /// Not asking for these data allows more focused creation - /// and we bypass the question who will get which data. - /// Also, many users will have at most one broadcast list - /// so, a generic name and image is sufficient at the first place. - /// - /// Later on, however, the name can be changed using dc_set_chat_name(). - /// The image cannot be changed to have a unique, recognizable icon in the chat lists. - /// All in all, this is also what other messengers are doing here. + /// For historical reasons, this function does not take a name directly, + /// instead you have to set the name using dc_set_chat_name() + /// after creating the broadcast list. async fn create_broadcast_list(&self, account_id: u32) -> Result { let ctx = self.get_context(account_id).await?; chat::create_broadcast_list(&ctx) diff --git a/src/chat.rs b/src/chat.rs index 70025f40f..5f2580cbf 100644 --- a/src/chat.rs +++ b/src/chat.rs @@ -6094,22 +6094,40 @@ mod tests { get_chat_contacts(&alice, chat_bob.id).await?.pop().unwrap(), ) .await?; - let chat = Chat::load_from_db(&alice, broadcast_id).await?; - assert_eq!(chat.typ, Chattype::Broadcast); - assert_eq!(chat.name, stock_str::broadcast_list(&alice).await); - assert!(!chat.is_self_talk()); + set_chat_name(&alice, broadcast_id, "Broadcast list").await?; + { + let chat = Chat::load_from_db(&alice, broadcast_id).await?; + assert_eq!(chat.typ, Chattype::Broadcast); + assert_eq!(chat.name, "Broadcast list"); + assert!(!chat.is_self_talk()); - send_text_msg(&alice, broadcast_id, "ola!".to_string()).await?; - let msg = alice.get_last_msg().await; - assert_eq!(msg.chat_id, chat.id); + send_text_msg(&alice, broadcast_id, "ola!".to_string()).await?; + let msg = alice.get_last_msg().await; + assert_eq!(msg.chat_id, chat.id); + } - let msg = bob.recv_msg(&alice.pop_sent_msg().await).await; - assert_eq!(msg.get_text(), "ola!"); - assert!(!msg.get_showpadlock()); // avoid leaking recipients in encryption data - let chat = Chat::load_from_db(&bob, msg.chat_id).await?; - assert_eq!(chat.typ, Chattype::Single); - assert_eq!(chat.id, chat_bob.id); - assert!(!chat.is_self_talk()); + { + let msg = bob.recv_msg(&alice.pop_sent_msg().await).await; + assert_eq!(msg.get_text(), "ola!"); + assert_eq!(msg.subject, "Broadcast list"); + assert!(!msg.get_showpadlock()); // avoid leaking recipients in encryption data + let chat = Chat::load_from_db(&bob, msg.chat_id).await?; + assert_eq!(chat.typ, Chattype::Mailinglist); + assert_ne!(chat.id, chat_bob.id); + assert_eq!(chat.name, "Broadcast list"); + assert!(!chat.is_self_talk()); + } + + { + // Alice changes the name: + set_chat_name(&alice, broadcast_id, "My great broadcast").await?; + let sent = alice.send_text(broadcast_id, "I changed the title!").await; + + let msg = bob.recv_msg(&sent).await; + assert_eq!(msg.subject, "Re: My great broadcast"); + let bob_chat = Chat::load_from_db(&bob, msg.chat_id).await?; + assert_eq!(bob_chat.name, "My great broadcast"); + } Ok(()) } diff --git a/src/mimefactory.rs b/src/mimefactory.rs index 2b37977c1..b14e6a99b 100644 --- a/src/mimefactory.rs +++ b/src/mimefactory.rs @@ -415,7 +415,9 @@ impl<'a> MimeFactory<'a> { return Ok(self.msg.subject.clone()); } - if chat.typ == Chattype::Group && quoted_msg_subject.is_none_or_empty() { + if (chat.typ == Chattype::Group || chat.typ == Chattype::Broadcast) + && quoted_msg_subject.is_none_or_empty() + { let re = if self.in_reply_to.is_empty() { "" } else { @@ -424,15 +426,13 @@ impl<'a> MimeFactory<'a> { return Ok(format!("{}{}", re, chat.name)); } - if chat.typ != Chattype::Broadcast { - let parent_subject = if quoted_msg_subject.is_none_or_empty() { - chat.param.get(Param::LastSubject) - } else { - quoted_msg_subject.as_deref() - }; - if let Some(last_subject) = parent_subject { - return Ok(format!("Re: {}", remove_subject_prefix(last_subject))); - } + let parent_subject = if quoted_msg_subject.is_none_or_empty() { + chat.param.get(Param::LastSubject) + } else { + quoted_msg_subject.as_deref() + }; + if let Some(last_subject) = parent_subject { + return Ok(format!("Re: {}", remove_subject_prefix(last_subject))); } let self_name = &match context.get_config(Config::Displayname).await? { @@ -594,6 +594,15 @@ impl<'a> MimeFactory<'a> { )); } + if let Loaded::Message { chat } = &self.loaded { + if chat.typ == Chattype::Broadcast { + headers.protected.push(Header::new( + "List-ID".into(), + format!("{} <{}>", chat.name, chat.grpid), + )); + } + } + // Non-standard headers. headers .unprotected diff --git a/src/mimeparser.rs b/src/mimeparser.rs index 5855beea7..1bd0fb9a0 100644 --- a/src/mimeparser.rs +++ b/src/mimeparser.rs @@ -123,23 +123,6 @@ pub(crate) enum AvatarAction { Change(String), } -#[derive(Debug, PartialEq)] -pub(crate) enum MailinglistType { - /// The message belongs to a mailing list and has a `ListId:`-header - /// that should be used to get a unique id. - ListIdBased, - - /// The message belongs to a mailing list, but there is no `ListId:`-header; - /// `Sender:`-header should be used to get a unique id. - /// This method is used by implementations as Majordomo. - /// Note, that the `Sender:` header alone is not sufficient to detect these lists, - /// `get_mailinglist_type()` check additional conditions therefore. - SenderBased, - - /// The message does not belong to a mailing list. - None, -} - /// System message type. #[derive( Debug, Default, Display, Clone, Copy, PartialEq, Eq, FromPrimitive, ToPrimitive, ToSql, FromSql, @@ -1340,26 +1323,28 @@ impl MimeMessage { self.parts.push(part); } - pub(crate) fn get_mailinglist_type(&self) -> MailinglistType { - if self.get_header(HeaderDef::ListId).is_some() { - return MailinglistType::ListIdBased; - } else if self.get_header(HeaderDef::Sender).is_some() { + pub(crate) fn get_mailinglist_header(&self) -> Option<&str> { + if let Some(list_id) = self.get_header(HeaderDef::ListId) { + // The message belongs to a mailing list and has a `ListId:`-header + // that should be used to get a unique id. + return Some(list_id); + } else if let Some(sender) = self.get_header(HeaderDef::Sender) { // the `Sender:`-header alone is no indicator for mailing list // as also used for bot-impersonation via `set_override_sender_name()` if let Some(precedence) = self.get_header(HeaderDef::Precedence) { if precedence == "list" || precedence == "bulk" { - return MailinglistType::SenderBased; + // The message belongs to a mailing list, but there is no `ListId:`-header; + // `Sender:`-header is be used to get a unique id. + // This method is used by implementations as Majordomo. + return Some(sender); } } } - MailinglistType::None + None } pub(crate) fn is_mailinglist_message(&self) -> bool { - match self.get_mailinglist_type() { - MailinglistType::ListIdBased | MailinglistType::SenderBased => true, - MailinglistType::None => false, - } + self.get_mailinglist_header().is_some() } pub fn repl_msg_by_error(&mut self, error_msg: &str) { diff --git a/src/receive_imf.rs b/src/receive_imf.rs index bbba10564..58a094c91 100644 --- a/src/receive_imf.rs +++ b/src/receive_imf.rs @@ -28,9 +28,7 @@ use crate::log::LogExt; use crate::message::{ self, rfc724_mid_exists, Message, MessageState, MessengerMessage, MsgId, Viewtype, }; -use crate::mimeparser::{ - parse_message_ids, AvatarAction, MailinglistType, MimeMessage, SystemMessage, -}; +use crate::mimeparser::{parse_message_ids, AvatarAction, MimeMessage, SystemMessage}; use crate::param::{Param, Params}; use crate::peerstate::{Peerstate, PeerstateKeyType, PeerstateVerifiedStatus}; use crate::reaction::{set_msg_reaction, Reaction}; @@ -654,45 +652,23 @@ async fn add_parts( if chat_id.is_none() { // check if the message belongs to a mailing list - match mime_parser.get_mailinglist_type() { - MailinglistType::ListIdBased => { - if let Some(list_id) = mime_parser.get_header(HeaderDef::ListId) { - if let Some((new_chat_id, new_chat_id_blocked)) = - create_or_lookup_mailinglist( - context, - allow_creation, - list_id, - mime_parser, - ) - .await? - { - chat_id = Some(new_chat_id); - chat_id_blocked = new_chat_id_blocked; - } - } + if let Some(mailinglist_header) = mime_parser.get_mailinglist_header() { + if let Some((new_chat_id, new_chat_id_blocked)) = create_or_lookup_mailinglist( + context, + allow_creation, + mailinglist_header, + mime_parser, + ) + .await? + { + chat_id = Some(new_chat_id); + chat_id_blocked = new_chat_id_blocked; } - MailinglistType::SenderBased => { - if let Some(sender) = mime_parser.get_header(HeaderDef::Sender) { - if let Some((new_chat_id, new_chat_id_blocked)) = - create_or_lookup_mailinglist( - context, - allow_creation, - sender, - mime_parser, - ) - .await? - { - chat_id = Some(new_chat_id); - chat_id_blocked = new_chat_id_blocked; - } - } - } - MailinglistType::None => {} } } if let Some(chat_id) = chat_id { - apply_mailinglist_changes(context, mime_parser, chat_id).await?; + apply_mailinglist_changes(context, mime_parser, sent_timestamp, chat_id).await?; } // if contact renaming is prevented (for mailinglists and bots), @@ -1921,6 +1897,8 @@ async fn apply_group_changes( Ok(better_msg) } +static LIST_ID_REGEX: Lazy = Lazy::new(|| Regex::new(r"^(.+)<(.+)>$").unwrap()); + /// Create or lookup a mailing list chat. /// /// `list_id_header` contains the Id that must be used for the mailing list @@ -1937,23 +1915,71 @@ async fn create_or_lookup_mailinglist( list_id_header: &str, mime_parser: &MimeMessage, ) -> Result> { - static LIST_ID: Lazy = Lazy::new(|| Regex::new(r"^(.+)<(.+)>$").unwrap()); - let (mut name, listid) = match LIST_ID.captures(list_id_header) { - Some(cap) => (cap[1].trim().to_string(), cap[2].trim().to_string()), - None => ( - "".to_string(), - list_id_header - .trim() - .trim_start_matches('<') - .trim_end_matches('>') - .to_string(), - ), + let listid = match LIST_ID_REGEX.captures(list_id_header) { + Some(cap) => cap[2].trim().to_string(), + None => list_id_header + .trim() + .trim_start_matches('<') + .trim_end_matches('>') + .to_string(), }; if let Some((chat_id, _, blocked)) = chat::get_chat_id_by_grpid(context, &listid).await? { return Ok(Some((chat_id, blocked))); } + let name = compute_mailinglist_name(list_id_header, &listid, mime_parser); + + if allow_creation { + // list does not exist but should be created + let param = mime_parser.list_post.as_ref().map(|list_post| { + let mut p = Params::new(); + p.set(Param::ListPost, list_post); + p.to_string() + }); + + let is_bot = context.get_config_bool(Config::Bot).await?; + let blocked = if is_bot { + Blocked::Not + } else { + Blocked::Request + }; + let chat_id = ChatId::create_multiuser_record( + context, + Chattype::Mailinglist, + &listid, + &name, + blocked, + ProtectionStatus::Unprotected, + param, + ) + .await + .with_context(|| { + format!( + "failed to create mailinglist '{}' for grpid={}", + &name, &listid + ) + })?; + + chat::add_to_chat_contacts_table(context, chat_id, &[ContactId::SELF]).await?; + Ok(Some((chat_id, blocked))) + } else { + info!(context, "Creating list forbidden by caller."); + Ok(None) + } +} + +#[allow(clippy::indexing_slicing)] +fn compute_mailinglist_name( + list_id_header: &str, + listid: &str, + mime_parser: &MimeMessage, +) -> String { + let mut name = match LIST_ID_REGEX.captures(list_id_header) { + Some(cap) => cap[1].trim().to_string(), + None => "".to_string(), + }; + // for mailchimp lists, the name in `ListId` is just a long number. // a usable name for these lists is in the `From` header // and we can detect these lists by a unique `ListId`-suffix. @@ -1997,50 +2023,14 @@ async fn create_or_lookup_mailinglist( // 51231231231231231231231232869f58.xing.com -> xing.com static PREFIX_32_CHARS_HEX: Lazy = Lazy::new(|| Regex::new(r"([0-9a-fA-F]{32})\.(.{6,})").unwrap()); - if let Some(cap) = PREFIX_32_CHARS_HEX.captures(&listid) { + if let Some(cap) = PREFIX_32_CHARS_HEX.captures(listid) { name = cap[2].to_string(); } else { - name = listid.clone(); + name = listid.to_string(); } } - if allow_creation { - // list does not exist but should be created - let param = mime_parser.list_post.as_ref().map(|list_post| { - let mut p = Params::new(); - p.set(Param::ListPost, list_post); - p.to_string() - }); - - let is_bot = context.get_config_bool(Config::Bot).await?; - let blocked = if is_bot { - Blocked::Not - } else { - Blocked::Request - }; - let chat_id = ChatId::create_multiuser_record( - context, - Chattype::Mailinglist, - &listid, - &name, - blocked, - ProtectionStatus::Unprotected, - param, - ) - .await - .with_context(|| { - format!( - "failed to create mailinglist '{}' for grpid={}", - &name, &listid - ) - })?; - - chat::add_to_chat_contacts_table(context, chat_id, &[ContactId::SELF]).await?; - Ok(Some((chat_id, blocked))) - } else { - info!(context, "Creating list forbidden by caller."); - Ok(None) - } + strip_rtlo_characters(&name) } /// Set ListId param on the contact and ListPost param the chat. @@ -2049,9 +2039,10 @@ async fn create_or_lookup_mailinglist( async fn apply_mailinglist_changes( context: &Context, mime_parser: &MimeMessage, + sent_timestamp: i64, chat_id: ChatId, ) -> Result<()> { - let Some(list_post) = &mime_parser.list_post else { + let Some(mailinglist_header) = mime_parser.get_mailinglist_header() else { return Ok(()); }; @@ -2061,6 +2052,24 @@ async fn apply_mailinglist_changes( } let listid = &chat.grpid; + let new_name = compute_mailinglist_name(mailinglist_header, listid, mime_parser); + if chat.name != new_name + && chat_id + .update_timestamp(context, Param::GroupNameTimestamp, sent_timestamp) + .await? + { + info!(context, "Updating listname for chat {chat_id}."); + context + .sql + .execute("UPDATE chats SET name=? WHERE id=?;", (new_name, chat_id)) + .await?; + context.emit_event(EventType::ChatModified(chat_id)); + } + + let Some(list_post) = &mime_parser.list_post else { + return Ok(()); + }; + let list_post = match ContactAddress::new(list_post) { Ok(list_post) => list_post, Err(err) => { From df7c44ae425d0ef3cb34220181330d249515440a Mon Sep 17 00:00:00 2001 From: adbenitez Date: Tue, 17 Oct 2023 17:18:25 +0200 Subject: [PATCH 06/22] fix: wrong type hint --- deltachat-rpc-client/src/deltachat_rpc_client/client.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/deltachat-rpc-client/src/deltachat_rpc_client/client.py b/deltachat-rpc-client/src/deltachat_rpc_client/client.py index 04c30e5dc..e27c35a1f 100644 --- a/deltachat-rpc-client/src/deltachat_rpc_client/client.py +++ b/deltachat-rpc-client/src/deltachat_rpc_client/client.py @@ -3,7 +3,6 @@ import logging from typing import ( TYPE_CHECKING, Callable, - Coroutine, Dict, Iterable, Optional, @@ -92,7 +91,7 @@ class Client: """Process events forever.""" self.run_until(lambda _: False) - def run_until(self, func: Callable[[AttrDict], Union[bool, Coroutine]]) -> AttrDict: + def run_until(self, func: Callable[[AttrDict], bool]) -> AttrDict: """Process events until the given callable evaluates to True. The callable should accept an AttrDict object representing the From 955f4fbb195741255f43939a858587669577bb17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Asiel=20D=C3=ADaz=20Ben=C3=ADtez?= Date: Wed, 18 Oct 2023 12:31:32 -0400 Subject: [PATCH 07/22] add self-address to backup filename (#4820) close #4816 --------- Co-authored-by: B. Petersen --- deltachat-ffi/deltachat.h | 3 +-- src/imex.rs | 20 ++++++++++++-------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/deltachat-ffi/deltachat.h b/deltachat-ffi/deltachat.h index d021f1c5b..ea68725cb 100644 --- a/deltachat-ffi/deltachat.h +++ b/deltachat-ffi/deltachat.h @@ -2266,8 +2266,7 @@ dc_contact_t* dc_get_contact (dc_context_t* context, uint32_t co * the backup is not encrypted. * The backup contains all contacts, chats, images and other data and device independent settings. * The backup does not contain device dependent settings as ringtones or LED notification settings. - * The name of the backup is typically `delta-chat-.tar`, if more than one backup is create on a day, - * the format is `delta-chat--.tar` + * The name of the backup is `delta-chat-backup---.tar`. * * - **DC_IMEX_IMPORT_BACKUP** (12) - `param1` is the file (not: directory) to import. `param2` is the passphrase. * The file is normally created by DC_IMEX_EXPORT_BACKUP and detected by dc_imex_has_backup(). Importing a backup diff --git a/src/imex.rs b/src/imex.rs index 8698a9f0f..d137c8b20 100644 --- a/src/imex.rs +++ b/src/imex.rs @@ -58,8 +58,7 @@ pub enum ImexMode { /// Export a backup to the directory given as `path` with the given `passphrase`. /// The backup contains all contacts, chats, images and other data and device independent settings. /// The backup does not contain device dependent settings as ringtones or LED notification settings. - /// The name of the backup is typically `delta-chat-.tar`, if more than one backup is create on a day, - /// the format is `delta-chat--.tar` + /// The name of the backup is `delta-chat-backup---.tar`. ExportBackup = 11, /// `path` is the file (not: directory) to import. The file is normally @@ -128,7 +127,7 @@ pub async fn has_backup(_context: &Context, dir_name: &Path) -> Result { && (newest_backup_name.is_empty() || name > newest_backup_name) { // We just use string comparison to determine which backup is newer. - // This works fine because the filenames have the form ...delta-chat-backup-2020-07-24-00.tar + // This works fine because the filenames have the form `delta-chat-backup-2023-10-18-00-foo@example.com.tar` newest_backup_path = Some(path); newest_backup_name = name; } @@ -484,7 +483,11 @@ async fn import_backup( /// Returns Ok((temp_db_path, temp_path, dest_path)) on success. Unencrypted database can be /// written to temp_db_path. The backup can then be written to temp_path. If the backup succeeded, /// it can be renamed to dest_path. This guarantees that the backup is complete. -fn get_next_backup_path(folder: &Path, backup_time: i64) -> Result<(PathBuf, PathBuf, PathBuf)> { +fn get_next_backup_path( + folder: &Path, + addr: &str, + backup_time: i64, +) -> Result<(PathBuf, PathBuf, PathBuf)> { let folder = PathBuf::from(folder); let stem = chrono::NaiveDateTime::from_timestamp_opt(backup_time, 0) .context("can't get next backup path")? @@ -495,13 +498,13 @@ fn get_next_backup_path(folder: &Path, backup_time: i64) -> Result<(PathBuf, Pat // 64 backup files per day should be enough for everyone for i in 0..64 { let mut tempdbfile = folder.clone(); - tempdbfile.push(format!("{stem}-{i:02}.db")); + tempdbfile.push(format!("{stem}-{i:02}-{addr}.db")); let mut tempfile = folder.clone(); - tempfile.push(format!("{stem}-{i:02}.tar.part")); + tempfile.push(format!("{stem}-{i:02}-{addr}.tar.part")); let mut destfile = folder.clone(); - destfile.push(format!("{stem}-{i:02}.tar")); + destfile.push(format!("{stem}-{i:02}-{addr}.tar")); if !tempdbfile.exists() && !tempfile.exists() && !destfile.exists() { return Ok((tempdbfile, tempfile, destfile)); @@ -516,7 +519,8 @@ fn get_next_backup_path(folder: &Path, backup_time: i64) -> Result<(PathBuf, Pat async fn export_backup(context: &Context, dir: &Path, passphrase: String) -> Result<()> { // get a fine backup file name (the name includes the date so that multiple backup instances are possible) let now = time(); - let (temp_db_path, temp_path, dest_path) = get_next_backup_path(dir, now)?; + let self_addr = context.get_primary_self_addr().await?; + let (temp_db_path, temp_path, dest_path) = get_next_backup_path(dir, &self_addr, now)?; let _d1 = DeleteOnDrop(temp_db_path.clone()); let _d2 = DeleteOnDrop(temp_path.clone()); From c6358169ad6c5d6e482f8f43a5cc2bd724ee7916 Mon Sep 17 00:00:00 2001 From: link2xt Date: Tue, 17 Oct 2023 16:54:58 +0000 Subject: [PATCH 08/22] fix: s/env/venv/ in scripts/make-python-testenv.sh --- scripts/make-python-testenv.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/make-python-testenv.sh b/scripts/make-python-testenv.sh index 64f959a68..ce3d0b71e 100755 --- a/scripts/make-python-testenv.sh +++ b/scripts/make-python-testenv.sh @@ -14,4 +14,4 @@ export DCC_RS_DEV="$PWD" cargo build -p deltachat_ffi --features jsonrpc tox -c python -e py --devenv venv -env/bin/pip install --upgrade pip +venv/bin/pip install --upgrade pip From 21e0bb28ad83d09fbaec580017fca78b25054976 Mon Sep 17 00:00:00 2001 From: link2xt Date: Tue, 17 Oct 2023 07:35:58 +0000 Subject: [PATCH 09/22] build: create source distribution for deltachat-rpc-server --- scripts/wheel-rpc-server.py | 99 +++++++++++++++++++++++++++++++++++-- 1 file changed, 94 insertions(+), 5 deletions(-) diff --git a/scripts/wheel-rpc-server.py b/scripts/wheel-rpc-server.py index 8809852dd..7c2413687 100755 --- a/scripts/wheel-rpc-server.py +++ b/scripts/wheel-rpc-server.py @@ -4,6 +4,98 @@ Run scripts/zig-rpc-server.sh first.""" from pathlib import Path from wheel.wheelfile import WheelFile import tomllib +import tarfile +from io import BytesIO + + +def metadata_contents(version): + return f"""Metadata-Version: 2.1 +Name: deltachat-rpc-server +Version: {version} +Summary: Delta Chat JSON-RPC server +""" + + +SETUP_PY = """ +import sys +from setuptools import setup, find_packages +from distutils.cmd import Command +from setuptools.command.install import install +from setuptools.command.build import build +import subprocess +import platform +import tempfile +from zipfile import ZipFile +from pathlib import Path +import shutil + + +class BuildCommand(build): + def run(self): + tmpdir = tempfile.mkdtemp() + subprocess.run( + [ + sys.executable, + "-m", + "pip", + "download", + "--no-input", + "--timeout", + "1000", + "--platform", + "musllinux_1_1_" + platform.machine(), + "--only-binary=:all:", + "deltachat-rpc-server", + ], + cwd=tmpdir, + ) + + wheel_path = next(Path(tmpdir).glob("*.whl")) + with ZipFile(wheel_path, "r") as wheel: + exe_path = wheel.extract("deltachat_rpc_server/deltachat-rpc-server", "src") + Path(exe_path).chmod(0o700) + wheel.extract("deltachat_rpc_server/__init__.py", "src") + + shutil.rmtree(tmpdir) + return super().run() + + +setup( + cmdclass={"build": BuildCommand}, + package_data={"deltachat_rpc_server": ["deltachat-rpc-server"]}, +) +""" + + +def build_source_package(version): + filename = f"dist/deltachat-rpc-server-{version}.tar.gz" + + with tarfile.open(filename, "w:gz") as pkg: + + def pack(name, contents): + contents = contents.encode() + tar_info = tarfile.TarInfo(f"deltachat-rpc-server-{version}/{name}") + tar_info.mode = 0o644 + tar_info.size = len(contents) + pkg.addfile(tar_info, BytesIO(contents)) + + pack("PKG-INFO", metadata_contents(version)) + pack( + "pyproject.toml", + """[build-system] +requires = ["setuptools==68.2.2", "pip"] +build-backend = "setuptools.build_meta" + +[project] +name = "deltachat-rpc-server" +version = "1.125.0" + +[project.scripts] +deltachat-rpc-server = "deltachat_rpc_server:main" +""", + ) + pack("setup.py", SETUP_PY) + pack("src/deltachat_rpc_server/__init__.py", "") def build_wheel(version, binary, tag): @@ -27,11 +119,7 @@ def main(): ) wheel.writestr( f"deltachat_rpc_server-{version}.dist-info/METADATA", - f"""Metadata-Version: 2.1 -Name: deltachat-rpc-server -Version: {version} -Summary: Delta Chat JSON-RPC server -""", + metadata_contents(version), ) wheel.writestr( f"deltachat_rpc_server-{version}.dist-info/WHEEL", @@ -48,6 +136,7 @@ def main(): cargo_toml = tomllib.load(f) version = cargo_toml["package"]["version"] Path("dist").mkdir(exist_ok=True) + build_source_package(version) build_wheel( version, "target/x86_64-unknown-linux-musl/release/deltachat-rpc-server", From 64035d3ecb93083c7b0baca664bbb4bc59cfcbe2 Mon Sep 17 00:00:00 2001 From: link2xt Date: Fri, 20 Oct 2023 02:11:21 +0000 Subject: [PATCH 10/22] fix: set soft_heap_limit on SQLite database This should prevent unlimited growth of memory usage by SQLite for is page cache. --- src/sql.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sql.rs b/src/sql.rs index ceb38067e..521280f62 100644 --- a/src/sql.rs +++ b/src/sql.rs @@ -686,6 +686,7 @@ fn new_connection(path: &Path, passphrase: &str) -> Result { PRAGMA secure_delete=on; PRAGMA busy_timeout = 0; -- fail immediately PRAGMA temp_store=memory; -- Avoid SQLITE_IOERR_GETTEMPPATH errors on Android + PRAGMA soft_heap_limit = 8388608; -- 8 MiB limit, same as set in Android SQLiteDatabase. PRAGMA foreign_keys=on; ", )?; From d05afec28986cdb082f82631a83bd978965c85bc Mon Sep 17 00:00:00 2001 From: link2xt Date: Fri, 20 Oct 2023 19:46:52 +0000 Subject: [PATCH 11/22] chore(cargo): update async-imap to fix STATUS command --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b553f3d22..0d388b721 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -221,9 +221,9 @@ dependencies = [ [[package]] name = "async-imap" -version = "0.9.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b538b767cbf9c162a6c5795d4b932bd2c20ba10b5a91a94d2b2b6886c1dce6a8" +checksum = "936c1b580be4373b48c9c687e0c79285441664398354df28d0860087cac0c069" dependencies = [ "async-channel", "base64 0.21.2", From 0227bbc3059ebc35b90afdeae65c284e67d6664d Mon Sep 17 00:00:00 2001 From: link2xt Date: Fri, 20 Oct 2023 13:10:15 +0000 Subject: [PATCH 12/22] fix(imap): fallback to STATUS if SELECT did not return UIDNEXT Winmail Pro Mail Server 5.1.0616 does not return UIDNEXT in response to SELECT, but returns it when explicitly requested via STATUS command: ? SELECT INBOX * FLAGS (\Draft \Answered \Flagged \Deleted \Seen \Recent) * OK [PERMANENTFLAGS (\Draft \Answered \Flagged \Deleted \Seen)] Limited * 2 EXISTS * 0 RECENT * OK [UIDVALIDITY 1697802109] Ok ? OK [READ-WRITE] Ok SELECT completed ? STATUS INBOX (UIDNEXT) * STATUS "INBOX" (UIDNEXT 4) ? OK STATUS completed Previously used FETCH method is reported to fail for some users, the FETCH command sometimes returns no results. Besides, there is no guarantee that the message with the highest sequence number has the highest UID. In the worst case if STATUS does not return UIDNEXT in response to explicit request, we fall back to setting UIDNEXT to 1 instead of returning an error. --- src/imap.rs | 55 +++++++++++++++++++++++++---------------------------- 1 file changed, 26 insertions(+), 29 deletions(-) diff --git a/src/imap.rs b/src/imap.rs index 8f009f8ba..970273a5f 100644 --- a/src/imap.rs +++ b/src/imap.rs @@ -82,7 +82,6 @@ const RFC724MID_UID: &str = "(UID BODY.PEEK[HEADER.FIELDS (\ MESSAGE-ID \ X-MICROSOFT-ORIGINAL-MESSAGE-ID\ )])"; -const JUST_UID: &str = "(UID)"; const BODY_FULL: &str = "(FLAGS BODY.PEEK[])"; const BODY_PARTIAL: &str = "(FLAGS RFC822.SIZE BODY.PEEK[HEADER])"; @@ -627,18 +626,6 @@ impl Imap { // UIDVALIDITY is modified, reset highest seen MODSEQ. set_modseq(context, folder, 0).await?; - if mailbox.exists == 0 { - info!(context, "Folder {folder:?} is empty."); - - // set uid_next=1 for empty folders. - // If we do not do this here, we'll miss the first message - // as we will get in here again and fetch from uid_next then. - // Also, the "fall back to fetching" below would need a non-zero mailbox.exists to work. - set_uid_next(context, folder, 1).await?; - set_uidvalidity(context, folder, new_uid_validity).await?; - return Ok(false); - } - // ============== uid_validity has changed or is being set the first time. ============== let new_uid_next = match mailbox.uid_next { @@ -646,25 +633,35 @@ impl Imap { None => { warn!( context, - "IMAP folder {folder:?} has no uid_next, fall back to fetching." + "SELECT response for IMAP folder {folder:?} has no UIDNEXT, fall back to STATUS command." ); - // note that we use fetch by sequence number - // and thus we only need to get exactly the - // last-index message. - let set = format!("{}", mailbox.exists); - let mut list = session - .inner - .fetch(set, JUST_UID) - .await - .context("Error fetching UID")?; - let mut new_last_seen_uid = None; - while let Some(fetch) = list.try_next().await? { - if fetch.message == mailbox.exists && fetch.uid.is_some() { - new_last_seen_uid = fetch.uid; - } + // RFC 3501 says STATUS command SHOULD NOT be used + // on the currently seleced mailbox because the same + // information can be obtained by other means, + // such as reading SELECT response. + // + // However, it also says that UIDNEXT is REQUIRED + // in the SELECT response and if we are here, + // it is actually not returned. + // + // In particular, Winmail Pro Mail Server 5.1.0616 + // never returns UIDNEXT in SELECT response, + // but responds to "SELECT INBOX (UIDNEXT)" command. + let status = session + .inner + .status(folder, "(UIDNEXT)") + .await + .context("STATUS (UIDNEXT) error for {folder:?}")?; + + if let Some(uid_next) = status.uid_next { + uid_next + } else { + warn!(context, "STATUS {folder} (UIDNEXT) did not return UIDNEXT"); + + // Set UIDNEXT to 1 as a last resort fallback. + 1 } - new_last_seen_uid.context("select: failed to fetch")? + 1 } }; From defcd5764b72a25353af142ca454c56baca6019d Mon Sep 17 00:00:00 2001 From: link2xt Date: Sun, 22 Oct 2023 06:53:26 +0000 Subject: [PATCH 13/22] chore: spellcheck --- deltachat-ffi/deltachat.h | 2 +- deltachat-jsonrpc/typescript/test/online.ts | 2 +- draft/aeap-mvp.md | 2 +- node/lib/context.ts | 2 +- spec.md | 2 +- src/chat.rs | 2 +- src/context.rs | 2 +- src/imap.rs | 2 +- src/mimefactory.rs | 2 +- src/mimeparser.rs | 2 +- src/peerstate.rs | 2 +- src/stock_str.rs | 2 +- src/tools.rs | 4 ++-- 13 files changed, 14 insertions(+), 14 deletions(-) diff --git a/deltachat-ffi/deltachat.h b/deltachat-ffi/deltachat.h index ea68725cb..c330f5510 100644 --- a/deltachat-ffi/deltachat.h +++ b/deltachat-ffi/deltachat.h @@ -3950,7 +3950,7 @@ int64_t dc_msg_get_received_timestamp (const dc_msg_t* msg); * Get the message time used for sorting. * This function returns the timestamp that is used for sorting the message * into lists as returned e.g. by dc_get_chat_msgs(). - * This may be the reveived time, the sending time or another time. + * This may be the received time, the sending time or another time. * * To get the receiving time, use dc_msg_get_received_timestamp(). * To get the sending time, use dc_msg_get_timestamp(). diff --git a/deltachat-jsonrpc/typescript/test/online.ts b/deltachat-jsonrpc/typescript/test/online.ts index 3ab6cb820..c9fa4077a 100644 --- a/deltachat-jsonrpc/typescript/test/online.ts +++ b/deltachat-jsonrpc/typescript/test/online.ts @@ -148,7 +148,7 @@ describe("online tests", function () { waitForEvent(dc, "IncomingMsg", accountId1), ]); dc.rpc.miscSendTextMessage(accountId2, chatId, "super secret message"); - // Check if answer arives at A and if it is encrypted + // Check if answer arrives at A and if it is encrypted await eventPromise2; const messageId = ( diff --git a/draft/aeap-mvp.md b/draft/aeap-mvp.md index 94ac3c03e..040125f57 100644 --- a/draft/aeap-mvp.md +++ b/draft/aeap-mvp.md @@ -108,7 +108,7 @@ The most obvious alternative would be to create a new contact with the new addre #### Upsides: - With this approach, it's easier to switch to a model where the info about the transition is encoded in the PGP key. Since the key is gossiped, the information about the transition will spread virally. - (Also, less important: Slightly faster transition: If you send a message to e.g. "Delta Chat Dev", all members of the "sub-group" "delta android" will know of your transition.) -- It's easier to implement (if too many problems turn up, we can still switch to another approach and didn't wast that much development time.) +- It's easier to implement (if too many problems turn up, we can still switch to another approach and didn't waste that much development time.) [full messages](https://github.com/deltachat/deltachat-core-rust/pull/2896#discussion_r852002161) diff --git a/node/lib/context.ts b/node/lib/context.ts index 4c926ba78..a9cf9c687 100644 --- a/node/lib/context.ts +++ b/node/lib/context.ts @@ -36,7 +36,7 @@ export class Context extends EventEmitter { } } - /** Opens a stanalone context (without an account manager) + /** Opens a standalone context (without an account manager) * automatically starts the event handler */ static open(cwd: string): Context { const dbFile = join(cwd, 'db.sqlite') diff --git a/spec.md b/spec.md index b12a75464..ea34862aa 100644 --- a/spec.md +++ b/spec.md @@ -43,7 +43,7 @@ the `Subject` header SHOULD be `Message from `. Replies to messages MAY follow the typical `Re:`-format. The body MAY contain text which MUST have the content type `text/plain` -or `mulipart/alternative` containing `text/plain`. +or `multipart/alternative` containing `text/plain`. The text MAY be divided into a user-text-part and a footer-part using the line `-- ` (minus, minus, space, lineend). diff --git a/src/chat.rs b/src/chat.rs index 5f2580cbf..378a19ba4 100644 --- a/src/chat.rs +++ b/src/chat.rs @@ -2337,7 +2337,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 { - // protect all system messages againts RTLO attacks + // protect all system messages against RTLO attacks if msg.is_system_message() { msg.text = strip_rtlo_characters(&msg.text); } diff --git a/src/context.rs b/src/context.rs index 210eddc0d..22dc265c7 100644 --- a/src/context.rs +++ b/src/context.rs @@ -38,7 +38,7 @@ use crate::tools::{duration_to_str, time}; /// /// # Examples /// -/// Creating a new unecrypted database: +/// Creating a new unencrypted database: /// /// ``` /// # let rt = tokio::runtime::Runtime::new().unwrap(); diff --git a/src/imap.rs b/src/imap.rs index 970273a5f..6c41244aa 100644 --- a/src/imap.rs +++ b/src/imap.rs @@ -637,7 +637,7 @@ impl Imap { ); // RFC 3501 says STATUS command SHOULD NOT be used - // on the currently seleced mailbox because the same + // on the currently selected mailbox because the same // information can be obtained by other means, // such as reading SELECT response. // diff --git a/src/mimefactory.rs b/src/mimefactory.rs index b14e6a99b..1a016f00a 100644 --- a/src/mimefactory.rs +++ b/src/mimefactory.rs @@ -2341,7 +2341,7 @@ mod tests { // Now Bob can send an encrypted message to Alice. let mut msg = Message::new(Viewtype::File); // Long messages are truncated and MimeMessage::decoded_data is set for them. We need - // decoded_data to check presense of the necessary headers. + // decoded_data to check presence of the necessary headers. msg.set_text("a".repeat(constants::DC_DESIRED_TEXT_LEN + 1)); msg.set_file_from_bytes(&bob, "foo.bar", "content".as_bytes(), None) .await?; diff --git a/src/mimeparser.rs b/src/mimeparser.rs index 1bd0fb9a0..d1cfd5fa2 100644 --- a/src/mimeparser.rs +++ b/src/mimeparser.rs @@ -1878,7 +1878,7 @@ fn get_mime_type( } else { // Enacapsulated messages, see // Also used as part "message/disposition-notification" of "multipart/report", which, however, will - // be handled separatedly. + // be handled separately. // I've not seen any messages using this, so we do not attach these parts (maybe they're used to attach replies, // which are unwanted at all). // For now, we skip these parts at all; if desired, we could return DcMimeType::File/DC_MSG_File diff --git a/src/peerstate.rs b/src/peerstate.rs index 8a08a175d..e41752541 100644 --- a/src/peerstate.rs +++ b/src/peerstate.rs @@ -21,7 +21,7 @@ use crate::stock_str; /// Type of the public key stored inside the peerstate. #[derive(Debug)] pub enum PeerstateKeyType { - /// Pubilc key sent in the `Autocrypt-Gossip` header. + /// Public key sent in the `Autocrypt-Gossip` header. GossipKey, /// Public key sent in the `Autocrypt` header. diff --git a/src/stock_str.rs b/src/stock_str.rs index ea19a2a72..0dc305107 100644 --- a/src/stock_str.rs +++ b/src/stock_str.rs @@ -1550,7 +1550,7 @@ mod tests { let chats = Chatlist::try_load(&t, 0, None, None).await.unwrap(); assert_eq!(chats.len(), 0); - // a subsequent call to update_device_chats() must not re-add manally deleted messages or chats + // a subsequent call to update_device_chats() must not re-add manually deleted messages or chats t.update_device_chats().await.unwrap(); let chats = Chatlist::try_load(&t, 0, None, None).await.unwrap(); assert_eq!(chats.len(), 0); diff --git a/src/tools.rs b/src/tools.rs index 0e3ea3076..338570f7a 100644 --- a/src/tools.rs +++ b/src/tools.rs @@ -268,7 +268,7 @@ pub(crate) fn create_id() -> String { /// Function generates a Message-ID that can be used for a new outgoing message. /// - this function is called for all outgoing messages. /// - the message ID should be globally unique -/// - do not add a counter or any private data as this leaks information unncessarily +/// - do not add a counter or any private data as this leaks information unnecessarily pub(crate) fn create_outgoing_rfc724_mid(grpid: Option<&str>, from_addr: &str) -> String { let hostname = from_addr .find('@') @@ -700,7 +700,7 @@ pub(crate) fn buf_decompress(buf: &[u8]) -> Result> { } const RTLO_CHARACTERS: [char; 5] = ['\u{202A}', '\u{202B}', '\u{202C}', '\u{202D}', '\u{202E}']; -/// This method strips all occurances of the RTLO Unicode character. +/// This method strips all occurrences of the RTLO Unicode character. /// [Why is this needed](https://github.com/deltachat/deltachat-core-rust/issues/3479)? pub(crate) fn strip_rtlo_characters(input_str: &str) -> String { input_str.replace(|char| RTLO_CHARACTERS.contains(&char), "") From 680d024b05b34283462e499cb1ad5014ee532cb7 Mon Sep 17 00:00:00 2001 From: link2xt Date: Sun, 22 Oct 2023 06:53:44 +0000 Subject: [PATCH 14/22] docs: document scripts/codespell.sh --- scripts/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/README.md b/scripts/README.md index 18dc3bd0c..551d7e77a 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -10,6 +10,8 @@ and an own build machine. - `deny.sh` runs `cargo deny` for all Rust code in the project. +- `codespell.sh` spellchecks the source code using `codespell` tool. + - `../.github/workflows` contains jobs run by GitHub Actions. - `remote_tests_python.sh` rsyncs to a build machine and runs From 58330fe8b2243bbe92856e4b521ee680bdc94975 Mon Sep 17 00:00:00 2001 From: link2xt Date: Sun, 22 Oct 2023 11:54:55 +0000 Subject: [PATCH 15/22] ci: remove musl check It requires installing Zig and slows down CI. This was not broken for a while and we can do more frequent core releases to catch problems. --- .github/workflows/ci.yml | 4 ---- scripts/zig-musl-check.sh | 28 ---------------------------- 2 files changed, 32 deletions(-) delete mode 100755 scripts/zig-musl-check.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 54e4e81ad..5faa447dc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,10 +39,6 @@ jobs: - name: Check run: cargo check --workspace --all-targets --all-features - # Check with musl libc target which is used for `deltachat-rpc-server` releases. - - name: Check musl - run: scripts/zig-musl-check.sh - cargo_deny: name: cargo deny runs-on: ubuntu-latest diff --git a/scripts/zig-musl-check.sh b/scripts/zig-musl-check.sh deleted file mode 100755 index 89c9b5bf6..000000000 --- a/scripts/zig-musl-check.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/sh -# -# Run `cargo check` with musl libc. -# This requires `zig` to compile vendored openssl. - -set -x -set -e - -unset RUSTFLAGS - -# Pin Rust version to avoid uncontrolled changes in the compiler and linker flags. -export RUSTUP_TOOLCHAIN=1.72.0 - -ZIG_VERSION=0.11.0 - -# Download Zig -rm -fr "$ZIG_VERSION" "zig-linux-x86_64-$ZIG_VERSION.tar.xz" -wget "https://ziglang.org/builds/zig-linux-x86_64-$ZIG_VERSION.tar.xz" -tar xf "zig-linux-x86_64-$ZIG_VERSION.tar.xz" -export PATH="$PWD/zig-linux-x86_64-$ZIG_VERSION:$PATH" - -rustup target add x86_64-unknown-linux-musl -CC="$PWD/scripts/zig-cc" \ -TARGET_CC="$PWD/scripts/zig-cc" \ -CARGO_TARGET_X86_64_UNKNOWN_LINUX_MUSL_LINKER="$PWD/scripts/zig-cc" \ -LD="$PWD/scripts/zig-cc" \ -ZIG_TARGET="x86_64-linux-musl" \ -cargo check --release --target x86_64-unknown-linux-musl -p deltachat_ffi --features jsonrpc From c13bbd05cd932f465c2fbcbbf7119e963eb425a3 Mon Sep 17 00:00:00 2001 From: link2xt Date: Sun, 22 Oct 2023 12:22:40 +0000 Subject: [PATCH 16/22] chore: add dist/ to .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 1f574be4a..aed04dd95 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ /target **/*.rs.bk /build +/dist # ignore vi temporaries *~ From b4fe9e3eece6ee4acb7158f292fed450906e8ed4 Mon Sep 17 00:00:00 2001 From: link2xt Date: Sun, 22 Oct 2023 08:46:37 +0000 Subject: [PATCH 17/22] build: use Zig via `ziglang` PyPI package This avoids cluttering workdir with unpacked tarball and will work automatically once PEP-723 is implemented. --- scripts/zig-cc | 20 ++++++++++++++++++-- scripts/zig-rpc-server.sh | 8 -------- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/scripts/zig-cc b/scripts/zig-cc index b6d9ebc68..ffa3278b4 100755 --- a/scripts/zig-cc +++ b/scripts/zig-cc @@ -1,7 +1,13 @@ #!/usr/bin/env python +# /// pyproject +# [run] +# dependencies = [ +# "ziglang==0.11.0" +# ] +# /// +import os import subprocess import sys -import os def flag_filter(flag: str) -> bool: @@ -34,7 +40,17 @@ def main(): args += ["-DBROKEN_CLANG_ATOMICS"] subprocess.run( - ["zig", "cc", "-target", zig_target, *zig_cpu_args, *args], check=True + [ + sys.executable, + "-m", + "ziglang", + "cc", + "-target", + zig_target, + *zig_cpu_args, + *args, + ], + check=True, ) diff --git a/scripts/zig-rpc-server.sh b/scripts/zig-rpc-server.sh index 1f35b3fd1..6d08f339f 100755 --- a/scripts/zig-rpc-server.sh +++ b/scripts/zig-rpc-server.sh @@ -10,14 +10,6 @@ unset RUSTFLAGS # Pin Rust version to avoid uncontrolled changes in the compiler and linker flags. export RUSTUP_TOOLCHAIN=1.72.0 -ZIG_VERSION=0.11.0 - -# Download Zig -rm -fr "$ZIG_VERSION" "zig-linux-x86_64-$ZIG_VERSION.tar.xz" -wget "https://ziglang.org/builds/zig-linux-x86_64-$ZIG_VERSION.tar.xz" -tar xf "zig-linux-x86_64-$ZIG_VERSION.tar.xz" -export PATH="$PWD/zig-linux-x86_64-$ZIG_VERSION:$PATH" - rustup target add i686-unknown-linux-musl CC="$PWD/scripts/zig-cc" \ TARGET_CC="$PWD/scripts/zig-cc" \ From 418cd24979716e5ade3a3a696bcb39fe3b9c2198 Mon Sep 17 00:00:00 2001 From: link2xt Date: Sun, 22 Oct 2023 11:02:01 +0000 Subject: [PATCH 18/22] build: strip release binaries This significantly reduces binary size for deltachat-rpc-server, from 42 MiB to 15 MiB in case of aarch64. --- Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.toml b/Cargo.toml index f5e389e9e..7cf3db408 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,7 @@ lto = true panic = 'abort' opt-level = "z" codegen-units = 1 +strip = true [patch.crates-io] quinn-udp = { git = "https://github.com/quinn-rs/quinn", branch="main" } From 088eda29837e122d8e0b9b3c181054922ddff647 Mon Sep 17 00:00:00 2001 From: link2xt Date: Sun, 22 Oct 2023 11:03:20 +0000 Subject: [PATCH 19/22] build(scripts/zig-rpc-server.sh): move built binaries to dist/ --- scripts/wheel-rpc-server.py | 8 ++++---- scripts/zig-rpc-server.sh | 6 ++++++ 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/scripts/wheel-rpc-server.py b/scripts/wheel-rpc-server.py index 7c2413687..9dc481847 100755 --- a/scripts/wheel-rpc-server.py +++ b/scripts/wheel-rpc-server.py @@ -139,22 +139,22 @@ def main(): build_source_package(version) build_wheel( version, - "target/x86_64-unknown-linux-musl/release/deltachat-rpc-server", + "dist/deltachat-rpc-server-x86_64-linux", "py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.musllinux_1_1_x86_64", ) build_wheel( version, - "target/armv7-unknown-linux-musleabihf/release/deltachat-rpc-server", + "dist/deltachat-rpc-server-armv7-linux", "py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.musllinux_1_1_armv7l", ) build_wheel( version, - "target/aarch64-unknown-linux-musl/release/deltachat-rpc-server", + "dist/deltachat-rpc-server-aarch64-linux", "py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64", ) build_wheel( version, - "target/i686-unknown-linux-musl/release/deltachat-rpc-server", + "dist/deltachat-rpc-server-i686-linux", "py3-none-manylinux_2_12_i686.manylinux2010_i686.musllinux_1_1_i686", ) diff --git a/scripts/zig-rpc-server.sh b/scripts/zig-rpc-server.sh index 6d08f339f..455c0f150 100755 --- a/scripts/zig-rpc-server.sh +++ b/scripts/zig-rpc-server.sh @@ -42,3 +42,9 @@ CARGO_TARGET_AARCH64_UNKNOWN_LINUX_MUSL_LINKER="$PWD/scripts/zig-cc" \ LD="$PWD/scripts/zig-cc" \ ZIG_TARGET="aarch64-linux-musl" \ cargo build --release --target aarch64-unknown-linux-musl -p deltachat-rpc-server --features vendored + +mkdir -p dist +cp target/x86_64-unknown-linux-musl/release/deltachat-rpc-server dist/deltachat-rpc-server-x86_64-linux +cp target/i686-unknown-linux-musl/release/deltachat-rpc-server dist/deltachat-rpc-server-i686-linux +cp target/aarch64-unknown-linux-musl/release/deltachat-rpc-server dist/deltachat-rpc-server-aarch64-linux +cp target/armv7-unknown-linux-musleabihf/release/deltachat-rpc-server dist/deltachat-rpc-server-armv7-linux From e878caebe3c1ac8852f0fcef4b59ded40403cc16 Mon Sep 17 00:00:00 2001 From: link2xt Date: Sat, 21 Oct 2023 04:27:01 +0000 Subject: [PATCH 20/22] ci: build Python wheels for deltachat-rpc-server --- .github/workflows/deltachat-rpc-server.yml | 69 ++++++++++++---------- 1 file changed, 37 insertions(+), 32 deletions(-) diff --git a/.github/workflows/deltachat-rpc-server.yml b/.github/workflows/deltachat-rpc-server.yml index 59530701c..356534fe3 100644 --- a/.github/workflows/deltachat-rpc-server.yml +++ b/.github/workflows/deltachat-rpc-server.yml @@ -26,35 +26,26 @@ jobs: steps: - uses: actions/checkout@v3 - - name: Build + # Python 3.11 is needed for tomllib used in scripts/wheel-rpc-server.py + - name: Install python 3.12 + uses: actions/setup-python@v4 + with: + python-version: 3.12 + + - name: Install ziglang and wheel + run: pip install wheel ziglang==0.11.0 + + - name: Build deltachat-rpc-server binaries run: sh scripts/zig-rpc-server.sh - - name: Upload x86_64 binary - uses: actions/upload-artifact@v3 - with: - name: deltachat-rpc-server-x86_64 - path: target/x86_64-unknown-linux-musl/release/deltachat-rpc-server - if-no-files-found: error + - name: Build deltachat-rpc-server Python wheels and source package + run: scripts/wheel-rpc-server.py - - name: Upload i686 binary + - name: Upload dist directory uses: actions/upload-artifact@v3 with: - name: deltachat-rpc-server-i686 - path: target/i686-unknown-linux-musl/release/deltachat-rpc-server - if-no-files-found: error - - - name: Upload aarch64 binary - uses: actions/upload-artifact@v3 - with: - name: deltachat-rpc-server-aarch64 - path: target/aarch64-unknown-linux-musl/release/deltachat-rpc-server - if-no-files-found: error - - - name: Upload armv7 binary - uses: actions/upload-artifact@v3 - with: - name: deltachat-rpc-server-armv7 - path: target/armv7-unknown-linux-musleabihf/release/deltachat-rpc-server + name: dist + path: dist/ if-no-files-found: error build_windows: @@ -116,15 +107,29 @@ jobs: contents: write runs-on: "ubuntu-latest" steps: - - name: Download built binaries - uses: "actions/download-artifact@v3" + - name: Download Linux binaries + uses: actions/download-artifact@v3 + with: + name: dist + path: dist/ - - name: Compose dist/ directory - run: | - mkdir dist - for x in x86_64 i686 aarch64 armv7 win32.exe win64.exe x86_64-macos; do - mv "deltachat-rpc-server-$x"/* "dist/deltachat-rpc-server-$x" - done + - name: Download win32 binary + uses: actions/download-artifact@v3 + with: + name: deltachat-rpc-server-win32.exe + path: dist/deltachat-rpc-server-win32.exe + + - name: Download win64 binary + uses: actions/download-artifact@v3 + with: + name: deltachat-rpc-server-win64.exe + path: dist/deltachat-rpc-server-win32.exe + + - name: Download macOS binary + uses: actions/download-artifact@v3 + with: + name: deltachat-rpc-server-x86_64-macos + path: dist/deltachat-rpc-server-x86_64-macos - name: List downloaded artifacts run: ls -l dist/ From 934ca6a7d72443d230c2ccf3fb92813d047427a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Asiel=20D=C3=ADaz=20Ben=C3=ADtez?= Date: Sun, 22 Oct 2023 09:14:08 -0400 Subject: [PATCH 21/22] api: add send_draft() to JSON-RPC API (#4839) --- deltachat-jsonrpc/src/api/mod.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/deltachat-jsonrpc/src/api/mod.rs b/deltachat-jsonrpc/src/api/mod.rs index bcdc642f0..580634cf5 100644 --- a/deltachat-jsonrpc/src/api/mod.rs +++ b/deltachat-jsonrpc/src/api/mod.rs @@ -2053,6 +2053,23 @@ impl CommandApi { ChatId::new(chat_id).set_draft(&ctx, Some(&mut draft)).await } + + // send the chat's current set draft + async fn misc_send_draft(&self, account_id: u32, chat_id: u32) -> Result { + let ctx = self.get_context(account_id).await?; + if let Some(draft) = ChatId::new(chat_id).get_draft(&ctx).await? { + let mut draft = draft; + let msg_id = chat::send_msg(&ctx, ChatId::new(chat_id), &mut draft) + .await? + .to_u32(); + Ok(msg_id) + } else { + Err(anyhow!( + "chat with id {} doesn't have draft message", + chat_id + )) + } + } } // Helper functions (to prevent code duplication) From 4e08bb7b058dc8d3464b3a213933a71a67451b8c Mon Sep 17 00:00:00 2001 From: link2xt Date: Sun, 22 Oct 2023 13:24:07 +0000 Subject: [PATCH 22/22] chore(release): prepare for 1.126.0 --- CHANGELOG.md | 28 +++++++++++++++++++++++ Cargo.lock | 10 ++++---- Cargo.toml | 2 +- deltachat-ffi/Cargo.toml | 2 +- deltachat-jsonrpc/Cargo.toml | 2 +- deltachat-jsonrpc/typescript/package.json | 2 +- deltachat-repl/Cargo.toml | 2 +- deltachat-rpc-server/Cargo.toml | 2 +- package.json | 2 +- release-date.in | 2 +- 10 files changed, 41 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b88db8a2..479472882 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,32 @@ # Changelog +## [1.126.0] - 2023-10-22 + +### API-Changes + +- Allow to filter by unread in `chatlist:try_load` ([#4824](https://github.com/deltachat/deltachat-core-rust/pull/4824)). +- Add `misc_send_draft()` to JSON-RPC API ([#4839](https://github.com/deltachat/deltachat-core-rust/pull/4839)). + +### Features / Changes + +- [**breaking**] Make broadcast lists create their own chat ([#4644](https://github.com/deltachat/deltachat-core-rust/pull/4644)). + - This means that UIs need to ask for the name when creating a broadcast list, similar to . +- Add self-address to backup filename ([#4820](https://github.com/deltachat/deltachat-core-rust/pull/4820)) + +### CI + +- Build Python wheels for deltachat-rpc-server. + +### Build system + +- Strip release binaries. +- Workaround OpenSSL crate expecting libatomic to be available. + +### Fixes + +- Set `soft_heap_limit` on SQLite database. +- imap: Fallback to `STATUS` if `SELECT` did not return UIDNEXT. + ## [1.125.0] - 2023-10-14 ### API-Changes @@ -2915,3 +2942,4 @@ https://github.com/deltachat/deltachat-core-rust/pulls?q=is%3Apr+is%3Aclosed [1.124.0]: https://github.com/deltachat/deltachat-core-rust/compare/v1.123.0...v1.124.0 [1.124.1]: https://github.com/deltachat/deltachat-core-rust/compare/v1.124.0...v1.124.1 [1.125.0]: https://github.com/deltachat/deltachat-core-rust/compare/v1.124.1...v1.125.0 +[1.126.0]: https://github.com/deltachat/deltachat-core-rust/compare/v1.125.0...v1.126.0 diff --git a/Cargo.lock b/Cargo.lock index 0d388b721..a843f5f0a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1103,7 +1103,7 @@ dependencies = [ [[package]] name = "deltachat" -version = "1.125.0" +version = "1.126.0" dependencies = [ "ansi_term", "anyhow", @@ -1179,7 +1179,7 @@ dependencies = [ [[package]] name = "deltachat-jsonrpc" -version = "1.125.0" +version = "1.126.0" dependencies = [ "anyhow", "async-channel", @@ -1203,7 +1203,7 @@ dependencies = [ [[package]] name = "deltachat-repl" -version = "1.125.0" +version = "1.126.0" dependencies = [ "ansi_term", "anyhow", @@ -1218,7 +1218,7 @@ dependencies = [ [[package]] name = "deltachat-rpc-server" -version = "1.125.0" +version = "1.126.0" dependencies = [ "anyhow", "deltachat", @@ -1243,7 +1243,7 @@ dependencies = [ [[package]] name = "deltachat_ffi" -version = "1.125.0" +version = "1.126.0" dependencies = [ "anyhow", "deltachat", diff --git a/Cargo.toml b/Cargo.toml index 7cf3db408..1cd4824a0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "deltachat" -version = "1.125.0" +version = "1.126.0" edition = "2021" license = "MPL-2.0" rust-version = "1.65" diff --git a/deltachat-ffi/Cargo.toml b/deltachat-ffi/Cargo.toml index e53f1d96f..253c59bf3 100644 --- a/deltachat-ffi/Cargo.toml +++ b/deltachat-ffi/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "deltachat_ffi" -version = "1.125.0" +version = "1.126.0" description = "Deltachat FFI" edition = "2018" readme = "README.md" diff --git a/deltachat-jsonrpc/Cargo.toml b/deltachat-jsonrpc/Cargo.toml index 1a087922e..1aeca3fdf 100644 --- a/deltachat-jsonrpc/Cargo.toml +++ b/deltachat-jsonrpc/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "deltachat-jsonrpc" -version = "1.125.0" +version = "1.126.0" description = "DeltaChat JSON-RPC API" edition = "2021" default-run = "deltachat-jsonrpc-server" diff --git a/deltachat-jsonrpc/typescript/package.json b/deltachat-jsonrpc/typescript/package.json index 4f3dd7b4d..c2c0b4ad5 100644 --- a/deltachat-jsonrpc/typescript/package.json +++ b/deltachat-jsonrpc/typescript/package.json @@ -55,5 +55,5 @@ }, "type": "module", "types": "dist/deltachat.d.ts", - "version": "1.125.0" + "version": "1.126.0" } diff --git a/deltachat-repl/Cargo.toml b/deltachat-repl/Cargo.toml index 1a810f6fe..ccc0c8071 100644 --- a/deltachat-repl/Cargo.toml +++ b/deltachat-repl/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "deltachat-repl" -version = "1.125.0" +version = "1.126.0" license = "MPL-2.0" edition = "2021" diff --git a/deltachat-rpc-server/Cargo.toml b/deltachat-rpc-server/Cargo.toml index 044dc79c2..bb99f55b6 100644 --- a/deltachat-rpc-server/Cargo.toml +++ b/deltachat-rpc-server/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "deltachat-rpc-server" -version = "1.125.0" +version = "1.126.0" description = "DeltaChat JSON-RPC server" edition = "2021" readme = "README.md" diff --git a/package.json b/package.json index 05486b8fe..eb738e36f 100644 --- a/package.json +++ b/package.json @@ -60,5 +60,5 @@ "test:mocha": "mocha -r esm node/test/test.js --growl --reporter=spec --bail --exit" }, "types": "node/dist/index.d.ts", - "version": "1.125.0" + "version": "1.126.0" } diff --git a/release-date.in b/release-date.in index ac787018e..1a26377ca 100644 --- a/release-date.in +++ b/release-date.in @@ -1 +1 @@ -2023-10-14 \ No newline at end of file +2023-10-22 \ No newline at end of file