diff --git a/src/chat.rs b/src/chat.rs index 3610762d0..08e9e9674 100644 --- a/src/chat.rs +++ b/src/chat.rs @@ -924,27 +924,31 @@ impl ChatId { /// This sets a protection modus for the chat and enforces that messages are only send if they /// meet the encryption modus (ForcePlaintext, Opportunistic, ForceEncrypted, ForceVerified) pub async fn set_encryption_modus(self, context: &Context, modus: &EncryptionModus) -> Result<()> { - let encryption_modus: u32 = context + context .sql - .query_get_value( - "SELECT encryption_modus FROM chats WHERE id=?;", - paramsv![self], + .execute( + "UPDATE chats SET encryption_modus=? WHERE id=?;", + paramsv![modus, self], ) - .await??; - Ok(encryption_modus.unwrap_or_default()) + .await?; + + Ok(()) } /// This sets a protection modus for the chat and enforces that messages are only send if they /// meet the encryption modus (ForcePlaintext, Opportunistic, ForceEncrypted, ForceVerified) - pub async fn get_encryption_modus(self, context: &Context) -> Result { - let encryption_modus: u32 = context + pub async fn get_encryption_modus(self, context: &Context) -> Result> { + let encryption_modus: Option = context .sql .query_get_value( "SELECT encryption_modus FROM chats WHERE id=?;", paramsv![self], ) - .await??; - Ok(encryption_modus.into()) + .await?; + + + + Ok(encryption_modus) } @@ -5699,4 +5703,20 @@ mod tests { Ok(()) } + + + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] + async fn test_encryption_modus() -> Result<()> { + let t = TestContext::new_alice().await; + let contact_fiona = Contact::create(&t, "", "fiona@example.net").await?; + let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "Group").await?; + + assert_eq!(chat_id.get_encryption_modus(&t).await?, Some(EncryptionModus::Opportunistic)); + + chat_id.set_encryption_modus(&t, &EncryptionModus::ForceEncrypted).await?; + + assert_eq!(chat_id.get_encryption_modus(&t).await?, Some(EncryptionModus::ForceEncrypted)); + + Ok(()) + } } diff --git a/src/message.rs b/src/message.rs index 600bbc8f1..154b62839 100644 --- a/src/message.rs +++ b/src/message.rs @@ -31,6 +31,7 @@ use crate::tools::{ create_smeared_timestamp, get_filebytes, get_filemeta, gm2local_offset, read_file, time, timestamp_to_str, truncate, }; +use crate::chat::EncryptionModus; /// Message ID, including reserved IDs. /// @@ -302,6 +303,7 @@ impl Message { " m.hidden AS hidden,", " m.location_id AS location,", " c.blocked AS blocked", + " m.encryption_modus as encryption_modus", " FROM msgs m LEFT JOIN chats c ON c.id=m.chat_id", " WHERE m.id=?;" ), @@ -354,7 +356,7 @@ impl Message { location_id: row.get("location")?, chat_blocked: row .get::<_, Option>("blocked")? - .unwrap_or_default(), + .unwrap_or_default() }; Ok(msg) }, @@ -848,10 +850,35 @@ impl Message { } /// Force the message to be sent in plain text. + /// Deprecated: use Message::set_encryption_modus(EncryptionModus::ForcePlaintext) pub fn force_plaintext(&mut self) { self.param.set_int(Param::ForcePlaintext, 1); } + async fn set_encryption_modus(&mut self, context: &Context, encryption_modus: &EncryptionModus) -> Result<()> { + context + .sql + .execute( + "UPDATE msgs SET encryption_modus=? WHERE id=?;", + paramsv![encryption_modus, self.id], + ) + .await?; + + Ok(()) + } + + pub async fn get_encryption_modus(&self, context: &Context) -> Result> { + let encryption_modus: Option = context + .sql + .query_get_value( + "SELECT encryption_modus FROM msgs WHERE id=?;", + paramsv![self.id], + ) + .await?; + + Ok(encryption_modus) + } + pub async fn update_param(&self, context: &Context) -> Result<()> { context .sql diff --git a/src/mimefactory.rs b/src/mimefactory.rs index b6f0fef04..afc5327c5 100644 --- a/src/mimefactory.rs +++ b/src/mimefactory.rs @@ -29,6 +29,7 @@ use crate::tools::{ create_outgoing_rfc724_mid, create_smeared_timestamp, get_filebytes, remove_subject_prefix, time, }; +use crate::chat::EncryptionModus; // attachments of 25 mb brutto should work on the majority of providers // (brutto examples: web.de=50, 1&1=40, t-online.de=32, gmail=25, posteo=50, yahoo=25, all-inkl=100). @@ -324,7 +325,7 @@ impl<'a> MimeFactory<'a> { } } - fn should_force_plaintext(&self) -> bool { + fn should_force_plaintext(&self, encryption_modus: &EncryptionModus) -> bool { match &self.loaded { Loaded::Message { chat } => { if chat.is_protected() { @@ -333,6 +334,8 @@ impl<'a> MimeFactory<'a> { // encryption may disclose recipients; // this is probably a worse issue than not opportunistically (!) encrypting true + } else if encryption_modus == &EncryptionModus::ForcePlaintext { + true } else { self.msg .param @@ -602,7 +605,11 @@ impl<'a> MimeFactory<'a> { let min_verified = self.min_verified(); let grpimage = self.grpimage(); - let force_plaintext = self.should_force_plaintext(); + let encryption_modus = match self.msg.get_encryption_modus(context).await? { + Some(encryption_modus) => encryption_modus, + None => EncryptionModus::Opportunistic + }; + let force_plaintext = self.should_force_plaintext(&encryption_modus); let skip_autocrypt = self.should_skip_autocrypt(); let e2ee_guaranteed = self.is_e2ee_guaranteed(); let encrypt_helper = EncryptHelper::new(context).await?; @@ -644,6 +651,26 @@ impl<'a> MimeFactory<'a> { encrypt_helper.should_encrypt(context, e2ee_guaranteed, &peerstates)?; let is_encrypted = should_encrypt && !force_plaintext; + + // Ensure we fulfill encryption_modus + match encryption_modus { + EncryptionModus::Opportunistic => {}, + EncryptionModus::ForcePlaintext => { + ensure!(!is_encrypted, "EncryptionModus is ForcePlaintext but message is encrypted"); + }, + EncryptionModus::ForceEncrypted => { + ensure!(is_encrypted, "EncryptionModus is ForceEncrypted but message is unencrypted"); + }, + EncryptionModus::ForceVerified => { + let chat_is_protected = if let Loaded::Message { chat } = &self.loaded { + chat.is_protected() + } else { + false + }; + ensure!(is_encrypted && chat_is_protected, "EncryptionModus is ForceVerified but chat is not protected"); + } + }; + let message = if parts.is_empty() { // Single part, render as regular message. main_part diff --git a/src/param.rs b/src/param.rs index 981a1f0dd..d120188dd 100644 --- a/src/param.rs +++ b/src/param.rs @@ -51,6 +51,7 @@ pub enum Param { ErroneousE2ee = b'e', /// For Messages: force unencrypted message, a value from `ForcePlaintext` enum. + /// Deprecated: Use EncryptionModus::ForcePlaintext ForcePlaintext = b'u', /// For Messages: do not include Autocrypt header. diff --git a/src/sql/tables.sql b/src/sql/tables.sql index 9daf778b2..d105dddec 100644 --- a/src/sql/tables.sql +++ b/src/sql/tables.sql @@ -93,7 +93,8 @@ CREATE TABLE msgs ( -- deleted. It is convenient to store it here because UI -- needs this value to display how much time is left until -- the message is deleted. - ephemeral_timestamp INTEGER DEFAULT 0 + ephemeral_timestamp INTEGER DEFAULT 0, + encryption_modus INTEGER DEFAULT 0 ); CREATE INDEX msgs_index1 ON msgs (rfc724_mid);