mirror of
https://github.com/chatmail/core.git
synced 2026-10-08 14:10:31 +03:00
Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c5e2eab8ce | ||
|
|
3adf53dcf8 |
+49
-22
@@ -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(())
|
||||
@@ -684,6 +674,43 @@ 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<usize>;
|
||||
|
||||
/// 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<T>(
|
||||
&self,
|
||||
query: &str,
|
||||
params: impl rusqlite::Params + Send,
|
||||
) -> Result<Option<T>>
|
||||
where
|
||||
T: rusqlite::types::FromSql + Send + 'static;
|
||||
}
|
||||
|
||||
impl TransactionExt for rusqlite::Transaction<'_> {
|
||||
fn count(&self, query: &str, params: impl rusqlite::Params + Send) -> Result<usize> {
|
||||
let count: isize = self.query_row(query, params, |row| row.get(0))?;
|
||||
Ok(usize::try_from(count)?)
|
||||
}
|
||||
|
||||
fn query_get_value<T>(
|
||||
&self,
|
||||
query: &str,
|
||||
params: impl rusqlite::Params + Send,
|
||||
) -> Result<Option<T>>
|
||||
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.
|
||||
///
|
||||
/// `path` is the database path.
|
||||
|
||||
+106
-3
@@ -16,6 +16,7 @@ use crate::key::DcKey;
|
||||
use crate::log::warn;
|
||||
|
||||
use crate::sql::Sql;
|
||||
use crate::sql::TransactionExt as _;
|
||||
use crate::tools::{self, Time, inc_and_check, time_elapsed};
|
||||
use crate::transport::ConfiguredLoginParam;
|
||||
|
||||
@@ -713,6 +714,95 @@ pub(crate) async fn msgs_to_key_contacts(context: &Context) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn unencrypted_chats_migration(
|
||||
context: &Context,
|
||||
transaction: &mut rusqlite::Transaction<'_>,
|
||||
) -> Result<bool> {
|
||||
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)
|
||||
// - all chats of type Mailinglist into a group with 0 members.
|
||||
transaction.execute_batch(
|
||||
"
|
||||
-- Save the rewritten chats in a table in case the migration is faulty
|
||||
-- and we need to fix something later:
|
||||
CREATE TEMP TABLE temp.legacy_unencrypted_chats(chat_id INTEGER PRIMARY KEY, type INTEGER) STRICT;
|
||||
|
||||
INSERT INTO temp.legacy_unencrypted_chats(chat_id, type)
|
||||
SELECT id, type FROM chats
|
||||
WHERE ((type=120 AND grpid='') OR type=140) -- Ad-hoc groups and mailinglists
|
||||
AND id>9;
|
||||
|
||||
DELETE FROM chats_contacts
|
||||
WHERE chat_id IN (SELECT chat_id FROM temp.legacy_unencrypted_chats);
|
||||
|
||||
UPDATE chats SET type=120
|
||||
WHERE id IN (SELECT chat_id FROM temp.legacy_unencrypted_chats);
|
||||
",
|
||||
)?;
|
||||
|
||||
// Rewrite all address-contacts into a key-contact with "Hidden" origin.
|
||||
// We still need the contacts because we want to keep the messages, and every message needs a sender.
|
||||
// Make sure that the address is available in the name, so that the user can still see it.
|
||||
transaction.execute_batch(
|
||||
"
|
||||
UPDATE contacts
|
||||
SET origin=8 -- Origin::Hidden
|
||||
WHERE fingerprint='' AND id>9;
|
||||
|
||||
UPDATE contacts
|
||||
SET name=name || ' (' || addr || ')'
|
||||
WHERE fingerprint='' AND id>9 AND name!='';
|
||||
|
||||
UPDATE contacts
|
||||
SET authname=authname || ' (' || addr || ')'
|
||||
WHERE fingerprint='' AND id>9 AND name='' AND authname!='';
|
||||
|
||||
UPDATE contacts
|
||||
SET authname=addr
|
||||
WHERE fingerprint='' AND id>9 AND name='' AND authname=''
|
||||
",
|
||||
)?;
|
||||
|
||||
let legacy_chats =
|
||||
transaction.count("SELECT COUNT(*) FROM temp.legacy_unencrypted_chats", ())?;
|
||||
let legacy_contacts = transaction.count(
|
||||
"SELECT COUNT(*) FROM contacts WHERE fingerprint='' AND id>9",
|
||||
(),
|
||||
)?;
|
||||
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"),
|
||||
"icon-unencrypted.png",
|
||||
)?;
|
||||
let new_param = &format!("i={}", blob.as_name());
|
||||
transaction.execute(
|
||||
"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<String> = 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(show_unencrypted_device_msg)
|
||||
}
|
||||
|
||||
impl Sql {
|
||||
async fn set_db_version(&self, version: i32) -> Result<()> {
|
||||
self.set_raw_config_int(VERSION_CFG, version).await?;
|
||||
@@ -812,7 +902,7 @@ pub async fn run(context: &Context, sql: &Sql) -> Result<bool> {
|
||||
}
|
||||
|
||||
let dbversion = dbversion_before_update;
|
||||
let mut recode_avatar = false;
|
||||
let mut show_unencrypted_device_msg = false;
|
||||
|
||||
if dbversion < 1 {
|
||||
sql.execute_migration(
|
||||
@@ -1187,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 {
|
||||
@@ -2687,6 +2777,19 @@ CREATE TABLE smtp_success (
|
||||
.await?;
|
||||
}
|
||||
|
||||
inc_and_check(&mut migration_version, 169)?;
|
||||
if dbversion < migration_version {
|
||||
sql.execute_migration_transaction(
|
||||
|transaction| {
|
||||
show_unencrypted_device_msg = unencrypted_chats_migration(context, transaction)?;
|
||||
|
||||
Ok(())
|
||||
},
|
||||
migration_version,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
|
||||
let new_version = sql
|
||||
.get_raw_config_int(VERSION_CFG)
|
||||
.await?
|
||||
@@ -2701,7 +2804,7 @@ CREATE TABLE smtp_success (
|
||||
}
|
||||
info!(context, "Database version: v{new_version}.");
|
||||
|
||||
Ok(recode_avatar)
|
||||
Ok(show_unencrypted_device_msg)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -145,7 +145,8 @@ async fn test_key_contacts_migration_email1() -> Result<()> {
|
||||
.unwrap();
|
||||
let email_bob = Contact::get_by_id(&t, email_bob_id).await?;
|
||||
assert_eq!(email_bob.is_key_contact(), false);
|
||||
assert_eq!(email_bob.origin, Origin::OutgoingTo);
|
||||
// All email address contacts are hidden now:
|
||||
assert_eq!(email_bob.origin, Origin::Hidden);
|
||||
assert_eq!(email_bob.e2ee_avail(&t).await?, false);
|
||||
assert_eq!(email_bob.fingerprint(), None);
|
||||
|
||||
@@ -178,7 +179,8 @@ async fn test_key_contacts_migration_email2() -> Result<()> {
|
||||
.unwrap();
|
||||
let email_bob = Contact::get_by_id(&t, email_bob_id).await?;
|
||||
assert_eq!(email_bob.is_key_contact(), false);
|
||||
assert_eq!(email_bob.origin, Origin::OutgoingTo);
|
||||
// All email address contacts are hidden now:
|
||||
assert_eq!(email_bob.origin, Origin::Hidden);
|
||||
assert_eq!(email_bob.e2ee_avail(&t).await?, false);
|
||||
assert_eq!(email_bob.fingerprint(), None);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user