From c5e2eab8ceda162f249f12e6c1941e8a7e09b668 Mon Sep 17 00:00:00 2001 From: Hocuri Date: Thu, 8 Oct 2026 11:02:12 +0200 Subject: [PATCH] Show device message about removal of unencrypted messages In its stead, remove `recode_avatar` high-level migration. It was added 5 years ago, and even if someone updates Delta Chat after such a long time, large avatars are not a problem anymore since we don't send them in unencrypted messages. --- src/sql.rs | 60 +++++++++++++++++++++++++++---------------- src/sql/migrations.rs | 25 ++++++++++++------ 2 files changed, 55 insertions(+), 30 deletions(-) diff --git a/src/sql.rs b/src/sql.rs index 7b4513f10..3e5b2260a 100644 --- a/src/sql.rs +++ b/src/sql.rs @@ -8,8 +8,7 @@ use anyhow::{Context as _, Result, bail}; use rusqlite::{Connection, OpenFlags, Row, config::DbConfig, types::ValueRef}; use tokio::sync::RwLock; -use crate::blob::BlobObject; -use crate::chat::ChatId; +use crate::chat::{ChatId, add_device_msg}; use crate::config::Config; use crate::context::Context; use crate::debug_logging::set_debug_logging_xdc; @@ -214,32 +213,23 @@ impl Sql { // this should be done before updates that use high-level objects that // rely themselves on the low-level structure. - let recode_avatar = migrations::run(context, self) + let show_unencrypted_device_msg = migrations::run(context, self) .await .context("failed to run migrations")?; // (2) updates that require high-level objects // the structure is complete now and all objects are usable - if recode_avatar && let Some(avatar) = context.get_config(Config::Selfavatar).await? { - let mut blob = BlobObject::from_path(context, Path::new(&avatar))?; - match blob.recode_to_avatar_size(context).await { - Ok(()) => { - if let Some(path) = blob.to_abs_path().to_str() { - context - .set_config_internal(Config::Selfavatar, Some(path)) - .await?; - } else { - warn!(context, "Setting selfavatar failed: non-UTF-8 filename"); - } - } - Err(e) => { - warn!(context, "Migrations can't recode avatar, removing. {:#}", e); - context - .set_config_internal(Config::Selfavatar, None) - .await? - } - } + if show_unencrypted_device_msg { + let txt = r#"To keep Delta Chat simpler and more secure, it no longer sends or receives messages that aren't end-to-end encrypted. + +Nothing is lost: your encrypted chats work as before, and old unencrypted chats remain readable. + +To keep sending and receiving unencrypted email, use a regular email app. You can find your email password in Delta Chat under "Settings → Advanced → Relays": tap (or right-click) your address and choose "Edit Relay". + +More details: https://..."#; + let mut msg = crate::message::Message::new_text(txt.to_string()); + add_device_msg(context, Some("unencrypted-device-msg"), Some(&mut msg)).await?; } Ok(()) @@ -685,7 +675,18 @@ impl Sql { } pub(crate) trait TransactionExt { + /// Used for executing `SELECT COUNT` statements only. Returns the resulting count. fn count(&self, query: &str, params: impl rusqlite::Params + Send) -> Result; + + /// Executes a query which is expected to return one row and one + /// column. If the query does not return any rows, returns `Ok(None)`. + fn query_get_value( + &self, + query: &str, + params: impl rusqlite::Params + Send, + ) -> Result> + where + T: rusqlite::types::FromSql + Send + 'static; } impl TransactionExt for rusqlite::Transaction<'_> { @@ -693,6 +694,21 @@ impl TransactionExt for rusqlite::Transaction<'_> { let count: isize = self.query_row(query, params, |row| row.get(0))?; Ok(usize::try_from(count)?) } + + fn query_get_value( + &self, + query: &str, + params: impl rusqlite::Params + Send, + ) -> Result> + where + T: rusqlite::types::FromSql + Send + 'static, + { + match self.query_row(query, params, |row| row.get::<_, T>(0)) { + Ok(res) => Ok(Some(res)), + Err(rusqlite::Error::QueryReturnedNoRows) => Ok(None), + Err(err) => Err(err.into()), + } + } } /// Creates a new SQLite connection. diff --git a/src/sql/migrations.rs b/src/sql/migrations.rs index 03d4bc2f1..149b0eed1 100644 --- a/src/sql/migrations.rs +++ b/src/sql/migrations.rs @@ -717,7 +717,9 @@ pub(crate) async fn msgs_to_key_contacts(context: &Context) -> Result<()> { fn unencrypted_chats_migration( context: &Context, transaction: &mut rusqlite::Transaction<'_>, -) -> Result<()> { +) -> Result { + let mut show_unencrypted_device_msg = false; + // Migrate: // - all unencrypted (i.e. ad-hoc) groups into regular groups with 0 members. // - all unencrypted 1:1 chats into a group with 0 members. (probably unnecessary, not implemented right now) @@ -764,7 +766,6 @@ WHERE fingerprint='' AND id>9 AND name='' AND authname='' ", )?; - // Set the gray letter avatar for all legacy chats: let legacy_chats = transaction.count("SELECT COUNT(*) FROM temp.legacy_unencrypted_chats", ())?; let legacy_contacts = transaction.count( @@ -772,6 +773,7 @@ WHERE fingerprint='' AND id>9 AND name='' AND authname='' (), )?; if legacy_chats > 0 || legacy_contacts > 0 { + // Set the gray letter avatar for all legacy chats: let blob = crate::blob::BlobObject::create_and_deduplicate_from_bytes( context, include_bytes!("../../assets/icon-unencrypted.png"), @@ -782,16 +784,23 @@ WHERE fingerprint='' AND id>9 AND name='' AND authname='' "UPDATE chats SET param=? WHERE id IN (SELECT chat_id FROM temp.legacy_unencrypted_chats)", (new_param,), )?; - transaction.execute( "UPDATE contacts SET param=? WHERE fingerprint='' AND id>9", (new_param,), )?; + + let force_encryption: Option = transaction.query_get_value( + "SELECT value FROM config WHERE keyname='force_encryption'", + (), + )?; + if force_encryption == Some("1".to_string()) { + show_unencrypted_device_msg = true; + } } transaction.execute("DROP TABLE temp.legacy_unencrypted_chats", ())?; - Ok(()) + Ok(show_unencrypted_device_msg) } impl Sql { @@ -893,7 +902,7 @@ pub async fn run(context: &Context, sql: &Sql) -> Result { } let dbversion = dbversion_before_update; - let mut recode_avatar = false; + let mut show_unencrypted_device_msg = false; if dbversion < 1 { sql.execute_migration( @@ -1268,7 +1277,7 @@ CREATE TABLE imap_sync (folder TEXT PRIMARY KEY, uidvalidity INTEGER DEFAULT 0, .await?; } if dbversion < 77 { - recode_avatar = true; + // removed sql.set_db_version(77).await?; } if dbversion < 78 { @@ -2772,7 +2781,7 @@ CREATE TABLE smtp_success ( if dbversion < migration_version { sql.execute_migration_transaction( |transaction| { - unencrypted_chats_migration(context, transaction)?; + show_unencrypted_device_msg = unencrypted_chats_migration(context, transaction)?; Ok(()) }, @@ -2795,7 +2804,7 @@ CREATE TABLE smtp_success ( } info!(context, "Database version: v{new_version}."); - Ok(recode_avatar) + Ok(show_unencrypted_device_msg) } #[cfg(test)]