mirror of
https://github.com/chatmail/core.git
synced 2026-04-02 05:22:14 +03:00
Compare commits
9 Commits
6ab8b3c3e7
...
feat_encry
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
41a87a0626 | ||
|
|
d5faf75f56 | ||
|
|
e3ff786eb7 | ||
|
|
540cc8b205 | ||
|
|
7533f863d1 | ||
|
|
bc4eea0c28 | ||
|
|
d8844a0524 | ||
|
|
a714a4c2da | ||
|
|
cb03e93570 |
@@ -47,7 +47,7 @@ use types::provider_info::ProviderInfo;
|
||||
use types::webxdc::WebxdcMessageInfo;
|
||||
|
||||
use self::types::{
|
||||
chat::{BasicChat, JSONRPCChatVisibility, MuteDuration},
|
||||
chat::{BasicChat, JSONRPCChatVisibility, JSONRPCEncryptionModus, MuteDuration},
|
||||
location::JsonrpcLocation,
|
||||
message::{
|
||||
JSONRPCMessageListItem, MessageNotificationInfo, MessageSearchResult, MessageViewtype,
|
||||
@@ -528,6 +528,8 @@ impl CommandApi {
|
||||
ChatId::new(chat_id).get_encryption_info(&ctx).await
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Get QR code (text and SVG) that will offer an Setup-Contact or Verified-Group invitation.
|
||||
/// The QR code is compatible to the OPENPGP4FPR format
|
||||
/// so that a basic fingerprint comparison also works e.g. with OpenKeychain.
|
||||
@@ -557,6 +559,32 @@ impl CommandApi {
|
||||
))
|
||||
}
|
||||
|
||||
async fn set_chat_encryption_modus(
|
||||
&self,
|
||||
account_id: u32,
|
||||
chat_id: u32,
|
||||
encryption_modus: JSONRPCEncryptionModus
|
||||
) -> Result<()> {
|
||||
let ctx = self.get_context(account_id).await?;
|
||||
let chat = ChatId::new(chat_id);
|
||||
Ok(chat.set_encryption_modus(&ctx, encryption_modus.into_core_type()).await?)
|
||||
}
|
||||
|
||||
async fn get_chat_encryption_modus(
|
||||
&self,
|
||||
account_id: u32,
|
||||
chat_id: u32
|
||||
) -> Result<Option<JSONRPCEncryptionModus>> {
|
||||
let ctx = self.get_context(account_id).await?;
|
||||
let chat = ChatId::new(chat_id);
|
||||
Ok(
|
||||
match chat.encryption_modus(&ctx).await? {
|
||||
Some(encryption_modus) => Some(JSONRPCEncryptionModus::from_core_type(encryption_modus)),
|
||||
None => None,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/// Continue a Setup-Contact or Verified-Group-Invite protocol
|
||||
/// started on another device with `get_chat_securejoin_qr_code_svg()`.
|
||||
/// This function is typically called when `check_qr()` returns
|
||||
|
||||
@@ -2,7 +2,7 @@ use std::time::{Duration, SystemTime};
|
||||
|
||||
use anyhow::{anyhow, bail, Result};
|
||||
use deltachat::chat::{self, get_chat_contacts, ChatVisibility};
|
||||
use deltachat::chat::{Chat, ChatId};
|
||||
use deltachat::chat::{Chat, ChatId, EncryptionModus};
|
||||
use deltachat::constants::Chattype;
|
||||
use deltachat::contact::{Contact, ContactId};
|
||||
use deltachat::context::Context;
|
||||
@@ -211,3 +211,33 @@ impl JSONRPCChatVisibility {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize, TypeDef)]
|
||||
#[serde(rename = "EncryptionModus")]
|
||||
pub enum JSONRPCEncryptionModus {
|
||||
Opportunistic = 0,
|
||||
ForcePlaintext = 1,
|
||||
ForceEncrypted = 2,
|
||||
ForceVerified = 3,
|
||||
}
|
||||
|
||||
impl JSONRPCEncryptionModus {
|
||||
pub fn into_core_type(self) -> EncryptionModus {
|
||||
match self {
|
||||
JSONRPCEncryptionModus::Opportunistic => EncryptionModus::Opportunistic,
|
||||
JSONRPCEncryptionModus::ForcePlaintext => EncryptionModus::ForcePlaintext,
|
||||
JSONRPCEncryptionModus::ForceEncrypted => EncryptionModus::ForceEncrypted,
|
||||
JSONRPCEncryptionModus::ForceVerified => EncryptionModus::ForceVerified
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_core_type(core_encryption_modus: EncryptionModus) -> Self {
|
||||
match core_encryption_modus {
|
||||
EncryptionModus::Opportunistic => JSONRPCEncryptionModus::Opportunistic,
|
||||
EncryptionModus::ForcePlaintext => JSONRPCEncryptionModus::ForcePlaintext,
|
||||
EncryptionModus::ForceEncrypted => JSONRPCEncryptionModus::ForceEncrypted,
|
||||
EncryptionModus::ForceVerified => JSONRPCEncryptionModus::ForceVerified
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -331,6 +331,16 @@ export class RawClient {
|
||||
return (this._transport.request('get_chat_securejoin_qr_code_svg', [accountId, chatId] as RPC.Params)) as Promise<[string,string]>;
|
||||
}
|
||||
|
||||
|
||||
public setChatEncryptionModus(accountId: T.U32, chatId: T.U32, encryptionModus: T.EncryptionModus): Promise<null> {
|
||||
return (this._transport.request('set_chat_encryption_modus', [accountId, chatId, encryptionModus] as RPC.Params)) as Promise<null>;
|
||||
}
|
||||
|
||||
|
||||
public getChatEncryptionModus(accountId: T.U32, chatId: T.U32): Promise<(T.EncryptionModus|null)> {
|
||||
return (this._transport.request('get_chat_encryption_modus', [accountId, chatId] as RPC.Params)) as Promise<(T.EncryptionModus|null)>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Continue a Setup-Contact or Verified-Group-Invite protocol
|
||||
* started on another device with `get_chat_securejoin_qr_code_svg()`.
|
||||
@@ -730,8 +740,8 @@ export class RawClient {
|
||||
}
|
||||
|
||||
|
||||
public deleteContact(accountId: T.U32, contactId: T.U32): Promise<boolean> {
|
||||
return (this._transport.request('delete_contact', [accountId, contactId] as RPC.Params)) as Promise<boolean>;
|
||||
public deleteContact(accountId: T.U32, contactId: T.U32): Promise<null> {
|
||||
return (this._transport.request('delete_contact', [accountId, contactId] as RPC.Params)) as Promise<null>;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -50,6 +50,7 @@ export type BasicChat=
|
||||
* used when you only need the basic metadata of a chat like type, name, profile picture
|
||||
*/
|
||||
{"id":U32;"name":string;"isProtected":boolean;"profileImage":(string|null);"archived":boolean;"chatType":U32;"isUnpromoted":boolean;"isSelfTalk":boolean;"color":string;"isContactRequest":boolean;"isDeviceChat":boolean;"isMuted":boolean;};
|
||||
export type EncryptionModus=("Opportunistic"|"ForcePlaintext"|"ForceEncrypted"|"ForceVerified");
|
||||
export type ChatVisibility=("Normal"|"Archived"|"Pinned");
|
||||
export type MuteDuration=("NotMuted"|"Forever"|{"Until":I64;});
|
||||
export type MessageListItem=(({"kind":"message";}&{"msg_id":U32;})|({
|
||||
@@ -195,4 +196,4 @@ export type MessageNotificationInfo={"id":U32;"chatId":U32;"accountId":U32;"imag
|
||||
export type MessageSearchResult={"id":U32;"authorProfileImage":(string|null);"authorName":string;"authorColor":string;"chatName":(string|null);"message":string;"timestamp":I64;};
|
||||
export type F64=number;
|
||||
export type Location={"locationId":U32;"isIndependent":boolean;"latitude":F64;"longitude":F64;"accuracy":F64;"timestamp":I64;"contactId":U32;"msgId":U32;"chatId":U32;"marker":(string|null);};
|
||||
export type __AllTyps=[string,boolean,Record<string,string>,U32,U32,null,(U32)[],U32,null,(U32|null),(Account)[],null,null,U32,null,U32,null,U32,Account,U32,U64,U32,string,(ProviderInfo|null),U32,boolean,U32,Record<string,string>,U32,string,(string|null),null,U32,Record<string,(string|null)>,null,U32,string,null,U32,string,Qr,U32,string,(string|null),U32,(string)[],Record<string,(string|null)>,Record<U32,string>,null,U32,null,U32,null,U32,string,(string|null),null,U32,string,(string|null),null,U32,(U32)[],U32,U32,Usize,U32,boolean,I64,Usize,U32,string,U32,U32,string,null,U32,(U32|null),(string|null),(U32|null),(ChatListEntry)[],U32,(ChatListEntry)[],Record<U32,ChatListItemFetchResult>,U32,U32,FullChat,U32,U32,BasicChat,U32,U32,null,U32,U32,null,U32,U32,null,U32,U32,string,U32,(U32|null),[string,string],U32,string,U32,U32,U32,null,U32,U32,U32,null,U32,U32,U32,null,U32,U32,(U32)[],U32,string,boolean,U32,U32,U32,U32,U32,string,null,U32,U32,(string|null),null,U32,U32,ChatVisibility,null,U32,U32,U32,null,U32,U32,U32,U32,string,string,U32,U32,U32,null,U32,U32,(U32|null),U32,U32,MuteDuration,null,U32,U32,boolean,U32,(U32)[],null,U32,U32,U32,(U32)[],U32,U32,U32,(MessageListItem)[],U32,U32,Message,U32,U32,(string|null),U32,(U32)[],Record<U32,Message>,U32,U32,MessageNotificationInfo,U32,(U32)[],null,U32,U32,string,U32,U32,null,U32,string,(U32|null),(U32)[],U32,(U32)[],Record<U32,MessageSearchResult>,U32,U32,Contact,U32,string,(string|null),U32,U32,U32,U32,U32,U32,null,U32,U32,null,U32,(Contact)[],U32,U32,(string|null),(U32)[],U32,U32,(string|null),(Contact)[],U32,(U32)[],Record<U32,Contact>,U32,U32,boolean,U32,U32,string,null,U32,U32,string,U32,string,(U32|null),U32,(U32|null),Viewtype,(Viewtype|null),(Viewtype|null),(U32)[],U32,U32,Viewtype,(Viewtype|null),(Viewtype|null),[(U32|null),(U32|null)],U32,string,(string|null),null,U32,string,(string|null),null,null,U32,U32,U32,string,U32,(U32|null),(U32|null),I64,I64,(Location)[],U32,U32,string,string,null,U32,U32,U32,string,U32,U32,WebxdcMessageInfo,U32,(U32)[],U32,null,U32,U32,string,U32,U32,U32,(string)[],U32,U32,U32,null,U32,U32,(Message|null),U32,U32,U32,U32,string,U32,U32,string,null,U32,Record<string,(string)[]>,U32,U32,string,U32,U32,U32,(string|null),(string|null),([F64,F64]|null),(U32|null),[U32,Message],U32,U32,(string|null),(string|null),(U32|null),null];
|
||||
export type __AllTyps=[string,boolean,Record<string,string>,U32,U32,null,(U32)[],U32,null,(U32|null),(Account)[],null,null,U32,null,U32,null,U32,Account,U32,U64,U32,string,(ProviderInfo|null),U32,boolean,U32,Record<string,string>,U32,string,(string|null),null,U32,Record<string,(string|null)>,null,U32,string,null,U32,string,Qr,U32,string,(string|null),U32,(string)[],Record<string,(string|null)>,Record<U32,string>,null,U32,null,U32,null,U32,string,(string|null),null,U32,string,(string|null),null,U32,(U32)[],U32,U32,Usize,U32,boolean,I64,Usize,U32,string,U32,U32,string,null,U32,(U32|null),(string|null),(U32|null),(ChatListEntry)[],U32,(ChatListEntry)[],Record<U32,ChatListItemFetchResult>,U32,U32,FullChat,U32,U32,BasicChat,U32,U32,null,U32,U32,null,U32,U32,null,U32,U32,string,U32,(U32|null),[string,string],U32,U32,EncryptionModus,null,U32,U32,(EncryptionModus|null),U32,string,U32,U32,U32,null,U32,U32,U32,null,U32,U32,U32,null,U32,U32,(U32)[],U32,string,boolean,U32,U32,U32,U32,U32,string,null,U32,U32,(string|null),null,U32,U32,ChatVisibility,null,U32,U32,U32,null,U32,U32,U32,U32,string,string,U32,U32,U32,null,U32,U32,(U32|null),U32,U32,MuteDuration,null,U32,U32,boolean,U32,(U32)[],null,U32,U32,U32,(U32)[],U32,U32,U32,(MessageListItem)[],U32,U32,Message,U32,U32,(string|null),U32,(U32)[],Record<U32,Message>,U32,U32,MessageNotificationInfo,U32,(U32)[],null,U32,U32,string,U32,U32,null,U32,string,(U32|null),(U32)[],U32,(U32)[],Record<U32,MessageSearchResult>,U32,U32,Contact,U32,string,(string|null),U32,U32,U32,U32,U32,U32,null,U32,U32,null,U32,(Contact)[],U32,U32,(string|null),(U32)[],U32,U32,(string|null),(Contact)[],U32,(U32)[],Record<U32,Contact>,U32,U32,null,U32,U32,string,null,U32,U32,string,U32,string,(U32|null),U32,(U32|null),Viewtype,(Viewtype|null),(Viewtype|null),(U32)[],U32,U32,Viewtype,(Viewtype|null),(Viewtype|null),[(U32|null),(U32|null)],U32,string,(string|null),null,U32,string,(string|null),null,null,U32,U32,U32,string,U32,(U32|null),(U32|null),I64,I64,(Location)[],U32,U32,string,string,null,U32,U32,U32,string,U32,U32,WebxdcMessageInfo,U32,(U32)[],U32,null,U32,U32,string,U32,U32,U32,(string)[],U32,U32,U32,null,U32,U32,(Message|null),U32,U32,U32,U32,string,U32,U32,string,null,U32,Record<string,(string)[]>,U32,U32,string,U32,U32,U32,(string|null),(string|null),([F64,F64]|null),(U32|null),[U32,Message],U32,U32,(string|null),(string|null),(U32|null),null];
|
||||
|
||||
86
src/chat.rs
86
src/chat.rs
@@ -75,6 +75,29 @@ pub enum ProtectionStatus {
|
||||
Protected = 1,
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Debug,
|
||||
Display,
|
||||
Clone,
|
||||
Copy,
|
||||
PartialEq,
|
||||
Eq,
|
||||
FromPrimitive,
|
||||
ToPrimitive,
|
||||
FromSql,
|
||||
ToSql,
|
||||
IntoStaticStr,
|
||||
Serialize,
|
||||
Deserialize,
|
||||
)]
|
||||
#[repr(u32)]
|
||||
pub enum EncryptionModus {
|
||||
Opportunistic = 0,
|
||||
ForcePlaintext = 1,
|
||||
ForceEncrypted = 2,
|
||||
ForceVerified = 3,
|
||||
}
|
||||
|
||||
impl Default for ProtectionStatus {
|
||||
fn default() -> Self {
|
||||
ProtectionStatus::Unprotected
|
||||
@@ -898,6 +921,38 @@ impl ChatId {
|
||||
Ok(ret.trim().to_string())
|
||||
}
|
||||
|
||||
/// 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<()> {
|
||||
context
|
||||
.sql
|
||||
.execute(
|
||||
"UPDATE chats SET encryption_modus=? WHERE id=?;",
|
||||
paramsv![modus, self],
|
||||
)
|
||||
.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 encryption_modus(self, context: &Context) -> Result<Option<EncryptionModus>> {
|
||||
let encryption_modus: Option<EncryptionModus> = context
|
||||
.sql
|
||||
.query_get_value(
|
||||
"SELECT encryption_modus FROM chats WHERE id=?;",
|
||||
paramsv![self],
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(encryption_modus)
|
||||
}
|
||||
|
||||
/// Bad evil escape hatch.
|
||||
///
|
||||
/// Avoid using this, eventually types should be cleaned up enough
|
||||
@@ -1944,6 +1999,14 @@ pub async fn is_contact_in_chat(
|
||||
// the caller can get it from msg.chat_id. Forwards would need to
|
||||
// be fixed for this somehow too.
|
||||
pub async fn send_msg(context: &Context, chat_id: ChatId, msg: &mut Message) -> Result<MsgId> {
|
||||
// Propagate same encryption_mode of chat to message in case messages doesn't yet have an
|
||||
// encryption_mode
|
||||
if let None = msg.encryption_modus(&context).await? {
|
||||
if let Some(encryption_mode) = chat_id.encryption_modus(&context).await? {
|
||||
msg.set_encryption_modus(&context, encryption_mode).await?;
|
||||
}
|
||||
}
|
||||
|
||||
if chat_id.is_unset() {
|
||||
let forwards = msg.param.get(Param::PrepForwards);
|
||||
if let Some(forwards) = forwards {
|
||||
@@ -5649,4 +5712,27 @@ 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.encryption_modus(&t).await?,
|
||||
Some(EncryptionModus::Opportunistic)
|
||||
);
|
||||
|
||||
chat_id
|
||||
.set_encryption_modus(&t, EncryptionModus::ForceEncrypted)
|
||||
.await?;
|
||||
|
||||
assert_eq!(
|
||||
chat_id.encryption_modus(&t).await?,
|
||||
Some(EncryptionModus::ForceEncrypted)
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ use deltachat_derive::{FromSql, ToSql};
|
||||
use rusqlite::types::ValueRef;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::chat::EncryptionModus;
|
||||
use crate::chat::{self, Chat, ChatId};
|
||||
use crate::config::Config;
|
||||
use crate::constants::{
|
||||
@@ -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=?;"
|
||||
),
|
||||
@@ -848,10 +850,39 @@ 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);
|
||||
}
|
||||
|
||||
pub 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 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<()> {
|
||||
context
|
||||
.sql
|
||||
|
||||
@@ -9,6 +9,7 @@ use tokio::fs;
|
||||
|
||||
use crate::blob::BlobObject;
|
||||
use crate::chat::Chat;
|
||||
use crate::chat::EncryptionModus;
|
||||
use crate::config::Config;
|
||||
use crate::constants::{Chattype, DC_FROM_HANDSHAKE};
|
||||
use crate::contact::Contact;
|
||||
@@ -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.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,34 @@ 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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -10,7 +10,7 @@ use crate::provider::get_provider_by_domain;
|
||||
use crate::sql::Sql;
|
||||
use crate::tools::EmailAddress;
|
||||
|
||||
const DBVERSION: i32 = 68;
|
||||
const DBVERSION: i32 = 69;
|
||||
const VERSION_CFG: &str = "dbversion";
|
||||
const TABLES: &str = include_str!("./tables.sql");
|
||||
|
||||
@@ -616,6 +616,15 @@ CREATE INDEX smtp_messageid ON imap(rfc724_mid);
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
if dbversion < 94 {
|
||||
sql.execute_migration(
|
||||
r#"
|
||||
ALTER TABLE chats ADD COLUMN encryption_modus INTEGER DEFAULT 0;
|
||||
ALTER TABLE msgs ADD COLUMN encryption_modus INTEGER DEFAULT 0;"#,
|
||||
94,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
|
||||
let new_version = sql
|
||||
.get_raw_config_int(VERSION_CFG)
|
||||
|
||||
Reference in New Issue
Block a user