mirror of
https://github.com/chatmail/core.git
synced 2026-05-02 04:46:29 +03:00
Compare commits
3 Commits
hoc/remove
...
dependabot
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cbdbdcc2b3 | ||
|
|
4d537544ef | ||
|
|
4a16c0c3dd |
24
Cargo.lock
generated
24
Cargo.lock
generated
@@ -2654,7 +2654,7 @@ dependencies = [
|
||||
"hyper",
|
||||
"libc",
|
||||
"pin-project-lite",
|
||||
"socket2 0.6.0",
|
||||
"socket2 0.5.9",
|
||||
"tokio",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
@@ -3453,13 +3453,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "1.0.3"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd"
|
||||
checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.61.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5725,12 +5725,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
version = "0.6.0"
|
||||
version = "0.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807"
|
||||
checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.61.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6144,9 +6144,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.50.0"
|
||||
version = "1.52.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "27ad5e34374e03cfffefc301becb44e9dc3c17584f414349ebe29ed26661822d"
|
||||
checksum = "b67dee974fe86fd92cc45b7a95fdd2f99a36a6d7b0d431a231178d3d670bbcc6"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"libc",
|
||||
@@ -6154,7 +6154,7 @@ dependencies = [
|
||||
"parking_lot",
|
||||
"pin-project-lite",
|
||||
"signal-hook-registry",
|
||||
"socket2 0.6.0",
|
||||
"socket2 0.6.3",
|
||||
"tokio-macros",
|
||||
"windows-sys 0.61.1",
|
||||
]
|
||||
@@ -6171,9 +6171,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tokio-macros"
|
||||
version = "2.6.0"
|
||||
version = "2.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5"
|
||||
checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
//! Helper functions for decryption.
|
||||
//! The actual decryption is done in the [`crate::pgp`] module.
|
||||
|
||||
use std::collections::HashSet;
|
||||
use std::io::Cursor;
|
||||
|
||||
use anyhow::{Context as _, Result, bail};
|
||||
@@ -18,8 +19,8 @@ use crate::chat::ChatId;
|
||||
use crate::constants::Chattype;
|
||||
use crate::contact::ContactId;
|
||||
use crate::context::Context;
|
||||
use crate::key::load_self_secret_keyring;
|
||||
use crate::key::self_fingerprint;
|
||||
use crate::key::{Fingerprint, SignedPublicKey, load_self_secret_keyring};
|
||||
use crate::token::Namespace;
|
||||
|
||||
/// Tries to decrypt the message,
|
||||
@@ -334,6 +335,36 @@ fn get_autocrypt_mime<'a, 'b>(mail: &'a ParsedMail<'b>) -> Option<&'a ParsedMail
|
||||
}
|
||||
}
|
||||
|
||||
/// Validates signatures of Multipart/Signed message part, as defined in RFC 1847.
|
||||
///
|
||||
/// Returns the signed part and the set of key
|
||||
/// fingerprints for which there is a valid signature.
|
||||
///
|
||||
/// Returns None if the message is not Multipart/Signed or doesn't contain necessary parts.
|
||||
pub(crate) fn validate_detached_signature<'a, 'b>(
|
||||
mail: &'a ParsedMail<'b>,
|
||||
public_keyring_for_validate: &[SignedPublicKey],
|
||||
) -> Option<(&'a ParsedMail<'b>, HashSet<Fingerprint>)> {
|
||||
if mail.ctype.mimetype != "multipart/signed" {
|
||||
return None;
|
||||
}
|
||||
|
||||
if let [first_part, second_part] = &mail.subparts[..] {
|
||||
// First part is the content, second part is the signature.
|
||||
let content = first_part.raw_bytes;
|
||||
let ret_valid_signatures = match second_part.get_body_raw() {
|
||||
Ok(signature) => {
|
||||
crate::pgp::pk_validate(content, &signature, public_keyring_for_validate)
|
||||
.unwrap_or_default()
|
||||
}
|
||||
Err(_) => Default::default(),
|
||||
};
|
||||
Some((first_part, ret_valid_signatures))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
@@ -871,7 +871,7 @@ mod tests {
|
||||
use crate::config::Config;
|
||||
use crate::message::MessageState;
|
||||
use crate::receive_imf::receive_imf;
|
||||
use crate::test_utils::{TestContext, TestContextManager};
|
||||
use crate::test_utils::{ExpectedEvents, TestContext, TestContextManager};
|
||||
use crate::tools::SystemTime;
|
||||
|
||||
#[test]
|
||||
@@ -1103,6 +1103,9 @@ Content-Disposition: attachment; filename="location.kml"
|
||||
.await?;
|
||||
|
||||
let alice_chat = alice.create_chat(bob).await;
|
||||
// Bob needs the chat accepted so that "normal" messages from Alice trigger `IncomingMsg`.
|
||||
// Location-only messages still must trigger `MsgsChanged`.
|
||||
bob.create_chat(alice).await;
|
||||
|
||||
// Alice enables location streaming.
|
||||
// Bob receives a message saying that Alice enabled location streaming.
|
||||
@@ -1117,7 +1120,18 @@ Content-Disposition: attachment; filename="location.kml"
|
||||
SystemTime::shift(Duration::from_secs(10));
|
||||
delete_expired(alice, time()).await?;
|
||||
maybe_send(alice).await?;
|
||||
bob.evtracker.clear_events();
|
||||
bob.recv_msg_opt(&alice.pop_sent_msg().await).await;
|
||||
bob.evtracker
|
||||
.get_matching_ex(
|
||||
bob,
|
||||
ExpectedEvents {
|
||||
expected: |e| matches!(e, EventType::MsgsChanged { .. }),
|
||||
unexpected: |e| matches!(e, EventType::IncomingMsg { .. }),
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(get_range(alice, None, None, 0, 0).await?.len(), 1);
|
||||
assert_eq!(get_range(bob, None, None, 0, 0).await?.len(), 1);
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ use crate::config::Config;
|
||||
use crate::constants;
|
||||
use crate::contact::{ContactId, import_public_key};
|
||||
use crate::context::Context;
|
||||
use crate::decrypt::{self};
|
||||
use crate::decrypt::{self, validate_detached_signature};
|
||||
use crate::dehtml::dehtml;
|
||||
use crate::download::PostMsgMetadata;
|
||||
use crate::events::EventType;
|
||||
@@ -487,6 +487,17 @@ impl MimeMessage {
|
||||
HashMap::new()
|
||||
};
|
||||
|
||||
let mail = mail.as_ref().map(|mail| {
|
||||
let (content, signatures_detached) = validate_detached_signature(mail, &public_keyring)
|
||||
.unwrap_or((mail, Default::default()));
|
||||
let signatures_detached = signatures_detached
|
||||
.into_iter()
|
||||
.map(|fp| (fp, Vec::new()))
|
||||
.collect::<HashMap<_, _>>();
|
||||
signatures.extend(signatures_detached);
|
||||
content
|
||||
});
|
||||
|
||||
if let Some(expected_sender_fingerprint) = expected_sender_fingerprint {
|
||||
ensure!(
|
||||
!signatures.is_empty(),
|
||||
@@ -502,7 +513,7 @@ impl MimeMessage {
|
||||
);
|
||||
}
|
||||
|
||||
if let (Ok(mail), true) = (&mail, is_encrypted) {
|
||||
if let (Ok(mail), true) = (mail, is_encrypted) {
|
||||
if !signatures.is_empty() {
|
||||
// Unsigned "Subject" mustn't be prepended to messages shown as encrypted
|
||||
// (<https://github.com/deltachat/deltachat-core-rust/issues/1790>).
|
||||
@@ -527,7 +538,7 @@ impl MimeMessage {
|
||||
&mut inner_from,
|
||||
&mut list_post,
|
||||
&mut chat_disposition_notification_to,
|
||||
&mail,
|
||||
mail,
|
||||
);
|
||||
|
||||
if !signatures.is_empty() {
|
||||
@@ -571,7 +582,7 @@ impl MimeMessage {
|
||||
signatures.clear();
|
||||
}
|
||||
|
||||
if let (Ok(mail), true) = (&mail, is_encrypted)
|
||||
if let (Ok(mail), true) = (mail, is_encrypted)
|
||||
&& let Some(post_msg_rfc724_mid) =
|
||||
mail.headers.get_header_value(HeaderDef::ChatPostMessageId)
|
||||
{
|
||||
@@ -629,7 +640,7 @@ impl MimeMessage {
|
||||
from,
|
||||
incoming,
|
||||
chat_disposition_notification_to,
|
||||
decryption_error: mail.as_ref().err().map(|err| format!("{err:#}")),
|
||||
decryption_error: mail.err().map(|err| format!("{err:#}")),
|
||||
|
||||
// only non-empty if it was a valid autocrypt message
|
||||
signature,
|
||||
@@ -655,9 +666,9 @@ impl MimeMessage {
|
||||
pre_message,
|
||||
};
|
||||
|
||||
match &mail {
|
||||
match mail {
|
||||
Ok(mail) => {
|
||||
parser.parse_mime_recursive(context, &mail, false).await?;
|
||||
parser.parse_mime_recursive(context, mail, false).await?;
|
||||
}
|
||||
Err(err) => {
|
||||
let txt = "[This message cannot be decrypted.\n\n• It might already help to simply reply to this message and ask the sender to send the message again.\n\n• If you just re-installed Delta Chat then it is best if you re-setup Delta Chat now and choose \"Add as second device\" or import a backup.]";
|
||||
|
||||
@@ -1019,8 +1019,15 @@ UPDATE msgs SET state=? WHERE
|
||||
let is_bot = context.get_config_bool(Config::Bot).await?;
|
||||
let is_pre_message = matches!(mime_parser.pre_message, PreMessageMode::Pre { .. });
|
||||
let skip_bot_notify = is_bot && is_pre_message;
|
||||
let important =
|
||||
mime_parser.incoming && fresh && !is_old_contact_request && !skip_bot_notify;
|
||||
let is_empty = !is_pre_message
|
||||
&& mime_parser.parts.first().is_none_or(|p| {
|
||||
p.typ == Viewtype::Text && p.msg.is_empty() && p.param.get(Param::Quote).is_none()
|
||||
});
|
||||
let important = mime_parser.incoming
|
||||
&& !is_empty
|
||||
&& fresh
|
||||
&& !is_old_contact_request
|
||||
&& !skip_bot_notify;
|
||||
|
||||
for msg_id in &received_msg.msg_ids {
|
||||
chat_id.emit_msg_event(context, *msg_id, important);
|
||||
|
||||
@@ -1431,6 +1431,12 @@ pub fn fiona_keypair() -> SignedSecretKey {
|
||||
#[derive(Debug)]
|
||||
pub struct EventTracker(EventEmitter);
|
||||
|
||||
/// See [`super::EventTracker::get_matching_ex`].
|
||||
pub struct ExpectedEvents<E: Fn(&EventType) -> bool, U: Fn(&EventType) -> bool> {
|
||||
pub expected: E,
|
||||
pub unexpected: U,
|
||||
}
|
||||
|
||||
impl Deref for EventTracker {
|
||||
type Target = EventEmitter;
|
||||
|
||||
@@ -1467,21 +1473,39 @@ impl EventTracker {
|
||||
.expect("timeout waiting for event match")
|
||||
}
|
||||
|
||||
/// Consumes emitted events returning the first matching one if any.
|
||||
/// Consumes all emitted events returning the first matching one if any.
|
||||
pub async fn get_matching_opt<F: Fn(&EventType) -> bool>(
|
||||
&self,
|
||||
ctx: &Context,
|
||||
event_matcher: F,
|
||||
) -> Option<EventType> {
|
||||
self.get_matching_ex(
|
||||
ctx,
|
||||
ExpectedEvents {
|
||||
expected: event_matcher,
|
||||
unexpected: |_| false,
|
||||
},
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
/// Consumes all emitted events returning the first matching one if any. Panics on unexpected
|
||||
/// events.
|
||||
pub async fn get_matching_ex<E: Fn(&EventType) -> bool, U: Fn(&EventType) -> bool>(
|
||||
&self,
|
||||
ctx: &Context,
|
||||
args: ExpectedEvents<E, U>,
|
||||
) -> Option<EventType> {
|
||||
ctx.emit_event(EventType::Test);
|
||||
let mut found_event = None;
|
||||
loop {
|
||||
let event = self.recv().await.unwrap();
|
||||
assert!(!(args.unexpected)(&event.typ));
|
||||
if let EventType::Test = event.typ {
|
||||
return found_event;
|
||||
}
|
||||
if event_matcher(&event.typ) {
|
||||
found_event = Some(event.typ);
|
||||
if (args.expected)(&event.typ) {
|
||||
found_event.get_or_insert(event.typ);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user