mirror of
https://github.com/chatmail/core.git
synced 2026-04-27 02:16:29 +03:00
cleanup and finish missing parts
This commit is contained in:
@@ -116,8 +116,7 @@ pub fn dc_receive_imf(
|
|||||||
|
|
||||||
if let Some(value) = mime_parser.lookup_field("Date") {
|
if let Some(value) = mime_parser.lookup_field("Date") {
|
||||||
// is not yet checked against bad times! we do this later if we have the database information.
|
// is not yet checked against bad times! we do this later if we have the database information.
|
||||||
// sent_timestamp = dc_timestamp_from_date((*orig_date).dt_date_time)
|
sent_timestamp = mailparse::dateparse(value).unwrap_or_default();
|
||||||
// TODO
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// get From: and check if it is known (for known From:'s we add the other To:/Cc: in the 3rd pass)
|
// get From: and check if it is known (for known From:'s we add the other To:/Cc: in the 3rd pass)
|
||||||
|
|||||||
107
src/e2ee.rs
107
src/e2ee.rs
@@ -137,14 +137,14 @@ impl EncryptHelper {
|
|||||||
pub fn try_decrypt(
|
pub fn try_decrypt(
|
||||||
context: &Context,
|
context: &Context,
|
||||||
mail: &mailparse::ParsedMail<'_>,
|
mail: &mailparse::ParsedMail<'_>,
|
||||||
) -> Result<(Option<Vec<u8>>, HashSet<String>, HashSet<String>)> {
|
) -> Result<(Option<Vec<u8>>, HashSet<String>, i64)> {
|
||||||
use mailparse::MailHeaderMap;
|
use mailparse::MailHeaderMap;
|
||||||
|
|
||||||
let from = mail.headers.get_first_value("From")?.unwrap_or_default();
|
let from = mail.headers.get_first_value("From")?.unwrap_or_default();
|
||||||
let message_time = mail
|
let message_time = mail
|
||||||
.headers
|
.headers
|
||||||
.get_first_value("Date")?
|
.get_first_value("Date")?
|
||||||
.and_then(|v| v.parse().ok())
|
.and_then(|v| mailparse::dateparse(&v).ok())
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
let mut peerstate = None;
|
let mut peerstate = None;
|
||||||
@@ -171,7 +171,6 @@ pub fn try_decrypt(
|
|||||||
let mut private_keyring = Keyring::default();
|
let mut private_keyring = Keyring::default();
|
||||||
let mut public_keyring_for_validate = Keyring::default();
|
let mut public_keyring_for_validate = Keyring::default();
|
||||||
let mut out_mail = None;
|
let mut out_mail = None;
|
||||||
let mut gossipped_addr = HashSet::default();
|
|
||||||
let mut signatures = HashSet::default();
|
let mut signatures = HashSet::default();
|
||||||
let self_addr = context.get_config(Config::ConfiguredAddr);
|
let self_addr = context.get_config(Config::ConfiguredAddr);
|
||||||
|
|
||||||
@@ -199,15 +198,9 @@ pub fn try_decrypt(
|
|||||||
&public_keyring_for_validate,
|
&public_keyring_for_validate,
|
||||||
&mut signatures,
|
&mut signatures,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// TODO:
|
|
||||||
// if !gossip_headers.is_empty() {
|
|
||||||
// gossipped_addr =
|
|
||||||
// update_gossip_peerstates(context, message_time, imffields, gossip_headers)?;
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok((out_mail, signatures, gossipped_addr))
|
Ok((out_mail, signatures, message_time))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Load public key from database or generate a new one.
|
/// Load public key from database or generate a new one.
|
||||||
@@ -257,74 +250,6 @@ fn load_or_generate_self_public_key(context: &Context, self_addr: impl AsRef<str
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// fn update_gossip_peerstates(
|
|
||||||
// context: &Context,
|
|
||||||
// message_time: i64,
|
|
||||||
// imffields: *mut mailimf_fields,
|
|
||||||
// gossip_headers: *const mailimf_fields,
|
|
||||||
// ) -> Result<HashSet<String>> {
|
|
||||||
// // XXX split the parsing from the modification part
|
|
||||||
// let mut recipients: Option<HashSet<String>> = None;
|
|
||||||
// let mut gossipped_addr: HashSet<String> = Default::default();
|
|
||||||
|
|
||||||
// for cur_data in { (*(*gossip_headers).fld_list).into_iter() } {
|
|
||||||
// let field = cur_data as *mut mailimf_field;
|
|
||||||
// if field.is_null() {
|
|
||||||
// continue;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// let field = { *field };
|
|
||||||
|
|
||||||
// if field.fld_type == MAILIMF_FIELD_OPTIONAL_FIELD as libc::c_int {
|
|
||||||
// let optional_field = { field.fld_data.fld_optional_field };
|
|
||||||
// if optional_field.is_null() {
|
|
||||||
// continue;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// let optional_field = { *optional_field };
|
|
||||||
// if !optional_field.fld_name.is_null()
|
|
||||||
// && to_string_lossy(optional_field.fld_name) == "Autocrypt-Gossip"
|
|
||||||
// {
|
|
||||||
// let value = to_string_lossy(optional_field.fld_value);
|
|
||||||
// let gossip_header = Aheader::from_str(&value);
|
|
||||||
|
|
||||||
// if let Ok(ref header) = gossip_header {
|
|
||||||
// if recipients.is_none() {
|
|
||||||
// recipients = Some(mailimf_get_recipients(imffields));
|
|
||||||
// }
|
|
||||||
// if recipients.as_ref().unwrap().contains(&header.addr) {
|
|
||||||
// let mut peerstate =
|
|
||||||
// Peerstate::from_addr(context, &context.sql, &header.addr);
|
|
||||||
// if let Some(ref mut peerstate) = peerstate {
|
|
||||||
// peerstate.apply_gossip(header, message_time);
|
|
||||||
// peerstate.save_to_db(&context.sql, false)?;
|
|
||||||
// } else {
|
|
||||||
// let p = Peerstate::from_gossip(context, header, message_time);
|
|
||||||
// p.save_to_db(&context.sql, true)?;
|
|
||||||
// peerstate = Some(p);
|
|
||||||
// }
|
|
||||||
// if let Some(peerstate) = peerstate {
|
|
||||||
// if peerstate.degrade_event.is_some() {
|
|
||||||
// handle_degrade_event(context, &peerstate)?;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// gossipped_addr.insert(header.addr.clone());
|
|
||||||
// } else {
|
|
||||||
// info!(
|
|
||||||
// context,
|
|
||||||
// "Ignoring gossipped \"{}\" as the address is not in To/Cc list.",
|
|
||||||
// &header.addr,
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Ok(gossipped_addr)
|
|
||||||
// }
|
|
||||||
|
|
||||||
fn decrypt_if_autocrypt_message<'a>(
|
fn decrypt_if_autocrypt_message<'a>(
|
||||||
context: &Context,
|
context: &Context,
|
||||||
mail: &mailparse::ParsedMail<'a>,
|
mail: &mailparse::ParsedMail<'a>,
|
||||||
@@ -347,35 +272,13 @@ fn decrypt_if_autocrypt_message<'a>(
|
|||||||
Ok(res) => res,
|
Ok(res) => res,
|
||||||
};
|
};
|
||||||
|
|
||||||
let decrypted = decrypt_part(
|
decrypt_part(
|
||||||
context,
|
context,
|
||||||
encrypted_data_part,
|
encrypted_data_part,
|
||||||
private_keyring,
|
private_keyring,
|
||||||
public_keyring_for_validate,
|
public_keyring_for_validate,
|
||||||
ret_valid_signatures,
|
ret_valid_signatures,
|
||||||
)?;
|
)
|
||||||
|
|
||||||
// finally, let's also return gossip headers
|
|
||||||
// XXX better return parsed headers so that upstream
|
|
||||||
// does not need to dive into mmime-stuff again.
|
|
||||||
// {
|
|
||||||
// if (*ret_gossip_headers).is_null() && !ret_valid_signatures.is_empty() {
|
|
||||||
// let mut dummy: libc::size_t = 0;
|
|
||||||
// let mut test: *mut mailimf_fields = ptr::null_mut();
|
|
||||||
// if mailimf_envelope_and_optional_fields_parse(
|
|
||||||
// (*decrypted_mime).mm_mime_start,
|
|
||||||
// (*decrypted_mime).mm_length,
|
|
||||||
// &mut dummy,
|
|
||||||
// &mut test,
|
|
||||||
// ) == MAILIMF_NO_ERROR as libc::c_int
|
|
||||||
// && !test.is_null()
|
|
||||||
// {
|
|
||||||
// *ret_gossip_headers = test;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
Ok(decrypted)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns Ok(None) if nothing encrypted was found.
|
/// Returns Ok(None) if nothing encrypted was found.
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
|
|
||||||
use deltachat_derive::{FromSql, ToSql};
|
use deltachat_derive::{FromSql, ToSql};
|
||||||
|
use mailparse::MailHeaderMap;
|
||||||
|
|
||||||
|
use crate::aheader::Aheader;
|
||||||
use crate::blob::BlobObject;
|
use crate::blob::BlobObject;
|
||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
use crate::constants::Viewtype;
|
use crate::constants::Viewtype;
|
||||||
@@ -16,8 +18,9 @@ use crate::location;
|
|||||||
use crate::message;
|
use crate::message;
|
||||||
use crate::message::MsgId;
|
use crate::message::MsgId;
|
||||||
use crate::param::*;
|
use crate::param::*;
|
||||||
|
use crate::peerstate::Peerstate;
|
||||||
|
use crate::securejoin::handle_degrade_event;
|
||||||
use crate::stock::StockMessage;
|
use crate::stock::StockMessage;
|
||||||
use crate::wrapmime;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct MimeParser<'a> {
|
pub struct MimeParser<'a> {
|
||||||
@@ -100,14 +103,27 @@ impl<'a> MimeParser<'a> {
|
|||||||
|
|
||||||
let mail_raw;
|
let mail_raw;
|
||||||
let mail = match e2ee::try_decrypt(parser.context, &mail) {
|
let mail = match e2ee::try_decrypt(parser.context, &mail) {
|
||||||
Ok((raw, signatures, gossipped_addr)) => {
|
Ok((raw, signatures, message_time)) => {
|
||||||
|
// Valid autocrypt message, encrypted
|
||||||
parser.encrypted = raw.is_some();
|
parser.encrypted = raw.is_some();
|
||||||
parser.signatures = signatures;
|
parser.signatures = signatures;
|
||||||
parser.gossipped_addr = gossipped_addr;
|
|
||||||
if let Some(raw) = raw {
|
if let Some(raw) = raw {
|
||||||
mail_raw = raw;
|
mail_raw = raw;
|
||||||
mailparse::parse_mail(&mail_raw)?
|
let decrypted_mail = mailparse::parse_mail(&mail_raw)?;
|
||||||
|
|
||||||
|
// we have a decrypted mail, that is valid, check for gossip headers
|
||||||
|
|
||||||
|
let gossip_headers =
|
||||||
|
decrypted_mail.headers.get_all_values("Autocrypt-Gossip")?;
|
||||||
|
if !gossip_headers.is_empty() {
|
||||||
|
parser.gossipped_addr =
|
||||||
|
update_gossip_peerstates(context, message_time, &mail, gossip_headers)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
decrypted_mail
|
||||||
} else {
|
} else {
|
||||||
|
// Message was not encrypted
|
||||||
mail
|
mail
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -726,7 +742,7 @@ impl<'a> MimeParser<'a> {
|
|||||||
}
|
}
|
||||||
if let mailparse::MailAddr::Single(ref info) = addrs[0] {
|
if let mailparse::MailAddr::Single(ref info) = addrs[0] {
|
||||||
let from_addr_norm = addr_normalize(&info.addr);
|
let from_addr_norm = addr_normalize(&info.addr);
|
||||||
let recipients = wrapmime::mailimf_get_recipients(&self.header);
|
let recipients = get_recipients(self.header.iter());
|
||||||
if recipients.len() == 1 && recipients.contains(from_addr_norm) {
|
if recipients.len() == 1 && recipients.contains(from_addr_norm) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -774,8 +790,6 @@ impl<'a> MimeParser<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn process_report(&self, report: &mailparse::ParsedMail<'_>) -> Result<Option<Report>> {
|
fn process_report(&self, report: &mailparse::ParsedMail<'_>) -> Result<Option<Report>> {
|
||||||
use mailparse::MailHeaderMap;
|
|
||||||
|
|
||||||
let ct = report.get_content_disposition()?;
|
let ct = report.get_content_disposition()?;
|
||||||
let report_type = ct.params.get("report-type");
|
let report_type = ct.params.get("report-type");
|
||||||
if report_type.is_none() {
|
if report_type.is_none() {
|
||||||
@@ -853,6 +867,55 @@ impl<'a> MimeParser<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn update_gossip_peerstates(
|
||||||
|
context: &Context,
|
||||||
|
message_time: i64,
|
||||||
|
mail: &mailparse::ParsedMail<'_>,
|
||||||
|
gossip_headers: Vec<String>,
|
||||||
|
) -> Result<HashSet<String>> {
|
||||||
|
// XXX split the parsing from the modification part
|
||||||
|
let mut recipients: Option<HashSet<String>> = None;
|
||||||
|
let mut gossipped_addr: HashSet<String> = Default::default();
|
||||||
|
|
||||||
|
for value in &gossip_headers {
|
||||||
|
let gossip_header = value.parse::<Aheader>();
|
||||||
|
|
||||||
|
if let Ok(ref header) = gossip_header {
|
||||||
|
if recipients.is_none() {
|
||||||
|
recipients = Some(get_recipients(mail.headers.iter().map(|v| {
|
||||||
|
// TODO: error handling
|
||||||
|
(v.get_key().unwrap(), v.get_value().unwrap())
|
||||||
|
})));
|
||||||
|
}
|
||||||
|
if recipients.as_ref().unwrap().contains(&header.addr) {
|
||||||
|
let mut peerstate = Peerstate::from_addr(context, &context.sql, &header.addr);
|
||||||
|
if let Some(ref mut peerstate) = peerstate {
|
||||||
|
peerstate.apply_gossip(header, message_time);
|
||||||
|
peerstate.save_to_db(&context.sql, false)?;
|
||||||
|
} else {
|
||||||
|
let p = Peerstate::from_gossip(context, header, message_time);
|
||||||
|
p.save_to_db(&context.sql, true)?;
|
||||||
|
peerstate = Some(p);
|
||||||
|
}
|
||||||
|
if let Some(peerstate) = peerstate {
|
||||||
|
if peerstate.degrade_event.is_some() {
|
||||||
|
handle_degrade_event(context, &peerstate)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gossipped_addr.insert(header.addr.clone());
|
||||||
|
} else {
|
||||||
|
info!(
|
||||||
|
context,
|
||||||
|
"Ignoring gossipped \"{}\" as the address is not in To/Cc list.", &header.addr,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(gossipped_addr)
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct Report {
|
struct Report {
|
||||||
original_message_id: String,
|
original_message_id: String,
|
||||||
@@ -967,6 +1030,35 @@ fn mailmime_is_attachment_disposition(mail: &mailparse::ParsedMail<'_>) -> bool
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// returned addresses are normalized.
|
||||||
|
fn get_recipients<'a, S: AsRef<str>, T: Iterator<Item = (S, S)>>(headers: T) -> HashSet<String> {
|
||||||
|
let mut recipients: HashSet<String> = Default::default();
|
||||||
|
|
||||||
|
for (hkey, hvalue) in headers {
|
||||||
|
let hkey = hkey.as_ref();
|
||||||
|
let hvalue = hvalue.as_ref();
|
||||||
|
|
||||||
|
if hkey == "to" || hkey == "cc" {
|
||||||
|
if let Ok(addrs) = mailparse::addrparse(hvalue) {
|
||||||
|
for addr in addrs.iter() {
|
||||||
|
match addr {
|
||||||
|
mailparse::MailAddr::Single(ref info) => {
|
||||||
|
recipients.insert(addr_normalize(&info.addr).into());
|
||||||
|
}
|
||||||
|
mailparse::MailAddr::Group(ref infos) => {
|
||||||
|
for info in &infos.addrs {
|
||||||
|
recipients.insert(addr_normalize(&info.addr).into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
recipients
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|||||||
@@ -337,325 +337,329 @@ impl Default for HandshakeMessageStatus {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* library private: secure-join */
|
/// Handle incoming secure-join handshake.
|
||||||
pub(crate) fn handle_securejoin_handshake(
|
pub(crate) fn handle_securejoin_handshake(
|
||||||
context: &Context,
|
context: &Context,
|
||||||
mimeparser: &MimeParser,
|
mimeparser: &MimeParser,
|
||||||
contact_id: u32,
|
contact_id: u32,
|
||||||
) -> Result<HandshakeMessageStatus, Error> {
|
) -> Result<HandshakeMessageStatus, Error> {
|
||||||
unimplemented!()
|
let own_fingerprint: String;
|
||||||
// let own_fingerprint: String;
|
|
||||||
|
|
||||||
// ensure!(
|
ensure!(
|
||||||
// contact_id > DC_CONTACT_ID_LAST_SPECIAL,
|
contact_id > DC_CONTACT_ID_LAST_SPECIAL,
|
||||||
// "handle_securejoin_handshake(): called with special contact id"
|
"handle_securejoin_handshake(): called with special contact id"
|
||||||
// );
|
);
|
||||||
// let step = match mimeparser.lookup_optional_field("Secure-Join") {
|
let step = mimeparser
|
||||||
// Some(s) => s,
|
.lookup_field("Secure-Join")
|
||||||
// None => {
|
.ok_or_else(|| format_err!("This message is not a Secure-Join message"))?;
|
||||||
// bail!("This message is not a Secure-Join message");
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
// info!(
|
|
||||||
// context,
|
|
||||||
// ">>>>>>>>>>>>>>>>>>>>>>>>> secure-join message \'{}\' received", step,
|
|
||||||
// );
|
|
||||||
// let (contact_chat_id, contact_chat_id_blocked) =
|
|
||||||
// chat::create_or_lookup_by_contact_id(context, contact_id, Blocked::Not).unwrap_or_default();
|
|
||||||
|
|
||||||
// if contact_chat_id_blocked != Blocked::Not {
|
info!(
|
||||||
// chat::unblock(context, contact_chat_id);
|
context,
|
||||||
// }
|
">>>>>>>>>>>>>>>>>>>>>>>>> secure-join message \'{}\' received", step,
|
||||||
// let join_vg = step.starts_with("vg-");
|
);
|
||||||
// let mut ret = HandshakeMessageStatus::default();
|
|
||||||
|
|
||||||
// match step.as_str() {
|
let (contact_chat_id, contact_chat_id_blocked) =
|
||||||
// "vg-request" | "vc-request" => {
|
chat::create_or_lookup_by_contact_id(context, contact_id, Blocked::Not).unwrap_or_default();
|
||||||
// /* =========================================================
|
|
||||||
// ==== Alice - the inviter side ====
|
|
||||||
// ==== Step 3 in "Setup verified contact" protocol ====
|
|
||||||
// ========================================================= */
|
|
||||||
// // this message may be unencrypted (Bob, the joinder and the sender, might not have Alice's key yet)
|
|
||||||
// // it just ensures, we have Bobs key now. If we do _not_ have the key because eg. MitM has removed it,
|
|
||||||
// // send_message() will fail with the error "End-to-end-encryption unavailable unexpectedly.", so, there is no additional check needed here.
|
|
||||||
// // verify that the `Secure-Join-Invitenumber:`-header matches invitenumber written to the QR code
|
|
||||||
// let invitenumber = match mimeparser.lookup_optional_field("Secure-Join-Invitenumber") {
|
|
||||||
// Some(n) => n,
|
|
||||||
// None => {
|
|
||||||
// warn!(context, "Secure-join denied (invitenumber missing).",);
|
|
||||||
// return Ok(ret);
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
// if !token::exists(context, token::Namespace::InviteNumber, &invitenumber) {
|
|
||||||
// warn!(context, "Secure-join denied (bad invitenumber).",);
|
|
||||||
// return Ok(ret);
|
|
||||||
// }
|
|
||||||
// info!(context, "Secure-join requested.",);
|
|
||||||
|
|
||||||
// inviter_progress!(context, contact_id, 300);
|
if contact_chat_id_blocked != Blocked::Not {
|
||||||
// send_handshake_msg(
|
chat::unblock(context, contact_chat_id);
|
||||||
// context,
|
}
|
||||||
// contact_chat_id,
|
|
||||||
// &format!("{}-auth-required", &step[..2]),
|
|
||||||
// "",
|
|
||||||
// None,
|
|
||||||
// "",
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
// "vg-auth-required" | "vc-auth-required" => {
|
|
||||||
// let cond = {
|
|
||||||
// let bob = context.bob.read().unwrap();
|
|
||||||
// let scan = bob.qr_scan.as_ref();
|
|
||||||
// scan.is_none()
|
|
||||||
// || bob.expects != DC_VC_AUTH_REQUIRED
|
|
||||||
// || join_vg && scan.unwrap().state != LotState::QrAskVerifyGroup
|
|
||||||
// };
|
|
||||||
|
|
||||||
// if cond {
|
let join_vg = step.starts_with("vg-");
|
||||||
// warn!(context, "auth-required message out of sync.",);
|
let mut ret = HandshakeMessageStatus::default();
|
||||||
// // no error, just aborted somehow or a mail from another handshake
|
|
||||||
// return Ok(ret);
|
|
||||||
// }
|
|
||||||
// let scanned_fingerprint_of_alice = get_qr_attr!(context, fingerprint).to_string();
|
|
||||||
// let auth = get_qr_attr!(context, auth).to_string();
|
|
||||||
|
|
||||||
// if !encrypted_and_signed(mimeparser, &scanned_fingerprint_of_alice) {
|
match step.as_str() {
|
||||||
// could_not_establish_secure_connection(
|
"vg-request" | "vc-request" => {
|
||||||
// context,
|
/* =========================================================
|
||||||
// contact_chat_id,
|
==== Alice - the inviter side ====
|
||||||
// if mimeparser.encrypted {
|
==== Step 3 in "Setup verified contact" protocol ====
|
||||||
// "No valid signature."
|
========================================================= */
|
||||||
// } else {
|
// this message may be unencrypted (Bob, the joinder and the sender, might not have Alice's key yet)
|
||||||
// "Not encrypted."
|
// it just ensures, we have Bobs key now. If we do _not_ have the key because eg. MitM has removed it,
|
||||||
// },
|
// send_message() will fail with the error "End-to-end-encryption unavailable unexpectedly.", so, there is no additional check needed here.
|
||||||
// );
|
// verify that the `Secure-Join-Invitenumber:`-header matches invitenumber written to the QR code
|
||||||
// ret.stop_ongoing_process = true;
|
let invitenumber = match mimeparser.lookup_field("Secure-Join-Invitenumber") {
|
||||||
// ret.bob_securejoin_success = Some(false);
|
Some(n) => n,
|
||||||
// return Ok(ret);
|
None => {
|
||||||
// }
|
warn!(context, "Secure-join denied (invitenumber missing).",);
|
||||||
// if !fingerprint_equals_sender(context, &scanned_fingerprint_of_alice, contact_chat_id) {
|
return Ok(ret);
|
||||||
// could_not_establish_secure_connection(
|
}
|
||||||
// context,
|
};
|
||||||
// contact_chat_id,
|
if !token::exists(context, token::Namespace::InviteNumber, &invitenumber) {
|
||||||
// "Fingerprint mismatch on joiner-side.",
|
warn!(context, "Secure-join denied (bad invitenumber).",);
|
||||||
// );
|
return Ok(ret);
|
||||||
// ret.stop_ongoing_process = true;
|
}
|
||||||
// ret.bob_securejoin_success = Some(false);
|
info!(context, "Secure-join requested.",);
|
||||||
// return Ok(ret);
|
|
||||||
// }
|
|
||||||
// info!(context, "Fingerprint verified.",);
|
|
||||||
// own_fingerprint = get_self_fingerprint(context).unwrap();
|
|
||||||
// joiner_progress!(context, contact_id, 400);
|
|
||||||
// context.bob.write().unwrap().expects = DC_VC_CONTACT_CONFIRM;
|
|
||||||
|
|
||||||
// send_handshake_msg(
|
inviter_progress!(context, contact_id, 300);
|
||||||
// context,
|
send_handshake_msg(
|
||||||
// contact_chat_id,
|
context,
|
||||||
// &format!("{}-request-with-auth", &step[..2]),
|
contact_chat_id,
|
||||||
// auth,
|
&format!("{}-auth-required", &step[..2]),
|
||||||
// Some(own_fingerprint),
|
"",
|
||||||
// if join_vg {
|
None,
|
||||||
// get_qr_attr!(context, text2).to_string()
|
"",
|
||||||
// } else {
|
);
|
||||||
// "".to_string()
|
}
|
||||||
// },
|
"vg-auth-required" | "vc-auth-required" => {
|
||||||
// );
|
let cond = {
|
||||||
// }
|
let bob = context.bob.read().unwrap();
|
||||||
// "vg-request-with-auth" | "vc-request-with-auth" => {
|
let scan = bob.qr_scan.as_ref();
|
||||||
// /* ============================================================
|
scan.is_none()
|
||||||
// ==== Alice - the inviter side ====
|
|| bob.expects != DC_VC_AUTH_REQUIRED
|
||||||
// ==== Steps 5+6 in "Setup verified contact" protocol ====
|
|| join_vg && scan.unwrap().state != LotState::QrAskVerifyGroup
|
||||||
// ==== Step 6 in "Out-of-band verified groups" protocol ====
|
};
|
||||||
// ============================================================ */
|
|
||||||
// // verify that Secure-Join-Fingerprint:-header matches the fingerprint of Bob
|
|
||||||
// let fingerprint = match mimeparser.lookup_optional_field("Secure-Join-Fingerprint") {
|
|
||||||
// Some(fp) => fp,
|
|
||||||
// None => {
|
|
||||||
// could_not_establish_secure_connection(
|
|
||||||
// context,
|
|
||||||
// contact_chat_id,
|
|
||||||
// "Fingerprint not provided.",
|
|
||||||
// );
|
|
||||||
// return Ok(ret);
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
// if !encrypted_and_signed(mimeparser, &fingerprint) {
|
|
||||||
// could_not_establish_secure_connection(
|
|
||||||
// context,
|
|
||||||
// contact_chat_id,
|
|
||||||
// "Auth not encrypted.",
|
|
||||||
// );
|
|
||||||
// return Ok(ret);
|
|
||||||
// }
|
|
||||||
// if !fingerprint_equals_sender(context, &fingerprint, contact_chat_id) {
|
|
||||||
// could_not_establish_secure_connection(
|
|
||||||
// context,
|
|
||||||
// contact_chat_id,
|
|
||||||
// "Fingerprint mismatch on inviter-side.",
|
|
||||||
// );
|
|
||||||
// return Ok(ret);
|
|
||||||
// }
|
|
||||||
// info!(context, "Fingerprint verified.",);
|
|
||||||
// // verify that the `Secure-Join-Auth:`-header matches the secret written to the QR code
|
|
||||||
// let auth_0 = match mimeparser.lookup_optional_field("Secure-Join-Auth") {
|
|
||||||
// Some(auth) => auth,
|
|
||||||
// None => {
|
|
||||||
// could_not_establish_secure_connection(
|
|
||||||
// context,
|
|
||||||
// contact_chat_id,
|
|
||||||
// "Auth not provided.",
|
|
||||||
// );
|
|
||||||
// return Ok(ret);
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
// if !token::exists(context, token::Namespace::Auth, &auth_0) {
|
|
||||||
// could_not_establish_secure_connection(context, contact_chat_id, "Auth invalid.");
|
|
||||||
// return Ok(ret);
|
|
||||||
// }
|
|
||||||
// if mark_peer_as_verified(context, fingerprint).is_err() {
|
|
||||||
// could_not_establish_secure_connection(
|
|
||||||
// context,
|
|
||||||
// contact_chat_id,
|
|
||||||
// "Fingerprint mismatch on inviter-side.",
|
|
||||||
// );
|
|
||||||
// return Ok(ret);
|
|
||||||
// }
|
|
||||||
// Contact::scaleup_origin_by_id(context, contact_id, Origin::SecurejoinInvited);
|
|
||||||
// info!(context, "Auth verified.",);
|
|
||||||
// secure_connection_established(context, contact_chat_id);
|
|
||||||
// emit_event!(context, Event::ContactsChanged(Some(contact_id)));
|
|
||||||
// inviter_progress!(context, contact_id, 600);
|
|
||||||
// if join_vg {
|
|
||||||
// let field_grpid = mimeparser
|
|
||||||
// .lookup_optional_field("Secure-Join-Group")
|
|
||||||
// .unwrap_or_default();
|
|
||||||
// let (group_chat_id, _, _) = chat::get_chat_id_by_grpid(context, &field_grpid);
|
|
||||||
// if group_chat_id == 0 {
|
|
||||||
// error!(context, "Chat {} not found.", &field_grpid);
|
|
||||||
// return Ok(ret);
|
|
||||||
// } else {
|
|
||||||
// if let Err(err) =
|
|
||||||
// chat::add_contact_to_chat_ex(context, group_chat_id, contact_id, true)
|
|
||||||
// {
|
|
||||||
// error!(context, "failed to add contact: {}", err);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// send_handshake_msg(context, contact_chat_id, "vc-contact-confirm", "", None, "");
|
|
||||||
// inviter_progress!(context, contact_id, 1000);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// "vg-member-added" | "vc-contact-confirm" => {
|
|
||||||
// if join_vg {
|
|
||||||
// ret.hide_this_msg = false;
|
|
||||||
// }
|
|
||||||
// if context.bob.read().unwrap().expects != DC_VC_CONTACT_CONFIRM {
|
|
||||||
// info!(context, "Message belongs to a different handshake.",);
|
|
||||||
// return Ok(ret);
|
|
||||||
// }
|
|
||||||
// let cond = {
|
|
||||||
// let bob = context.bob.read().unwrap();
|
|
||||||
// let scan = bob.qr_scan.as_ref();
|
|
||||||
// scan.is_none() || join_vg && scan.unwrap().state != LotState::QrAskVerifyGroup
|
|
||||||
// };
|
|
||||||
// if cond {
|
|
||||||
// warn!(
|
|
||||||
// context,
|
|
||||||
// "Message out of sync or belongs to a different handshake.",
|
|
||||||
// );
|
|
||||||
// return Ok(ret);
|
|
||||||
// }
|
|
||||||
// let scanned_fingerprint_of_alice = get_qr_attr!(context, fingerprint).to_string();
|
|
||||||
|
|
||||||
// let vg_expect_encrypted = if join_vg {
|
if cond {
|
||||||
// let group_id = get_qr_attr!(context, text2).to_string();
|
warn!(context, "auth-required message out of sync.",);
|
||||||
// let (_, is_verified_group, _) = chat::get_chat_id_by_grpid(context, group_id);
|
// no error, just aborted somehow or a mail from another handshake
|
||||||
// // when joining a non-verified group
|
return Ok(ret);
|
||||||
// // the vg-member-added message may be unencrypted
|
}
|
||||||
// // when not all group members have keys or prefer encryption.
|
let scanned_fingerprint_of_alice = get_qr_attr!(context, fingerprint).to_string();
|
||||||
// // So only expect encryption if this is a verified group
|
let auth = get_qr_attr!(context, auth).to_string();
|
||||||
// is_verified_group
|
|
||||||
// } else {
|
|
||||||
// // setup contact is always encrypted
|
|
||||||
// true
|
|
||||||
// };
|
|
||||||
// if vg_expect_encrypted
|
|
||||||
// && !encrypted_and_signed(mimeparser, &scanned_fingerprint_of_alice)
|
|
||||||
// {
|
|
||||||
// could_not_establish_secure_connection(
|
|
||||||
// context,
|
|
||||||
// contact_chat_id,
|
|
||||||
// "Contact confirm message not encrypted.",
|
|
||||||
// );
|
|
||||||
// ret.bob_securejoin_success = Some(false);
|
|
||||||
// return Ok(ret);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if mark_peer_as_verified(context, &scanned_fingerprint_of_alice).is_err() {
|
if !encrypted_and_signed(mimeparser, &scanned_fingerprint_of_alice) {
|
||||||
// could_not_establish_secure_connection(
|
could_not_establish_secure_connection(
|
||||||
// context,
|
context,
|
||||||
// contact_chat_id,
|
contact_chat_id,
|
||||||
// "Fingerprint mismatch on joiner-side.",
|
if mimeparser.encrypted {
|
||||||
// );
|
"No valid signature."
|
||||||
// return Ok(ret);
|
} else {
|
||||||
// }
|
"Not encrypted."
|
||||||
// Contact::scaleup_origin_by_id(context, contact_id, Origin::SecurejoinJoined);
|
},
|
||||||
// emit_event!(context, Event::ContactsChanged(None));
|
);
|
||||||
// let cg_member_added = mimeparser
|
ret.stop_ongoing_process = true;
|
||||||
// .lookup_optional_field("Chat-Group-Member-Added")
|
ret.bob_securejoin_success = Some(false);
|
||||||
// .unwrap_or_default();
|
return Ok(ret);
|
||||||
// if join_vg && !addr_equals_self(context, cg_member_added) {
|
}
|
||||||
// info!(context, "Message belongs to a different handshake (scaled up contact anyway to allow creation of group).");
|
if !fingerprint_equals_sender(context, &scanned_fingerprint_of_alice, contact_chat_id) {
|
||||||
// return Ok(ret);
|
could_not_establish_secure_connection(
|
||||||
// }
|
context,
|
||||||
// secure_connection_established(context, contact_chat_id);
|
contact_chat_id,
|
||||||
// context.bob.write().unwrap().expects = 0;
|
"Fingerprint mismatch on joiner-side.",
|
||||||
// if join_vg {
|
);
|
||||||
// send_handshake_msg(
|
ret.stop_ongoing_process = true;
|
||||||
// context,
|
ret.bob_securejoin_success = Some(false);
|
||||||
// contact_chat_id,
|
return Ok(ret);
|
||||||
// "vg-member-added-received",
|
}
|
||||||
// "",
|
info!(context, "Fingerprint verified.",);
|
||||||
// None,
|
own_fingerprint = get_self_fingerprint(context).unwrap();
|
||||||
// "",
|
joiner_progress!(context, contact_id, 400);
|
||||||
// );
|
context.bob.write().unwrap().expects = DC_VC_CONTACT_CONFIRM;
|
||||||
// }
|
|
||||||
// ret.stop_ongoing_process = true;
|
send_handshake_msg(
|
||||||
// ret.bob_securejoin_success = Some(true);
|
context,
|
||||||
// }
|
contact_chat_id,
|
||||||
// "vg-member-added-received" => {
|
&format!("{}-request-with-auth", &step[..2]),
|
||||||
// /* ============================================================
|
auth,
|
||||||
// ==== Alice - the inviter side ====
|
Some(own_fingerprint),
|
||||||
// ==== Step 8 in "Out-of-band verified groups" protocol ====
|
if join_vg {
|
||||||
// ============================================================ */
|
get_qr_attr!(context, text2).to_string()
|
||||||
// if let Ok(contact) = Contact::get_by_id(context, contact_id) {
|
} else {
|
||||||
// if contact.is_verified(context) == VerifiedStatus::Unverified {
|
"".to_string()
|
||||||
// warn!(context, "vg-member-added-received invalid.",);
|
},
|
||||||
// return Ok(ret);
|
);
|
||||||
// }
|
}
|
||||||
// inviter_progress!(context, contact_id, 800);
|
"vg-request-with-auth" | "vc-request-with-auth" => {
|
||||||
// inviter_progress!(context, contact_id, 1000);
|
/* ============================================================
|
||||||
// let field_grpid = mimeparser
|
==== Alice - the inviter side ====
|
||||||
// .lookup_optional_field("Secure-Join-Group")
|
==== Steps 5+6 in "Setup verified contact" protocol ====
|
||||||
// .unwrap_or_default();
|
==== Step 6 in "Out-of-band verified groups" protocol ====
|
||||||
// let (group_chat_id, _, _) = chat::get_chat_id_by_grpid(context, &field_grpid);
|
============================================================ */
|
||||||
// context.call_cb(Event::SecurejoinMemberAdded {
|
// verify that Secure-Join-Fingerprint:-header matches the fingerprint of Bob
|
||||||
// chat_id: group_chat_id,
|
let fingerprint = match mimeparser.lookup_field("Secure-Join-Fingerprint") {
|
||||||
// contact_id: contact_id,
|
Some(fp) => fp,
|
||||||
// });
|
None => {
|
||||||
// } else {
|
could_not_establish_secure_connection(
|
||||||
// warn!(context, "vg-member-added-received invalid.",);
|
context,
|
||||||
// return Ok(ret);
|
contact_chat_id,
|
||||||
// }
|
"Fingerprint not provided.",
|
||||||
// }
|
);
|
||||||
// _ => {
|
return Ok(ret);
|
||||||
// warn!(context, "invalid step: {}", step);
|
}
|
||||||
// }
|
};
|
||||||
// }
|
if !encrypted_and_signed(mimeparser, &fingerprint) {
|
||||||
// if ret.hide_this_msg {
|
could_not_establish_secure_connection(
|
||||||
// ret.delete_this_msg = true;
|
context,
|
||||||
// }
|
contact_chat_id,
|
||||||
// Ok(ret)
|
"Auth not encrypted.",
|
||||||
|
);
|
||||||
|
return Ok(ret);
|
||||||
|
}
|
||||||
|
if !fingerprint_equals_sender(context, &fingerprint, contact_chat_id) {
|
||||||
|
could_not_establish_secure_connection(
|
||||||
|
context,
|
||||||
|
contact_chat_id,
|
||||||
|
"Fingerprint mismatch on inviter-side.",
|
||||||
|
);
|
||||||
|
return Ok(ret);
|
||||||
|
}
|
||||||
|
info!(context, "Fingerprint verified.",);
|
||||||
|
// verify that the `Secure-Join-Auth:`-header matches the secret written to the QR code
|
||||||
|
let auth_0 = match mimeparser.lookup_field("Secure-Join-Auth") {
|
||||||
|
Some(auth) => auth,
|
||||||
|
None => {
|
||||||
|
could_not_establish_secure_connection(
|
||||||
|
context,
|
||||||
|
contact_chat_id,
|
||||||
|
"Auth not provided.",
|
||||||
|
);
|
||||||
|
return Ok(ret);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if !token::exists(context, token::Namespace::Auth, &auth_0) {
|
||||||
|
could_not_establish_secure_connection(context, contact_chat_id, "Auth invalid.");
|
||||||
|
return Ok(ret);
|
||||||
|
}
|
||||||
|
if mark_peer_as_verified(context, fingerprint).is_err() {
|
||||||
|
could_not_establish_secure_connection(
|
||||||
|
context,
|
||||||
|
contact_chat_id,
|
||||||
|
"Fingerprint mismatch on inviter-side.",
|
||||||
|
);
|
||||||
|
return Ok(ret);
|
||||||
|
}
|
||||||
|
Contact::scaleup_origin_by_id(context, contact_id, Origin::SecurejoinInvited);
|
||||||
|
info!(context, "Auth verified.",);
|
||||||
|
secure_connection_established(context, contact_chat_id);
|
||||||
|
emit_event!(context, Event::ContactsChanged(Some(contact_id)));
|
||||||
|
inviter_progress!(context, contact_id, 600);
|
||||||
|
if join_vg {
|
||||||
|
let field_grpid = mimeparser
|
||||||
|
.lookup_field("Secure-Join-Group")
|
||||||
|
.map(|s| s.as_str())
|
||||||
|
.unwrap_or_else(|| "");
|
||||||
|
let (group_chat_id, _, _) = chat::get_chat_id_by_grpid(context, field_grpid);
|
||||||
|
if group_chat_id == 0 {
|
||||||
|
error!(context, "Chat {} not found.", &field_grpid);
|
||||||
|
return Ok(ret);
|
||||||
|
} else {
|
||||||
|
if let Err(err) =
|
||||||
|
chat::add_contact_to_chat_ex(context, group_chat_id, contact_id, true)
|
||||||
|
{
|
||||||
|
error!(context, "failed to add contact: {}", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
send_handshake_msg(context, contact_chat_id, "vc-contact-confirm", "", None, "");
|
||||||
|
inviter_progress!(context, contact_id, 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"vg-member-added" | "vc-contact-confirm" => {
|
||||||
|
if join_vg {
|
||||||
|
ret.hide_this_msg = false;
|
||||||
|
}
|
||||||
|
if context.bob.read().unwrap().expects != DC_VC_CONTACT_CONFIRM {
|
||||||
|
info!(context, "Message belongs to a different handshake.",);
|
||||||
|
return Ok(ret);
|
||||||
|
}
|
||||||
|
let cond = {
|
||||||
|
let bob = context.bob.read().unwrap();
|
||||||
|
let scan = bob.qr_scan.as_ref();
|
||||||
|
scan.is_none() || join_vg && scan.unwrap().state != LotState::QrAskVerifyGroup
|
||||||
|
};
|
||||||
|
if cond {
|
||||||
|
warn!(
|
||||||
|
context,
|
||||||
|
"Message out of sync or belongs to a different handshake.",
|
||||||
|
);
|
||||||
|
return Ok(ret);
|
||||||
|
}
|
||||||
|
let scanned_fingerprint_of_alice = get_qr_attr!(context, fingerprint).to_string();
|
||||||
|
|
||||||
|
let vg_expect_encrypted = if join_vg {
|
||||||
|
let group_id = get_qr_attr!(context, text2).to_string();
|
||||||
|
let (_, is_verified_group, _) = chat::get_chat_id_by_grpid(context, group_id);
|
||||||
|
// when joining a non-verified group
|
||||||
|
// the vg-member-added message may be unencrypted
|
||||||
|
// when not all group members have keys or prefer encryption.
|
||||||
|
// So only expect encryption if this is a verified group
|
||||||
|
is_verified_group
|
||||||
|
} else {
|
||||||
|
// setup contact is always encrypted
|
||||||
|
true
|
||||||
|
};
|
||||||
|
if vg_expect_encrypted
|
||||||
|
&& !encrypted_and_signed(mimeparser, &scanned_fingerprint_of_alice)
|
||||||
|
{
|
||||||
|
could_not_establish_secure_connection(
|
||||||
|
context,
|
||||||
|
contact_chat_id,
|
||||||
|
"Contact confirm message not encrypted.",
|
||||||
|
);
|
||||||
|
ret.bob_securejoin_success = Some(false);
|
||||||
|
return Ok(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
if mark_peer_as_verified(context, &scanned_fingerprint_of_alice).is_err() {
|
||||||
|
could_not_establish_secure_connection(
|
||||||
|
context,
|
||||||
|
contact_chat_id,
|
||||||
|
"Fingerprint mismatch on joiner-side.",
|
||||||
|
);
|
||||||
|
return Ok(ret);
|
||||||
|
}
|
||||||
|
Contact::scaleup_origin_by_id(context, contact_id, Origin::SecurejoinJoined);
|
||||||
|
emit_event!(context, Event::ContactsChanged(None));
|
||||||
|
let cg_member_added = mimeparser
|
||||||
|
.lookup_field("Chat-Group-Member-Added")
|
||||||
|
.map(|s| s.as_str())
|
||||||
|
.unwrap_or_else(|| "");
|
||||||
|
if join_vg && !addr_equals_self(context, cg_member_added) {
|
||||||
|
info!(context, "Message belongs to a different handshake (scaled up contact anyway to allow creation of group).");
|
||||||
|
return Ok(ret);
|
||||||
|
}
|
||||||
|
secure_connection_established(context, contact_chat_id);
|
||||||
|
context.bob.write().unwrap().expects = 0;
|
||||||
|
if join_vg {
|
||||||
|
send_handshake_msg(
|
||||||
|
context,
|
||||||
|
contact_chat_id,
|
||||||
|
"vg-member-added-received",
|
||||||
|
"",
|
||||||
|
None,
|
||||||
|
"",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
ret.stop_ongoing_process = true;
|
||||||
|
ret.bob_securejoin_success = Some(true);
|
||||||
|
}
|
||||||
|
"vg-member-added-received" => {
|
||||||
|
/* ============================================================
|
||||||
|
==== Alice - the inviter side ====
|
||||||
|
==== Step 8 in "Out-of-band verified groups" protocol ====
|
||||||
|
============================================================ */
|
||||||
|
if let Ok(contact) = Contact::get_by_id(context, contact_id) {
|
||||||
|
if contact.is_verified(context) == VerifiedStatus::Unverified {
|
||||||
|
warn!(context, "vg-member-added-received invalid.",);
|
||||||
|
return Ok(ret);
|
||||||
|
}
|
||||||
|
inviter_progress!(context, contact_id, 800);
|
||||||
|
inviter_progress!(context, contact_id, 1000);
|
||||||
|
let field_grpid = mimeparser
|
||||||
|
.lookup_field("Secure-Join-Group")
|
||||||
|
.map(|s| s.as_str())
|
||||||
|
.unwrap_or_else(|| "");
|
||||||
|
let (group_chat_id, _, _) = chat::get_chat_id_by_grpid(context, &field_grpid);
|
||||||
|
context.call_cb(Event::SecurejoinMemberAdded {
|
||||||
|
chat_id: group_chat_id,
|
||||||
|
contact_id: contact_id,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
warn!(context, "vg-member-added-received invalid.",);
|
||||||
|
return Ok(ret);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
warn!(context, "invalid step: {}", step);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ret.hide_this_msg {
|
||||||
|
ret.delete_this_msg = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(ret)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn secure_connection_established(context: &Context, contact_chat_id: u32) {
|
fn secure_connection_established(context: &Context, contact_chat_id: u32) {
|
||||||
|
|||||||
183
src/wrapmime.rs
183
src/wrapmime.rs
@@ -1,14 +1,7 @@
|
|||||||
use std::collections::{HashMap, HashSet};
|
|
||||||
|
|
||||||
use mailparse::ParsedMail;
|
use mailparse::ParsedMail;
|
||||||
|
|
||||||
use crate::contact::addr_normalize;
|
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
|
|
||||||
/**************************************
|
|
||||||
* mime parsing API
|
|
||||||
**************************************/
|
|
||||||
|
|
||||||
pub fn parse_message_id(message_id: &[u8]) -> Result<String, Error> {
|
pub fn parse_message_id(message_id: &[u8]) -> Result<String, Error> {
|
||||||
let value = std::str::from_utf8(message_id)?;
|
let value = std::str::from_utf8(message_id)?;
|
||||||
let addrs = mailparse::addrparse(value)
|
let addrs = mailparse::addrparse(value)
|
||||||
@@ -18,10 +11,7 @@ pub fn parse_message_id(message_id: &[u8]) -> Result<String, Error> {
|
|||||||
return Ok(info.addr);
|
return Ok(info.addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
bail!(
|
bail!("could not parse message_id: {}", value);
|
||||||
"could not parse message_id: {}",
|
|
||||||
String::from_utf8_lossy(message_id)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a reference to the encrypted payload and validates the autocrypt structure.
|
/// Returns a reference to the encrypted payload and validates the autocrypt structure.
|
||||||
@@ -48,181 +38,10 @@ pub fn get_autocrypt_mime<'a, 'b>(mail: &'a ParsedMail<'b>) -> Result<&'a Parsed
|
|||||||
Ok(&mail.subparts[1])
|
Ok(&mail.subparts[1])
|
||||||
}
|
}
|
||||||
|
|
||||||
// returned addresses are normalized.
|
|
||||||
pub fn mailimf_get_recipients(headers: &HashMap<String, String>) -> HashSet<String> {
|
|
||||||
let mut recipients: HashSet<String> = Default::default();
|
|
||||||
|
|
||||||
for (hkey, hvalue) in headers.iter() {
|
|
||||||
if hkey == "to" || hkey == "cc" {
|
|
||||||
if let Ok(addrs) = mailparse::addrparse(hvalue) {
|
|
||||||
for addr in addrs.iter() {
|
|
||||||
match addr {
|
|
||||||
mailparse::MailAddr::Single(ref info) => {
|
|
||||||
recipients.insert(addr_normalize(&info.addr).into());
|
|
||||||
}
|
|
||||||
mailparse::MailAddr::Group(ref infos) => {
|
|
||||||
for info in &infos.addrs {
|
|
||||||
recipients.insert(addr_normalize(&info.addr).into());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
recipients
|
|
||||||
}
|
|
||||||
|
|
||||||
/**************************************
|
|
||||||
* mime creation API
|
|
||||||
**************************************/
|
|
||||||
|
|
||||||
// pub fn add_filename_part(
|
|
||||||
// message: *mut Mailmime,
|
|
||||||
// basename: &str,
|
|
||||||
// mime_type: &str,
|
|
||||||
// file_content: &str,
|
|
||||||
// ) -> Result<(), Error> {
|
|
||||||
// let mime_type_c = CString::new(mime_type.to_string()).expect("failed to create CString");
|
|
||||||
// {
|
|
||||||
// let content_type = mailmime_content_new_with_str(mime_type_c.as_ptr());
|
|
||||||
// let mime_fields = mailmime_fields_new_filename(
|
|
||||||
// MAILMIME_DISPOSITION_TYPE_ATTACHMENT as libc::c_int,
|
|
||||||
// basename.strdup(),
|
|
||||||
// MAILMIME_MECHANISM_8BIT as libc::c_int,
|
|
||||||
// );
|
|
||||||
// let file_mime_part = mailmime_new_empty(content_type, mime_fields);
|
|
||||||
// set_body_text(file_mime_part, file_content)?;
|
|
||||||
// mailmime_smart_add_part(message, file_mime_part);
|
|
||||||
// }
|
|
||||||
// Ok(())
|
|
||||||
// }
|
|
||||||
|
|
||||||
// pub fn new_custom_field(fields: *mut mailimf_fields, name: &str, value: &str) {
|
|
||||||
// {
|
|
||||||
// let field = mailimf_field_new_custom(name.strdup(), value.strdup());
|
|
||||||
// let res = mailimf_fields_add(fields, field);
|
|
||||||
// assert!(
|
|
||||||
// res as u32 == MAILIMF_NO_ERROR,
|
|
||||||
// "could not create mailimf field"
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// pub fn build_body_text(text: &str) -> Result<*mut Mailmime, Error> {
|
|
||||||
// let mime_fields: *mut mailmime_fields;
|
|
||||||
// let message_part: *mut Mailmime;
|
|
||||||
|
|
||||||
// let content = new_content_type("text/plain")?;
|
|
||||||
// append_ct_param(content, "charset", "utf-8")?;
|
|
||||||
|
|
||||||
// {
|
|
||||||
// mime_fields = mailmime_fields_new_encoding(MAILMIME_MECHANISM_8BIT as libc::c_int);
|
|
||||||
// message_part = mailmime_new_empty(content, mime_fields);
|
|
||||||
// }
|
|
||||||
// set_body_text(message_part, text)?;
|
|
||||||
|
|
||||||
// Ok(message_part)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// pub fn append_ct_param(
|
|
||||||
// content: *mut mailmime_content,
|
|
||||||
// name: &str,
|
|
||||||
// value: &str,
|
|
||||||
// ) -> Result<(), Error> {
|
|
||||||
// {
|
|
||||||
// let name_c = CString::new(name).unwrap_or_default();
|
|
||||||
// let value_c = CString::new(value).unwrap_or_default();
|
|
||||||
|
|
||||||
// clist_append!(
|
|
||||||
// (*content).ct_parameters,
|
|
||||||
// mailmime_param_new_with_data(
|
|
||||||
// name_c.as_ptr() as *const u8 as *const libc::c_char as *mut libc::c_char,
|
|
||||||
// value_c.as_ptr() as *const u8 as *const libc::c_char as *mut libc::c_char
|
|
||||||
// )
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
// Ok(())
|
|
||||||
// }
|
|
||||||
|
|
||||||
// pub fn new_content_type(content_type: &str) -> Result<*mut mailmime_content, Error> {
|
|
||||||
// let ct = CString::new(content_type).unwrap_or_default();
|
|
||||||
// let content: *mut mailmime_content;
|
|
||||||
// // mailmime_content_new_with_str only parses but does not retain/own ct
|
|
||||||
// {
|
|
||||||
// content = mailmime_content_new_with_str(ct.as_ptr());
|
|
||||||
// }
|
|
||||||
// ensure!(!content.is_null(), "mailimf failed to allocate");
|
|
||||||
// Ok(content)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// pub fn set_body_text(part: *mut Mailmime, text: &str) -> Result<(), Error> {
|
|
||||||
// use libc::strlen;
|
|
||||||
// {
|
|
||||||
// let text_c = text.strdup();
|
|
||||||
// if 0 != mailmime_set_body_text(part, text_c, strlen(text_c)) {
|
|
||||||
// bail!("could not set body text on mime-structure");
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// Ok(())
|
|
||||||
// }
|
|
||||||
|
|
||||||
// pub fn content_type_needs_encoding(content: *const mailmime_content) -> bool {
|
|
||||||
// {
|
|
||||||
// if (*(*content).ct_type).tp_type == MAILMIME_TYPE_COMPOSITE_TYPE as libc::c_int {
|
|
||||||
// let composite = (*(*content).ct_type).tp_data.tp_composite_type;
|
|
||||||
// match (*composite).ct_type as u32 {
|
|
||||||
// MAILMIME_COMPOSITE_TYPE_MESSAGE => {
|
|
||||||
// to_string_lossy((*content).ct_subtype) != "rfc822"
|
|
||||||
// }
|
|
||||||
// MAILMIME_COMPOSITE_TYPE_MULTIPART => false,
|
|
||||||
// _ => false,
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// true
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// pub fn new_mailbox_list(displayname: &str, addr: &str) -> *mut mailimf_mailbox_list {
|
|
||||||
// let mbox: *mut mailimf_mailbox_list = { mailimf_mailbox_list_new_empty() };
|
|
||||||
// {
|
|
||||||
// mailimf_mailbox_list_add(
|
|
||||||
// mbox,
|
|
||||||
// mailimf_mailbox_new(
|
|
||||||
// if !displayname.is_empty() {
|
|
||||||
// dc_encode_header_words(&displayname).strdup()
|
|
||||||
// } else {
|
|
||||||
// ptr::null_mut()
|
|
||||||
// },
|
|
||||||
// addr.strdup(),
|
|
||||||
// ),
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
// mbox
|
|
||||||
// }
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
// #[test]
|
|
||||||
// fn test_needs_encoding() {
|
|
||||||
// assert!(content_type_needs_encoding(
|
|
||||||
// new_content_type("text/plain").unwrap()
|
|
||||||
// ));
|
|
||||||
// assert!(content_type_needs_encoding(
|
|
||||||
// new_content_type("application/octect-stream").unwrap()
|
|
||||||
// ));
|
|
||||||
// assert!(!content_type_needs_encoding(
|
|
||||||
// new_content_type("multipart/encrypted").unwrap()
|
|
||||||
// ));
|
|
||||||
// assert!(content_type_needs_encoding(
|
|
||||||
// new_content_type("application/pgp-encrypted").unwrap()
|
|
||||||
// ));
|
|
||||||
// }
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_message_id() {
|
fn test_parse_message_id() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|||||||
Reference in New Issue
Block a user