mirror of
https://github.com/chatmail/core.git
synced 2026-05-08 09:26:29 +03:00
- Add encryption_modus field for msgs table and expose getters/setters on
Message - Mind the encryption modus in RenderedEmail
This commit is contained in:
40
src/chat.rs
40
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
|
/// 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)
|
/// meet the encryption modus (ForcePlaintext, Opportunistic, ForceEncrypted, ForceVerified)
|
||||||
pub async fn set_encryption_modus(self, context: &Context, modus: &EncryptionModus) -> Result<()> {
|
pub async fn set_encryption_modus(self, context: &Context, modus: &EncryptionModus) -> Result<()> {
|
||||||
let encryption_modus: u32 = context
|
context
|
||||||
.sql
|
.sql
|
||||||
.query_get_value(
|
.execute(
|
||||||
"SELECT encryption_modus FROM chats WHERE id=?;",
|
"UPDATE chats SET encryption_modus=? WHERE id=?;",
|
||||||
paramsv![self],
|
paramsv![modus, self],
|
||||||
)
|
)
|
||||||
.await??;
|
.await?;
|
||||||
Ok(encryption_modus.unwrap_or_default())
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This sets a protection modus for the chat and enforces that messages are only send if they
|
/// 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)
|
/// meet the encryption modus (ForcePlaintext, Opportunistic, ForceEncrypted, ForceVerified)
|
||||||
pub async fn get_encryption_modus(self, context: &Context) -> Result<EncryptionModus> {
|
pub async fn get_encryption_modus(self, context: &Context) -> Result<Option<EncryptionModus>> {
|
||||||
let encryption_modus: u32 = context
|
let encryption_modus: Option<EncryptionModus> = context
|
||||||
.sql
|
.sql
|
||||||
.query_get_value(
|
.query_get_value(
|
||||||
"SELECT encryption_modus FROM chats WHERE id=?;",
|
"SELECT encryption_modus FROM chats WHERE id=?;",
|
||||||
paramsv![self],
|
paramsv![self],
|
||||||
)
|
)
|
||||||
.await??;
|
.await?;
|
||||||
Ok(encryption_modus.into())
|
|
||||||
|
|
||||||
|
|
||||||
|
Ok(encryption_modus)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -5699,4 +5703,20 @@ mod tests {
|
|||||||
|
|
||||||
Ok(())
|
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(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ use crate::tools::{
|
|||||||
create_smeared_timestamp, get_filebytes, get_filemeta, gm2local_offset, read_file, time,
|
create_smeared_timestamp, get_filebytes, get_filemeta, gm2local_offset, read_file, time,
|
||||||
timestamp_to_str, truncate,
|
timestamp_to_str, truncate,
|
||||||
};
|
};
|
||||||
|
use crate::chat::EncryptionModus;
|
||||||
|
|
||||||
/// Message ID, including reserved IDs.
|
/// Message ID, including reserved IDs.
|
||||||
///
|
///
|
||||||
@@ -302,6 +303,7 @@ impl Message {
|
|||||||
" m.hidden AS hidden,",
|
" m.hidden AS hidden,",
|
||||||
" m.location_id AS location,",
|
" m.location_id AS location,",
|
||||||
" c.blocked AS blocked",
|
" c.blocked AS blocked",
|
||||||
|
" m.encryption_modus as encryption_modus",
|
||||||
" FROM msgs m LEFT JOIN chats c ON c.id=m.chat_id",
|
" FROM msgs m LEFT JOIN chats c ON c.id=m.chat_id",
|
||||||
" WHERE m.id=?;"
|
" WHERE m.id=?;"
|
||||||
),
|
),
|
||||||
@@ -354,7 +356,7 @@ impl Message {
|
|||||||
location_id: row.get("location")?,
|
location_id: row.get("location")?,
|
||||||
chat_blocked: row
|
chat_blocked: row
|
||||||
.get::<_, Option<Blocked>>("blocked")?
|
.get::<_, Option<Blocked>>("blocked")?
|
||||||
.unwrap_or_default(),
|
.unwrap_or_default()
|
||||||
};
|
};
|
||||||
Ok(msg)
|
Ok(msg)
|
||||||
},
|
},
|
||||||
@@ -848,10 +850,35 @@ impl Message {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Force the message to be sent in plain text.
|
/// Force the message to be sent in plain text.
|
||||||
|
/// Deprecated: use Message::set_encryption_modus(EncryptionModus::ForcePlaintext)
|
||||||
pub fn force_plaintext(&mut self) {
|
pub fn force_plaintext(&mut self) {
|
||||||
self.param.set_int(Param::ForcePlaintext, 1);
|
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<Option<EncryptionModus>> {
|
||||||
|
let encryption_modus: Option<EncryptionModus> = 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<()> {
|
pub async fn update_param(&self, context: &Context) -> Result<()> {
|
||||||
context
|
context
|
||||||
.sql
|
.sql
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ use crate::tools::{
|
|||||||
create_outgoing_rfc724_mid, create_smeared_timestamp, get_filebytes, remove_subject_prefix,
|
create_outgoing_rfc724_mid, create_smeared_timestamp, get_filebytes, remove_subject_prefix,
|
||||||
time,
|
time,
|
||||||
};
|
};
|
||||||
|
use crate::chat::EncryptionModus;
|
||||||
|
|
||||||
// attachments of 25 mb brutto should work on the majority of providers
|
// 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).
|
// (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 {
|
match &self.loaded {
|
||||||
Loaded::Message { chat } => {
|
Loaded::Message { chat } => {
|
||||||
if chat.is_protected() {
|
if chat.is_protected() {
|
||||||
@@ -333,6 +334,8 @@ impl<'a> MimeFactory<'a> {
|
|||||||
// encryption may disclose recipients;
|
// encryption may disclose recipients;
|
||||||
// this is probably a worse issue than not opportunistically (!) encrypting
|
// this is probably a worse issue than not opportunistically (!) encrypting
|
||||||
true
|
true
|
||||||
|
} else if encryption_modus == &EncryptionModus::ForcePlaintext {
|
||||||
|
true
|
||||||
} else {
|
} else {
|
||||||
self.msg
|
self.msg
|
||||||
.param
|
.param
|
||||||
@@ -602,7 +605,11 @@ impl<'a> MimeFactory<'a> {
|
|||||||
|
|
||||||
let min_verified = self.min_verified();
|
let min_verified = self.min_verified();
|
||||||
let grpimage = self.grpimage();
|
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 skip_autocrypt = self.should_skip_autocrypt();
|
||||||
let e2ee_guaranteed = self.is_e2ee_guaranteed();
|
let e2ee_guaranteed = self.is_e2ee_guaranteed();
|
||||||
let encrypt_helper = EncryptHelper::new(context).await?;
|
let encrypt_helper = EncryptHelper::new(context).await?;
|
||||||
@@ -644,6 +651,26 @@ impl<'a> MimeFactory<'a> {
|
|||||||
encrypt_helper.should_encrypt(context, e2ee_guaranteed, &peerstates)?;
|
encrypt_helper.should_encrypt(context, e2ee_guaranteed, &peerstates)?;
|
||||||
let is_encrypted = should_encrypt && !force_plaintext;
|
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() {
|
let message = if parts.is_empty() {
|
||||||
// Single part, render as regular message.
|
// Single part, render as regular message.
|
||||||
main_part
|
main_part
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ pub enum Param {
|
|||||||
ErroneousE2ee = b'e',
|
ErroneousE2ee = b'e',
|
||||||
|
|
||||||
/// For Messages: force unencrypted message, a value from `ForcePlaintext` enum.
|
/// For Messages: force unencrypted message, a value from `ForcePlaintext` enum.
|
||||||
|
/// Deprecated: Use EncryptionModus::ForcePlaintext
|
||||||
ForcePlaintext = b'u',
|
ForcePlaintext = b'u',
|
||||||
|
|
||||||
/// For Messages: do not include Autocrypt header.
|
/// For Messages: do not include Autocrypt header.
|
||||||
|
|||||||
@@ -93,7 +93,8 @@ CREATE TABLE msgs (
|
|||||||
-- deleted. It is convenient to store it here because UI
|
-- deleted. It is convenient to store it here because UI
|
||||||
-- needs this value to display how much time is left until
|
-- needs this value to display how much time is left until
|
||||||
-- the message is deleted.
|
-- 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);
|
CREATE INDEX msgs_index1 ON msgs (rfc724_mid);
|
||||||
|
|||||||
Reference in New Issue
Block a user