Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 50 additions & 9 deletions src/chat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,13 @@ use crate::download::{
use crate::ensure_and_debug_assert_eq;
use crate::ephemeral::{Timer as EphemeralTimer, start_chat_ephemeral_timers};
use crate::events::EventType;
use crate::key;
use crate::key::{Fingerprint, self_fingerprint};
use crate::location;
use crate::log::{LogExt, warn};
use crate::logged_debug_assert;
use crate::message::{self, Message, MessageState, MsgId, Viewtype};
use crate::mimefactory;
use crate::mimefactory::{MimeFactory, RenderedEmail};
use crate::mimeparser::SystemMessage;
use crate::param::{Param, Params};
Expand Down Expand Up @@ -2757,6 +2759,11 @@ async fn render_mime_message_and_pre_message(
msg: &mut Message,
mimefactory: MimeFactory,
) -> Result<(Option<RenderedEmail>, RenderedEmail)> {
let from_addr = context.get_primary_self_addr().await?;
let public_key = key::load_self_public_key(context).await?;
let secret_key = key::load_self_secret_key(context).await?;
let timestamp = msg.timestamp_sort;

let needs_pre_message = msg.viewtype.has_file()
&& mimefactory.will_be_encrypted() // unencrypted is likely email, we don't want to spam by sending multiple messages
&& msg
Expand All @@ -2773,15 +2780,32 @@ async fn render_mime_message_and_pre_message(

let mut mimefactory_post_msg = mimefactory.clone();
mimefactory_post_msg.set_as_post_message();
let rendered_msg = Box::pin(mimefactory_post_msg.render(context))
let (queued_msg, side_effects) = Box::pin(mimefactory_post_msg.pre_render(context))
.await
.context("Failed to render post-message")?;

let rendered_msg = mimefactory::render_queued_mail(
queued_msg,
&public_key,
&secret_key,
from_addr.clone(),
timestamp,
side_effects,
)?;

let mut mimefactory_pre_msg = mimefactory;
mimefactory_pre_msg.set_as_pre_message_for(&rendered_msg);
let rendered_pre_msg = Box::pin(mimefactory_pre_msg.render(context))
let (queued_pre_msg, pre_side_effects) = Box::pin(mimefactory_pre_msg.pre_render(context))
.await
.context("pre-message failed to render")?;
let rendered_pre_msg = mimefactory::render_queued_mail(
queued_pre_msg,
&public_key,
&secret_key,
from_addr,
timestamp,
pre_side_effects,
)?;

if rendered_pre_msg.message.len() > PRE_MSG_SIZE_WARNING_THRESHOLD {
warn!(
Expand All @@ -2794,7 +2818,17 @@ async fn render_mime_message_and_pre_message(

Ok((Some(rendered_pre_msg), rendered_msg))
} else {
Ok((None, Box::pin(mimefactory.render(context)).await?))
let (queued_msg, side_effects) = Box::pin(mimefactory.pre_render(context)).await?;
let rendered_msg = mimefactory::render_queued_mail(
queued_msg,
&public_key,
&secret_key,
from_addr,
timestamp,
side_effects,
)?;

Ok((None, rendered_msg))
}
}

Expand Down Expand Up @@ -2839,7 +2873,6 @@ pub(crate) async fn create_send_msg_jobs(context: &Context, msg: &mut Message) -
return Err(err);
}
};
let attach_selfavatar = mimefactory.attach_selfavatar;
let mut recipients = mimefactory.recipients();

let from = context.get_primary_self_addr().await?;
Expand Down Expand Up @@ -2926,22 +2959,30 @@ pub(crate) async fn create_send_msg_jobs(context: &Context, msg: &mut Message) -

let now = time();

if let Some(last_added_location_timestamp) = rendered_msg.last_added_location_timestamp {
if let Some(last_added_location_timestamp) =
rendered_msg.side_effects.last_added_location_timestamp
{
location::set_kml_sent_timestamp(context, msg.chat_id, last_added_location_timestamp)
.await?;
}

if attach_selfavatar && let Err(err) = msg.chat_id.set_selfavatar_timestamp(context, now).await
if rendered_msg.side_effects.avatar_is_attached
|| rendered_pre_msg
.as_ref()
.is_some_and(|msg| msg.side_effects.avatar_is_attached)
{
error!(context, "Failed to set selfavatar timestamp: {err:#}.");
msg.chat_id
.set_selfavatar_timestamp(context, now)
.await
.context("Failed to set selfavatar timestamp")?;
}

if rendered_msg.is_encrypted {
msg.param.set_int(Param::GuaranteeE2ee, 1);
} else {
msg.param.remove(Param::GuaranteeE2ee);
}
msg.subject.clone_from(&rendered_msg.subject);
msg.subject.clone_from(&rendered_msg.side_effects.subject);
// Sort the message to the bottom. Employ `msgs_index7` to compute `timestamp`.
context
.sql
Expand Down Expand Up @@ -2974,7 +3015,7 @@ WHERE id=?
let trans_fn = |t: &mut rusqlite::Transaction| {
let mut row_ids = Vec::<i64>::new();

if let Some(sync_ids) = rendered_msg.sync_ids_to_delete {
if let Some(sync_ids) = rendered_msg.side_effects.sync_ids_to_delete {
t.execute(
&format!("DELETE FROM multi_device_sync WHERE id IN ({sync_ids})"),
(),
Expand Down
2 changes: 1 addition & 1 deletion src/download.rs
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ impl Session {
let (sender, receiver) = async_channel::unbounded();
{
let _fetch_msgs_lock_guard = context.fetch_msgs_mutex.lock().await;
self.fetch_many_msgs(context, folder, vec![uid], &uid_message_ids, sender)
Box::pin(self.fetch_many_msgs(context, folder, vec![uid], &uid_message_ids, sender))
.await?;
}
if receiver.recv().await.is_err() {
Expand Down
100 changes: 2 additions & 98 deletions src/e2ee.rs
Original file line number Diff line number Diff line change
@@ -1,105 +1,9 @@
//! End-to-end encryption support.

use std::io::Cursor;

use anyhow::Result;
use mail_builder::mime::MimePart;

use crate::aheader::{Aheader, EncryptPreference};
use crate::context::Context;
use crate::key::{SignedPublicKey, load_self_public_key, load_self_secret_key};
use crate::pgp::{self, SeipdVersion};

#[derive(Debug)]
pub struct EncryptHelper {
pub addr: String,
pub public_key: SignedPublicKey,
}

impl EncryptHelper {
pub async fn new(context: &Context) -> Result<EncryptHelper> {
let addr = context.get_primary_self_addr().await?;
let public_key = load_self_public_key(context).await?;

Ok(EncryptHelper { addr, public_key })
}

pub fn get_aheader(&self) -> Aheader {
Aheader {
addr: self.addr.clone(),
public_key: self.public_key.clone(),
prefer_encrypt: EncryptPreference::Mutual,
verified: false,
}
}

/// Tries to encrypt the passed in `mail`.
pub async fn encrypt(
self,
context: &Context,
keyring: Vec<SignedPublicKey>,
mail_to_encrypt: MimePart<'static>,
compress: bool,
seipd_version: SeipdVersion,
) -> Result<String> {
let mut raw_message = Vec::new();
let cursor = Cursor::new(&mut raw_message);
mail_to_encrypt.clone().write_part(cursor).ok();

let ctext = self
.encrypt_raw(context, keyring, raw_message, compress, seipd_version)
.await?;
Ok(ctext)
}

pub async fn encrypt_raw(
self,
context: &Context,
keyring: Vec<SignedPublicKey>,
raw_message: Vec<u8>,
compress: bool,
seipd_version: SeipdVersion,
) -> Result<String> {
let sign_key = load_self_secret_key(context).await?;
let ctext =
pgp::pk_encrypt(raw_message, keyring, sign_key, compress, seipd_version).await?;

Ok(ctext)
}

/// Symmetrically encrypt the message. This is used for broadcast channels.
/// `shared secret` is the secret that will be used for symmetric encryption.
pub async fn encrypt_symmetrically(
self,
context: &Context,
shared_secret: &str,
mail_to_encrypt: MimePart<'static>,
compress: bool,
sign: bool,
) -> Result<String> {
let sign_key = if sign {
Some(load_self_secret_key(context).await?)
} else {
None
};

let shared_secret = shared_secret.to_string();
let mut raw_message = Vec::new();
let cursor = Cursor::new(&mut raw_message);
mail_to_encrypt.clone().write_part(cursor).ok();

let ctext = tokio::task::spawn_blocking(move || {
pgp::symm_encrypt_message(raw_message, sign_key, shared_secret, compress)
})
.await??;

Ok(ctext)
}
}

#[cfg(test)]
mod tests {
use super::*;
use anyhow::Result;

use crate::chat;
use crate::chat::send_text_msg;
use crate::config::Config;
Expand Down
3 changes: 1 addition & 2 deletions src/imap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -753,8 +753,7 @@ impl Imap {
};

let actually_download_messages_future = async {
session
.fetch_many_msgs(context, folder, uids_fetch, &uid_message_ids, sender)
Box::pin(session.fetch_many_msgs(context, folder, uids_fetch, &uid_message_ids, sender))
.await
.context("fetch_many_msgs")
};
Expand Down
Loading
Loading