mirror of
https://github.com/chatmail/core.git
synced 2026-04-24 08:56:29 +03:00
remove mmime
This commit is contained in:
@@ -3,12 +3,9 @@
|
||||
//! Parse and create [Autocrypt-headers](https://autocrypt.org/en/latest/level1.html#the-autocrypt-header).
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::ffi::CStr;
|
||||
use std::str::FromStr;
|
||||
use std::{fmt, str};
|
||||
|
||||
use mmime::mailimf::types::*;
|
||||
|
||||
use crate::constants::*;
|
||||
use crate::contact::*;
|
||||
use crate::key::*;
|
||||
@@ -68,45 +65,21 @@ impl Aheader {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_imffields(wanted_from: &str, header: *const mailimf_fields) -> Option<Self> {
|
||||
if header.is_null() {
|
||||
return None;
|
||||
}
|
||||
pub fn from_imffields(
|
||||
wanted_from: &str,
|
||||
headers: &[mailparse::MailHeader<'_>],
|
||||
) -> Option<Self> {
|
||||
use mailparse::MailHeaderMap;
|
||||
|
||||
let mut fine_header = None;
|
||||
let mut cur = unsafe { (*(*header).fld_list).first };
|
||||
|
||||
while !cur.is_null() {
|
||||
let field = unsafe { (*cur).data as *mut mailimf_field };
|
||||
if !field.is_null()
|
||||
&& unsafe { (*field).fld_type } == MAILIMF_FIELD_OPTIONAL_FIELD as libc::c_int
|
||||
{
|
||||
let optional_field = unsafe { (*field).fld_data.fld_optional_field };
|
||||
if !optional_field.is_null()
|
||||
&& unsafe { !(*optional_field).fld_name.is_null() }
|
||||
&& unsafe { CStr::from_ptr((*optional_field).fld_name).to_string_lossy() }
|
||||
== "Autocrypt"
|
||||
{
|
||||
let value =
|
||||
unsafe { CStr::from_ptr((*optional_field).fld_value).to_string_lossy() };
|
||||
|
||||
if let Ok(test) = Self::from_str(&value) {
|
||||
if addr_cmp(&test.addr, wanted_from) {
|
||||
if fine_header.is_none() {
|
||||
fine_header = Some(test);
|
||||
} else {
|
||||
// TODO: figure out what kind of error case this is
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Ok(Some(value)) = headers.get_first_value("Autocrypt") {
|
||||
if let Ok(test) = Self::from_str(&value) {
|
||||
if addr_cmp(&test.addr, wanted_from) {
|
||||
return Some(test);
|
||||
}
|
||||
}
|
||||
|
||||
cur = unsafe { (*cur).next };
|
||||
}
|
||||
|
||||
fine_header
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,12 +11,12 @@ use crate::config::*;
|
||||
use crate::constants::*;
|
||||
use crate::contact::*;
|
||||
use crate::context::Context;
|
||||
use crate::dc_mimeparser::SystemMessage;
|
||||
use crate::dc_tools::*;
|
||||
use crate::error::Error;
|
||||
use crate::events::Event;
|
||||
use crate::job::*;
|
||||
use crate::message::{self, InvalidMsgId, Message, MessageState, MsgId};
|
||||
use crate::mimeparser::SystemMessage;
|
||||
use crate::param::*;
|
||||
use crate::sql::{self, Sql};
|
||||
use crate::stock::StockMessage;
|
||||
|
||||
1239
src/dc_mimeparser.rs
1239
src/dc_mimeparser.rs
File diff suppressed because it is too large
Load Diff
@@ -1,13 +1,4 @@
|
||||
use std::ptr;
|
||||
|
||||
use itertools::join;
|
||||
use libc::strcmp;
|
||||
use mmime::clist::*;
|
||||
use mmime::mailimf::types::*;
|
||||
use mmime::mailmime::content::*;
|
||||
use mmime::mailmime::types::*;
|
||||
use mmime::mailmime::*;
|
||||
use mmime::other::*;
|
||||
use sha2::{Digest, Sha256};
|
||||
|
||||
use num_traits::FromPrimitive;
|
||||
@@ -18,7 +9,6 @@ use crate::config::Config;
|
||||
use crate::constants::*;
|
||||
use crate::contact::*;
|
||||
use crate::context::Context;
|
||||
use crate::dc_mimeparser::*;
|
||||
use crate::dc_strencode::*;
|
||||
use crate::dc_tools::*;
|
||||
use crate::error::Result;
|
||||
@@ -26,12 +16,12 @@ use crate::events::Event;
|
||||
use crate::job::*;
|
||||
use crate::location;
|
||||
use crate::message::{self, MessageState, MsgId};
|
||||
use crate::mimeparser::*;
|
||||
use crate::param::*;
|
||||
use crate::peerstate::*;
|
||||
use crate::securejoin::handle_securejoin_handshake;
|
||||
use crate::sql;
|
||||
use crate::stock::StockMessage;
|
||||
use crate::wrapmime;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
enum CreateEvent {
|
||||
@@ -40,7 +30,7 @@ enum CreateEvent {
|
||||
}
|
||||
|
||||
/// Receive a message and add it to the database.
|
||||
pub unsafe fn dc_receive_imf(
|
||||
pub fn dc_receive_imf(
|
||||
context: &Context,
|
||||
imf_raw: &[u8],
|
||||
server_folder: impl AsRef<str>,
|
||||
@@ -63,9 +53,12 @@ pub unsafe fn dc_receive_imf(
|
||||
// we use mailmime_parse() through dc_mimeparser (both call mailimf_struct_multiple_parse()
|
||||
// somewhen, I did not found out anything that speaks against this approach yet)
|
||||
|
||||
let mut mime_parser = MimeParser::new(context);
|
||||
if let Err(err) = mime_parser.parse(imf_raw) {
|
||||
let mime_parser = MimeParser::from_bytes(context, imf_raw);
|
||||
let mut mime_parser = if let Err(err) = mime_parser {
|
||||
warn!(context, "dc_receive_imf parse error: {}", err);
|
||||
return;
|
||||
} else {
|
||||
mime_parser.unwrap()
|
||||
};
|
||||
|
||||
if mime_parser.header.is_empty() {
|
||||
@@ -77,7 +70,7 @@ pub unsafe fn dc_receive_imf(
|
||||
// the function returns the number of created messages in the database
|
||||
let mut incoming = 1;
|
||||
let mut incoming_origin = Origin::Unknown;
|
||||
let mut to_self = 0;
|
||||
let mut to_self = false;
|
||||
let mut from_id = 0u32;
|
||||
let mut from_id_blocked = 0;
|
||||
let mut to_id = 0u32;
|
||||
@@ -122,60 +115,54 @@ pub unsafe fn dc_receive_imf(
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(field) = mime_parser.lookup_field("Date") {
|
||||
if let Ok(value) = field.get_value() {
|
||||
// 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)
|
||||
// TODO
|
||||
}
|
||||
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.
|
||||
// sent_timestamp = dc_timestamp_from_date((*orig_date).dt_date_time)
|
||||
// TODO
|
||||
}
|
||||
|
||||
// get From: and check if it is known (for known From:'s we add the other To:/Cc: in the 3rd pass)
|
||||
// or if From: is equal to SELF (in this case, it is any outgoing messages,
|
||||
// we do not check Return-Path any more as this is unreliable, see issue #150
|
||||
if let Some(field) = mime_parser.lookup_field("From") {
|
||||
if let Ok(fld_from) = field.get_value() {
|
||||
let mut check_self = 0;
|
||||
// let mut from_list = Vec::with_capacity(16);
|
||||
// dc_add_or_lookup_contacts_by_mailbox_list(
|
||||
// context,
|
||||
// (*fld_from).frm_mb_list,
|
||||
// Origin::IncomingUnknownFrom,
|
||||
// &mut from_list,
|
||||
// &mut check_self,
|
||||
// );
|
||||
// if 0 != check_self {
|
||||
// incoming = 0;
|
||||
// if mime_parser.sender_equals_recipient() {
|
||||
// from_id = DC_CONTACT_ID_SELF;
|
||||
// }
|
||||
// } else if !from_list.is_empty() {
|
||||
// // if there is no from given, from_id stays 0 which is just fine. These messages
|
||||
// // are very rare, however, we have to add them to the database (they go to the
|
||||
// // "deaddrop" chat) to avoid a re-download from the server. See also [**]
|
||||
// from_id = from_list[0];
|
||||
// incoming_origin = Contact::get_origin_by_id(context, from_id, &mut from_id_blocked)
|
||||
// }
|
||||
if let Some(field_from) = mime_parser.lookup_field("From") {
|
||||
let mut check_self = false;
|
||||
let mut from_list = Vec::with_capacity(16);
|
||||
dc_add_or_lookup_contacts_by_address_list(
|
||||
context,
|
||||
&field_from,
|
||||
Origin::IncomingUnknownFrom,
|
||||
&mut from_list,
|
||||
&mut check_self,
|
||||
);
|
||||
if check_self {
|
||||
incoming = 0;
|
||||
if mime_parser.sender_equals_recipient() {
|
||||
from_id = DC_CONTACT_ID_SELF;
|
||||
}
|
||||
} else if !from_list.is_empty() {
|
||||
// if there is no from given, from_id stays 0 which is just fine. These messages
|
||||
// are very rare, however, we have to add them to the database (they go to the
|
||||
// "deaddrop" chat) to avoid a re-download from the server. See also [**]
|
||||
from_id = from_list[0];
|
||||
incoming_origin = Contact::get_origin_by_id(context, from_id, &mut from_id_blocked)
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure, to_ids starts with the first To:-address (Cc: is added in the loop below pass)
|
||||
if let Some(field) = mime_parser.lookup_field("To") {
|
||||
if let Ok(fld_to) = field.get_value() {
|
||||
// dc_add_or_lookup_contacts_by_address_list(
|
||||
// context,
|
||||
// (*fld_to).to_addr_list,
|
||||
// if 0 == incoming {
|
||||
// Origin::OutgoingTo
|
||||
// } else if incoming_origin.is_verified() {
|
||||
// Origin::IncomingTo
|
||||
// } else {
|
||||
// Origin::IncomingUnknownTo
|
||||
// },
|
||||
// &mut to_ids,
|
||||
// &mut to_self,
|
||||
// );
|
||||
}
|
||||
dc_add_or_lookup_contacts_by_address_list(
|
||||
context,
|
||||
&field,
|
||||
if 0 == incoming {
|
||||
Origin::OutgoingTo
|
||||
} else if incoming_origin.is_verified() {
|
||||
Origin::IncomingTo
|
||||
} else {
|
||||
Origin::IncomingUnknownTo
|
||||
},
|
||||
&mut to_ids,
|
||||
&mut to_self,
|
||||
);
|
||||
}
|
||||
|
||||
// Add parts
|
||||
@@ -243,17 +230,13 @@ pub unsafe fn dc_receive_imf(
|
||||
}
|
||||
}
|
||||
|
||||
if !mime_parser.reports.is_empty() {
|
||||
handle_reports(
|
||||
context,
|
||||
&mime_parser,
|
||||
from_id,
|
||||
sent_timestamp,
|
||||
&mut rr_event_to_send,
|
||||
&server_folder,
|
||||
server_uid,
|
||||
);
|
||||
}
|
||||
mime_parser.handle_reports(
|
||||
from_id,
|
||||
sent_timestamp,
|
||||
&mut rr_event_to_send,
|
||||
&server_folder,
|
||||
server_uid,
|
||||
);
|
||||
|
||||
if mime_parser.location_kml.is_some() || mime_parser.message_kml.is_some() {
|
||||
save_locations(
|
||||
@@ -292,7 +275,7 @@ pub unsafe fn dc_receive_imf(
|
||||
);
|
||||
}
|
||||
|
||||
unsafe fn add_parts(
|
||||
fn add_parts(
|
||||
context: &Context,
|
||||
mut mime_parser: &mut MimeParser,
|
||||
imf_raw: &[u8],
|
||||
@@ -310,7 +293,7 @@ unsafe fn add_parts(
|
||||
to_id: &mut u32,
|
||||
flags: u32,
|
||||
needs_delete_job: &mut bool,
|
||||
to_self: i32,
|
||||
to_self: bool,
|
||||
insert_msg_id: &mut MsgId,
|
||||
created_db_entries: &mut Vec<(usize, MsgId)>,
|
||||
create_event_to_send: &mut Option<CreateEvent>,
|
||||
@@ -326,22 +309,20 @@ unsafe fn add_parts(
|
||||
// collect the rest information, CC: is added to the to-list, BCC: is ignored
|
||||
// (we should not add BCC to groups as this would split groups. We could add them as "known contacts",
|
||||
// however, the benefit is very small and this may leak data that is expected to be hidden)
|
||||
if let Some(field) = mime_parser.lookup_field("Cc") {
|
||||
if let Ok(fld_cc) = field.get_value() {
|
||||
// dc_add_or_lookup_contacts_by_address_list(
|
||||
// context,
|
||||
// (*fld_cc).cc_addr_list,
|
||||
// if 0 == incoming {
|
||||
// Origin::OutgoingCc
|
||||
// } else if incoming_origin.is_verified() {
|
||||
// Origin::IncomingCc
|
||||
// } else {
|
||||
// Origin::IncomingUnknownCc
|
||||
// },
|
||||
// to_ids,
|
||||
// std::ptr::null_mut(),
|
||||
// );
|
||||
}
|
||||
if let Some(fld_cc) = mime_parser.lookup_field("Cc") {
|
||||
dc_add_or_lookup_contacts_by_address_list(
|
||||
context,
|
||||
fld_cc,
|
||||
if 0 == incoming {
|
||||
Origin::OutgoingCc
|
||||
} else if incoming_origin.is_verified() {
|
||||
Origin::IncomingCc
|
||||
} else {
|
||||
Origin::IncomingUnknownCc
|
||||
},
|
||||
to_ids,
|
||||
&mut false,
|
||||
);
|
||||
}
|
||||
|
||||
// check, if the mail is already in our database - if so, just update the folder/uid
|
||||
@@ -393,7 +374,7 @@ unsafe fn add_parts(
|
||||
|
||||
// handshake messages must be processed _before_ chats are created
|
||||
// (eg. contacs may be marked as verified)
|
||||
if mime_parser.lookup_field("Secure-Join").is_some() {
|
||||
if let Some(_) = mime_parser.lookup_field("Secure-Join") {
|
||||
// avoid discarding by show_emails setting
|
||||
msgrmsg = 1;
|
||||
*chat_id = 0;
|
||||
@@ -567,7 +548,7 @@ unsafe fn add_parts(
|
||||
}
|
||||
}
|
||||
if *chat_id == 0 {
|
||||
if to_ids.is_empty() && 0 != to_self {
|
||||
if to_ids.is_empty() && to_self {
|
||||
// from_id==to_id==DC_CONTACT_ID_SELF - this is a self-sent messages,
|
||||
// maybe an Autocrypt Setup Messag
|
||||
let (id, bl) =
|
||||
@@ -605,16 +586,12 @@ unsafe fn add_parts(
|
||||
// if the mime-headers should be saved, find out its size
|
||||
// (the mime-header ends with an empty line)
|
||||
let save_mime_headers = context.get_config_bool(Config::SaveMimeHeaders);
|
||||
if let Some(field) = mime_parser.lookup_field("In-Reply-To") {
|
||||
if let Ok(raw) = field.get_value() {
|
||||
mime_in_reply_to = raw;
|
||||
}
|
||||
if let Some(raw) = mime_parser.lookup_field("In-Reply-To") {
|
||||
mime_in_reply_to = raw.clone();
|
||||
}
|
||||
|
||||
if let Some(field) = mime_parser.lookup_field("References") {
|
||||
if let Ok(raw) = field.get_value() {
|
||||
mime_references = raw;
|
||||
}
|
||||
if let Some(raw) = mime_parser.lookup_field("References") {
|
||||
mime_references = raw.clone();
|
||||
}
|
||||
|
||||
// fine, so far. now, split the message into simple parts usable as "short messages"
|
||||
@@ -722,137 +699,6 @@ unsafe fn add_parts(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Handle reports (mainly MDNs)
|
||||
unsafe fn handle_reports(
|
||||
context: &Context,
|
||||
mime_parser: &MimeParser,
|
||||
from_id: u32,
|
||||
sent_timestamp: i64,
|
||||
rr_event_to_send: &mut Vec<(u32, MsgId)>,
|
||||
server_folder: impl AsRef<str>,
|
||||
server_uid: u32,
|
||||
) {
|
||||
let mdns_enabled = context.get_config_bool(Config::MdnsEnabled);
|
||||
|
||||
for report_root in &mime_parser.reports {
|
||||
let report_root = *report_root;
|
||||
let mut mdn_consumed = 0;
|
||||
let report_type = mailmime_find_ct_parameter(report_root, "report-type");
|
||||
|
||||
if report_root.is_null() || report_type.is_null() || (*report_type).pa_value.is_null() {
|
||||
continue;
|
||||
}
|
||||
|
||||
// the first part is for humans, the second for machines
|
||||
if strcmp(
|
||||
(*report_type).pa_value,
|
||||
b"disposition-notification\x00" as *const u8 as *const libc::c_char,
|
||||
) == 0
|
||||
&& (*(*report_root).mm_data.mm_multipart.mm_mp_list).count >= 2
|
||||
{
|
||||
// to get a clear functionality, do not show incoming MDNs if the options is disabled
|
||||
if mdns_enabled {
|
||||
let report_data = (if !if !(*(*report_root).mm_data.mm_multipart.mm_mp_list)
|
||||
.first
|
||||
.is_null()
|
||||
{
|
||||
(*(*(*report_root).mm_data.mm_multipart.mm_mp_list).first).next
|
||||
} else {
|
||||
ptr::null_mut()
|
||||
}
|
||||
.is_null()
|
||||
{
|
||||
(*if !(*(*report_root).mm_data.mm_multipart.mm_mp_list)
|
||||
.first
|
||||
.is_null()
|
||||
{
|
||||
(*(*(*report_root).mm_data.mm_multipart.mm_mp_list).first).next
|
||||
} else {
|
||||
ptr::null_mut()
|
||||
})
|
||||
.data
|
||||
} else {
|
||||
ptr::null_mut()
|
||||
}) as *mut Mailmime;
|
||||
|
||||
if !report_data.is_null()
|
||||
&& (*(*(*report_data).mm_content_type).ct_type).tp_type
|
||||
== MAILMIME_TYPE_COMPOSITE_TYPE as libc::c_int
|
||||
&& (*(*(*(*report_data).mm_content_type).ct_type)
|
||||
.tp_data
|
||||
.tp_composite_type)
|
||||
.ct_type
|
||||
== MAILMIME_COMPOSITE_TYPE_MESSAGE as libc::c_int
|
||||
&& strcmp(
|
||||
(*(*report_data).mm_content_type).ct_subtype,
|
||||
b"disposition-notification\x00" as *const u8 as *const libc::c_char,
|
||||
) == 0
|
||||
{
|
||||
if let Ok(report_body) = wrapmime::mailmime_transfer_decode(report_data) {
|
||||
let mut report_parsed = std::ptr::null_mut();
|
||||
let mut dummy = 0;
|
||||
|
||||
if mailmime_parse(
|
||||
report_body.as_ptr() as *const _,
|
||||
report_body.len(),
|
||||
&mut dummy,
|
||||
&mut report_parsed,
|
||||
) == MAIL_NO_ERROR as libc::c_int
|
||||
&& !report_parsed.is_null()
|
||||
{
|
||||
let report_fields =
|
||||
wrapmime::mailmime_find_mailimf_fields(report_parsed);
|
||||
if !report_fields.is_null() {
|
||||
let of_disposition = wrapmime::mailimf_find_optional_field(
|
||||
report_fields,
|
||||
b"Disposition\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
let of_org_msgid = wrapmime::mailimf_find_optional_field(
|
||||
report_fields,
|
||||
b"Original-Message-ID\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
if !of_disposition.is_null()
|
||||
&& !(*of_disposition).fld_value.is_null()
|
||||
&& !of_org_msgid.is_null()
|
||||
&& !(*of_org_msgid).fld_value.is_null()
|
||||
{
|
||||
if let Ok(rfc724_mid) =
|
||||
wrapmime::parse_message_id(std::slice::from_raw_parts(
|
||||
(*of_org_msgid).fld_value as *const u8,
|
||||
libc::strlen((*of_org_msgid).fld_value),
|
||||
))
|
||||
{
|
||||
if let Some((chat_id, msg_id)) = message::mdn_from_ext(
|
||||
context,
|
||||
from_id,
|
||||
&rfc724_mid,
|
||||
sent_timestamp,
|
||||
) {
|
||||
rr_event_to_send.push((chat_id, msg_id));
|
||||
mdn_consumed = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
mailmime_free(report_parsed);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if mime_parser.is_send_by_messenger || 0 != mdn_consumed {
|
||||
let mut param = Params::new();
|
||||
param.set(Param::ServerFolder, server_folder.as_ref());
|
||||
param.set_int(Param::ServerUid, server_uid as i32);
|
||||
if mime_parser.is_send_by_messenger && context.get_config_bool(Config::MvboxMove) {
|
||||
param.set_int(Param::AlsoMove, 1);
|
||||
}
|
||||
job_add(context, Action::MarkseenMdnOnImap, 0, param, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn save_locations(
|
||||
context: &Context,
|
||||
mime_parser: &MimeParser,
|
||||
@@ -906,15 +752,15 @@ fn save_locations(
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn calc_timestamps(
|
||||
fn calc_timestamps(
|
||||
context: &Context,
|
||||
chat_id: u32,
|
||||
from_id: u32,
|
||||
message_timestamp: i64,
|
||||
is_fresh_msg: libc::c_int,
|
||||
sort_timestamp: *mut i64,
|
||||
sent_timestamp: *mut i64,
|
||||
rcvd_timestamp: *mut i64,
|
||||
sort_timestamp: &mut i64,
|
||||
sent_timestamp: &mut i64,
|
||||
rcvd_timestamp: &mut i64,
|
||||
) {
|
||||
*rcvd_timestamp = time();
|
||||
*sent_timestamp = message_timestamp;
|
||||
@@ -953,7 +799,7 @@ unsafe fn calc_timestamps(
|
||||
///
|
||||
/// So when the function returns, the caller has the group id matching the current state of the group.
|
||||
#[allow(non_snake_case)]
|
||||
unsafe fn create_or_lookup_group(
|
||||
fn create_or_lookup_group(
|
||||
context: &Context,
|
||||
mime_parser: &mut MimeParser,
|
||||
allow_creation: libc::c_int,
|
||||
@@ -982,7 +828,7 @@ unsafe fn create_or_lookup_group(
|
||||
chat_id: u32,
|
||||
chat_id_blocked: Blocked| {
|
||||
if !ret_chat_id.is_null() {
|
||||
*ret_chat_id = chat_id;
|
||||
unsafe { *ret_chat_id = chat_id };
|
||||
}
|
||||
*ret_chat_id_blocked = if 0 != chat_id {
|
||||
chat_id_blocked
|
||||
@@ -998,32 +844,24 @@ unsafe fn create_or_lookup_group(
|
||||
set_better_msg(mime_parser, &better_msg);
|
||||
|
||||
if let Some(optional_field) = mime_parser.lookup_field("Chat-Group-ID") {
|
||||
grpid = optional_field.get_value().unwrap_or_default();
|
||||
grpid = optional_field.clone();
|
||||
}
|
||||
|
||||
if grpid.is_empty() {
|
||||
if let Some(field) = mime_parser.lookup_field("Message-ID") {
|
||||
if let Ok(value) = field.get_value() {
|
||||
// if let Some(extracted_grpid) =
|
||||
// dc_extract_grpid_from_rfc724_mid(&to_string_lossy((*fld_message_id).mid_value))
|
||||
// {
|
||||
// grpid = extracted_grpid.to_string();
|
||||
// } else {
|
||||
// grpid = "".to_string();
|
||||
// }
|
||||
if let Some(value) = mime_parser.lookup_field("Message-ID") {
|
||||
if let Some(extracted_grpid) = dc_extract_grpid_from_rfc724_mid(&value) {
|
||||
grpid = extracted_grpid.to_string();
|
||||
} else {
|
||||
grpid = "".to_string();
|
||||
}
|
||||
}
|
||||
if grpid.is_empty() {
|
||||
if let Some(field) = mime_parser.lookup_field("In-Reply-To") {
|
||||
if let Ok(value) = field.get_value() {
|
||||
grpid = value;
|
||||
}
|
||||
if let Some(value) = mime_parser.lookup_field("In-Reply-To") {
|
||||
grpid = value.clone();
|
||||
}
|
||||
if grpid.is_empty() {
|
||||
if let Some(field) = mime_parser.lookup_field("References") {
|
||||
if let Ok(value) = field.get_value() {
|
||||
grpid = value;
|
||||
}
|
||||
if let Some(value) = mime_parser.lookup_field("References") {
|
||||
grpid = value.clone();
|
||||
}
|
||||
|
||||
if grpid.is_empty() {
|
||||
@@ -1044,11 +882,14 @@ unsafe fn create_or_lookup_group(
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(optional_field) = mime_parser.lookup_field("Chat-Group-Name") {
|
||||
grpname = Some(dc_decode_header_words(&optional_field.get_value().unwrap()));
|
||||
if let Some(optional_field) = mime_parser.lookup_field("Chat-Group-Name").cloned() {
|
||||
grpname = Some(optional_field);
|
||||
}
|
||||
if let Some(optional_field) = mime_parser.lookup_field("Chat-Group-Member-Removed") {
|
||||
X_MrRemoveFromGrp = optional_field.get_value().ok();
|
||||
let field = mime_parser
|
||||
.lookup_field("Chat-Group-Member-Removed")
|
||||
.cloned();
|
||||
if let Some(optional_field) = field {
|
||||
X_MrRemoveFromGrp = Some(optional_field);
|
||||
mime_parser.is_system_message = SystemMessage::MemberRemovedFromGroup;
|
||||
let left_group = (Contact::lookup_id_by_addr(context, X_MrRemoveFromGrp.as_ref().unwrap())
|
||||
== from_id as u32) as libc::c_int;
|
||||
@@ -1063,11 +904,12 @@ unsafe fn create_or_lookup_group(
|
||||
from_id as u32,
|
||||
)
|
||||
} else {
|
||||
if let Some(optional_field) = mime_parser.lookup_field("Chat-Group-Member-Added") {
|
||||
X_MrAddToGrp = optional_field.get_value().ok();
|
||||
let field = mime_parser.lookup_field("Chat-Group-Member-Added").cloned();
|
||||
if let Some(optional_field) = field {
|
||||
X_MrAddToGrp = Some(optional_field);
|
||||
mime_parser.is_system_message = SystemMessage::MemberAddedToGroup;
|
||||
if let Some(optional_field) = mime_parser.lookup_field("Chat-Group-Image") {
|
||||
X_MrGrpImageChanged = optional_field.get_value().unwrap();
|
||||
if let Some(optional_field) = mime_parser.lookup_field("Chat-Group-Image").cloned() {
|
||||
X_MrGrpImageChanged = optional_field;
|
||||
}
|
||||
better_msg = context.stock_system_msg(
|
||||
StockMessage::MsgAddMember,
|
||||
@@ -1081,7 +923,7 @@ unsafe fn create_or_lookup_group(
|
||||
X_MrGrpNameChanged = 1;
|
||||
better_msg = context.stock_system_msg(
|
||||
StockMessage::MsgGrpName,
|
||||
&field.unwrap().get_value().unwrap(),
|
||||
&field.unwrap(),
|
||||
if let Some(ref name) = grpname {
|
||||
name
|
||||
} else {
|
||||
@@ -1089,12 +931,13 @@ unsafe fn create_or_lookup_group(
|
||||
},
|
||||
from_id as u32,
|
||||
);
|
||||
drop(field);
|
||||
|
||||
mime_parser.is_system_message = SystemMessage::GroupNameChanged;
|
||||
} else {
|
||||
if let Some(optional_field) = mime_parser.lookup_field("Chat-Group-Image") {
|
||||
if let Some(optional_field) = mime_parser.lookup_field("Chat-Group-Image").cloned()
|
||||
{
|
||||
// fld_value is a pointer somewhere into mime_parser, must not be freed
|
||||
X_MrGrpImageChanged = optional_field.get_value().unwrap();
|
||||
X_MrGrpImageChanged = optional_field;
|
||||
mime_parser.is_system_message = SystemMessage::GroupImageChanged;
|
||||
better_msg = context.stock_system_msg(
|
||||
if X_MrGrpImageChanged == "0" {
|
||||
@@ -1324,7 +1167,7 @@ unsafe fn create_or_lookup_group(
|
||||
}
|
||||
|
||||
/// Handle groups for received messages
|
||||
unsafe fn create_or_lookup_adhoc_group(
|
||||
fn create_or_lookup_adhoc_group(
|
||||
context: &Context,
|
||||
mime_parser: &MimeParser,
|
||||
allow_creation: libc::c_int,
|
||||
@@ -1344,7 +1187,7 @@ unsafe fn create_or_lookup_adhoc_group(
|
||||
chat_id: u32,
|
||||
chat_id_blocked: Blocked| {
|
||||
if !ret_chat_id.is_null() {
|
||||
*ret_chat_id = chat_id;
|
||||
unsafe { *ret_chat_id = chat_id };
|
||||
}
|
||||
*ret_chat_id_blocked = chat_id_blocked;
|
||||
};
|
||||
@@ -1681,42 +1524,30 @@ fn dc_is_reply_to_known_message(context: &Context, mime_parser: &MimeParser) ->
|
||||
`In-Reply-To`/`References:` (to support non-Delta-Clients) */
|
||||
|
||||
if let Some(field) = mime_parser.lookup_field("In-Reply-To") {
|
||||
if let Ok(value) = field.get_value() {
|
||||
// if !fld_in_reply_to.is_null() {
|
||||
// if is_known_rfc724_mid_in_list(
|
||||
// context,
|
||||
// (*(*field).fld_data.fld_in_reply_to).mid_list,
|
||||
// ) {
|
||||
// return 1;
|
||||
// }
|
||||
// }
|
||||
if is_known_rfc724_mid_in_list(context, &field) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(field) = mime_parser.lookup_field("References") {
|
||||
if let Ok(value) = field.get_value() {
|
||||
// if !fld_references.is_null()
|
||||
// && is_known_rfc724_mid_in_list(
|
||||
// context,
|
||||
// (*(*field).fld_data.fld_references).mid_list,
|
||||
// )
|
||||
// {
|
||||
// return 1;
|
||||
// }
|
||||
if is_known_rfc724_mid_in_list(context, &field) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
0
|
||||
}
|
||||
|
||||
unsafe fn is_known_rfc724_mid_in_list(context: &Context, mid_list: *const clist) -> bool {
|
||||
if mid_list.is_null() {
|
||||
fn is_known_rfc724_mid_in_list(context: &Context, mid_list: &String) -> bool {
|
||||
if mid_list.is_empty() {
|
||||
return false;
|
||||
}
|
||||
|
||||
for data in &*mid_list {
|
||||
if is_known_rfc724_mid(context, data.cast()) != 0 {
|
||||
return true;
|
||||
if let Ok(ids) = mailparse::addrparse(mid_list.as_str()) {
|
||||
for id in ids.iter() {
|
||||
if is_known_rfc724_mid(context, id) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1724,10 +1555,8 @@ unsafe fn is_known_rfc724_mid_in_list(context: &Context, mid_list: *const clist)
|
||||
}
|
||||
|
||||
/// Check if a message is a reply to a known message (messenger or non-messenger).
|
||||
fn is_known_rfc724_mid(context: &Context, rfc724_mid: *const libc::c_char) -> libc::c_int {
|
||||
if rfc724_mid.is_null() {
|
||||
return 0;
|
||||
}
|
||||
fn is_known_rfc724_mid(context: &Context, rfc724_mid: &mailparse::MailAddr) -> bool {
|
||||
let addr = extract_single_from_addr(rfc724_mid);
|
||||
context
|
||||
.sql
|
||||
.exists(
|
||||
@@ -1735,116 +1564,92 @@ fn is_known_rfc724_mid(context: &Context, rfc724_mid: *const libc::c_char) -> li
|
||||
LEFT JOIN chats c ON m.chat_id=c.id \
|
||||
WHERE m.rfc724_mid=? \
|
||||
AND m.chat_id>9 AND c.blocked=0;",
|
||||
params![to_string_lossy(rfc724_mid)],
|
||||
params![addr],
|
||||
)
|
||||
.unwrap_or_default() as libc::c_int
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
unsafe fn dc_is_reply_to_messenger_message(
|
||||
context: &Context,
|
||||
mime_parser: &MimeParser,
|
||||
) -> libc::c_int {
|
||||
fn dc_is_reply_to_messenger_message(context: &Context, mime_parser: &MimeParser) -> libc::c_int {
|
||||
/* function checks, if the message defined by mime_parser references a message send by us from Delta Chat.
|
||||
This is similar to is_reply_to_known_message() but
|
||||
- checks also if any of the referenced IDs are send by a messenger
|
||||
- it is okay, if the referenced messages are moved to trash here
|
||||
- no check for the Chat-* headers (function is only called if it is no messenger message itself) */
|
||||
|
||||
if let Some(field) = mime_parser.lookup_field("In-Reply-To") {
|
||||
if let Ok(value) = field.get_value() {
|
||||
// if 0 != is_msgrmsg_rfc724_mid_in_list(
|
||||
// context,
|
||||
// (*(*field).fld_data.fld_in_reply_to).mid_list,
|
||||
// ) {
|
||||
// return 1;
|
||||
// }
|
||||
if let Some(value) = mime_parser.lookup_field("In-Reply-To") {
|
||||
if is_msgrmsg_rfc724_mid_in_list(context, &value) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(field) = mime_parser.lookup_field("References") {
|
||||
if let Ok(value) = field.get_value() {
|
||||
// if 0 != is_msgrmsg_rfc724_mid_in_list(
|
||||
// context,
|
||||
// (*(*field).fld_data.fld_references).mid_list,
|
||||
// ) {
|
||||
// return 1;
|
||||
// }
|
||||
if let Some(value) = mime_parser.lookup_field("References") {
|
||||
if is_msgrmsg_rfc724_mid_in_list(context, &value) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
0
|
||||
}
|
||||
|
||||
unsafe fn is_msgrmsg_rfc724_mid_in_list(context: &Context, mid_list: *const clist) -> libc::c_int {
|
||||
if !mid_list.is_null() {
|
||||
let mut cur: *mut clistiter = (*mid_list).first;
|
||||
while !cur.is_null() {
|
||||
if 0 != is_msgrmsg_rfc724_mid(
|
||||
context,
|
||||
&to_string_lossy((*cur).data as *const libc::c_char),
|
||||
) {
|
||||
return 1;
|
||||
}
|
||||
cur = if !cur.is_null() {
|
||||
(*cur).next
|
||||
} else {
|
||||
ptr::null_mut()
|
||||
fn is_msgrmsg_rfc724_mid_in_list(context: &Context, mid_list: &String) -> bool {
|
||||
if let Ok(ids) = mailparse::addrparse(mid_list.as_str()) {
|
||||
for id in ids.iter() {
|
||||
if is_msgrmsg_rfc724_mid(context, id) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
0
|
||||
false
|
||||
}
|
||||
|
||||
fn extract_single_from_addr(addr: &mailparse::MailAddr) -> &String {
|
||||
match addr {
|
||||
mailparse::MailAddr::Group(infos) => &infos.addrs[0].addr,
|
||||
mailparse::MailAddr::Single(info) => &info.addr,
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if a message is a reply to any messenger message.
|
||||
fn is_msgrmsg_rfc724_mid(context: &Context, rfc724_mid: &str) -> libc::c_int {
|
||||
if rfc724_mid.is_empty() {
|
||||
return 0;
|
||||
}
|
||||
fn is_msgrmsg_rfc724_mid(context: &Context, rfc724_mid: &mailparse::MailAddr) -> bool {
|
||||
let addr = extract_single_from_addr(rfc724_mid);
|
||||
context
|
||||
.sql
|
||||
.exists(
|
||||
"SELECT id FROM msgs WHERE rfc724_mid=? AND msgrmsg!=0 AND chat_id>9;",
|
||||
params![rfc724_mid],
|
||||
params![addr],
|
||||
)
|
||||
.unwrap_or_default() as libc::c_int
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
unsafe fn dc_add_or_lookup_contacts_by_address_list(
|
||||
fn dc_add_or_lookup_contacts_by_address_list(
|
||||
context: &Context,
|
||||
adr_list: *const mailimf_address_list,
|
||||
addr_list_raw: &str,
|
||||
origin: Origin,
|
||||
ids: &mut Vec<u32>,
|
||||
check_self: *mut libc::c_int,
|
||||
check_self: &mut bool,
|
||||
) {
|
||||
if adr_list.is_null() {
|
||||
let addrs = mailparse::addrparse(addr_list_raw);
|
||||
if addrs.is_err() {
|
||||
return;
|
||||
}
|
||||
let mut cur: *mut clistiter = (*(*adr_list).ad_list).first;
|
||||
while !cur.is_null() {
|
||||
let adr: *mut mailimf_address = (if !cur.is_null() {
|
||||
(*cur).data
|
||||
} else {
|
||||
ptr::null_mut()
|
||||
}) as *mut mailimf_address;
|
||||
if !adr.is_null() {
|
||||
if (*adr).ad_type == MAILIMF_ADDRESS_MAILBOX as libc::c_int {
|
||||
let mb: *mut mailimf_mailbox = (*adr).ad_data.ad_mailbox;
|
||||
if !mb.is_null() {
|
||||
for addr in addrs.unwrap().iter() {
|
||||
match addr {
|
||||
mailparse::MailAddr::Single(info) => {
|
||||
add_or_lookup_contact_by_addr(
|
||||
context,
|
||||
&info.display_name,
|
||||
&info.addr,
|
||||
origin,
|
||||
ids,
|
||||
check_self,
|
||||
);
|
||||
}
|
||||
mailparse::MailAddr::Group(infos) => {
|
||||
for info in &infos.addrs {
|
||||
add_or_lookup_contact_by_addr(
|
||||
context,
|
||||
(*mb).mb_display_name,
|
||||
(*mb).mb_addr_spec,
|
||||
origin,
|
||||
ids,
|
||||
check_self,
|
||||
);
|
||||
}
|
||||
} else if (*adr).ad_type == MAILIMF_ADDRESS_GROUP as libc::c_int {
|
||||
let group: *mut mailimf_group = (*adr).ad_data.ad_group;
|
||||
if !group.is_null() && !(*group).grp_mb_list.is_null() {
|
||||
dc_add_or_lookup_contacts_by_mailbox_list(
|
||||
context,
|
||||
(*group).grp_mb_list,
|
||||
&info.display_name,
|
||||
&info.addr,
|
||||
origin,
|
||||
ids,
|
||||
check_self,
|
||||
@@ -1852,93 +1657,42 @@ unsafe fn dc_add_or_lookup_contacts_by_address_list(
|
||||
}
|
||||
}
|
||||
}
|
||||
cur = if !cur.is_null() {
|
||||
(*cur).next
|
||||
} else {
|
||||
ptr::null_mut()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn dc_add_or_lookup_contacts_by_mailbox_list(
|
||||
context: &Context,
|
||||
mb_list: *const mailimf_mailbox_list,
|
||||
origin: Origin,
|
||||
ids: &mut Vec<u32>,
|
||||
check_self: *mut libc::c_int,
|
||||
) {
|
||||
if mb_list.is_null() {
|
||||
return;
|
||||
}
|
||||
let mut cur: *mut clistiter = (*(*mb_list).mb_list).first;
|
||||
while !cur.is_null() {
|
||||
let mb: *mut mailimf_mailbox = (if !cur.is_null() {
|
||||
(*cur).data
|
||||
} else {
|
||||
ptr::null_mut()
|
||||
}) as *mut mailimf_mailbox;
|
||||
if !mb.is_null() {
|
||||
add_or_lookup_contact_by_addr(
|
||||
context,
|
||||
(*mb).mb_display_name,
|
||||
(*mb).mb_addr_spec,
|
||||
origin,
|
||||
ids,
|
||||
check_self,
|
||||
);
|
||||
}
|
||||
cur = if !cur.is_null() {
|
||||
(*cur).next
|
||||
} else {
|
||||
ptr::null_mut()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Add contacts to database on receiving messages.
|
||||
unsafe fn add_or_lookup_contact_by_addr(
|
||||
fn add_or_lookup_contact_by_addr(
|
||||
context: &Context,
|
||||
display_name_enc: *const libc::c_char,
|
||||
addr_spec: *const libc::c_char,
|
||||
display_name: &Option<String>,
|
||||
addr: &String,
|
||||
origin: Origin,
|
||||
ids: &mut Vec<u32>,
|
||||
mut check_self: *mut libc::c_int,
|
||||
check_self: &mut bool,
|
||||
) {
|
||||
/* is addr_spec equal to SELF? */
|
||||
let mut dummy: libc::c_int = 0;
|
||||
if check_self.is_null() {
|
||||
check_self = &mut dummy
|
||||
}
|
||||
if addr_spec.is_null() {
|
||||
return;
|
||||
}
|
||||
*check_self = 0;
|
||||
// is addr_spec equal to SELF?
|
||||
let self_addr = context
|
||||
.get_config(Config::ConfiguredAddr)
|
||||
.unwrap_or_default();
|
||||
|
||||
if addr_cmp(self_addr, to_string_lossy(addr_spec)) {
|
||||
*check_self = 1;
|
||||
if addr_cmp(self_addr, addr) {
|
||||
*check_self = true;
|
||||
}
|
||||
|
||||
if 0 != *check_self {
|
||||
if *check_self {
|
||||
return;
|
||||
}
|
||||
/* add addr_spec if missing, update otherwise */
|
||||
let mut display_name_dec = "".to_string();
|
||||
if !display_name_enc.is_null() {
|
||||
let tmp = dc_decode_header_words(&to_string_lossy(display_name_enc));
|
||||
display_name_dec = normalize_name(&tmp);
|
||||
}
|
||||
/*can be NULL*/
|
||||
let row_id = Contact::add_or_lookup(
|
||||
context,
|
||||
display_name_dec,
|
||||
to_string_lossy(addr_spec),
|
||||
origin,
|
||||
)
|
||||
.map(|(id, _)| id)
|
||||
.unwrap_or_default();
|
||||
|
||||
// add addr_spec if missing, update otherwise
|
||||
let display_name_normalized = display_name
|
||||
.as_ref()
|
||||
.map(normalize_name)
|
||||
.unwrap_or_default();
|
||||
|
||||
// can be NULL
|
||||
let row_id = Contact::add_or_lookup(context, display_name_normalized, addr, origin)
|
||||
.map(|(id, _)| id)
|
||||
.unwrap_or_default();
|
||||
|
||||
if 0 != row_id && !ids.contains(&row_id) {
|
||||
ids.push(row_id);
|
||||
};
|
||||
|
||||
@@ -5,8 +5,6 @@ use std::ptr;
|
||||
|
||||
use charset::Charset;
|
||||
use libc::free;
|
||||
use mmime::mailmime::decode::mailmime_encoded_phrase_parse;
|
||||
use mmime::other::*;
|
||||
use percent_encoding::{percent_decode, utf8_percent_encode, AsciiSet, CONTROLS};
|
||||
|
||||
use crate::dc_tools::*;
|
||||
@@ -68,31 +66,6 @@ fn quote_word(word: &[u8]) -> String {
|
||||
* Encode/decode header words, RFC 2047
|
||||
******************************************************************************/
|
||||
|
||||
pub(crate) fn dc_decode_header_words(input: &str) -> String {
|
||||
static FROM_ENCODING: &[u8] = b"iso-8859-1\x00";
|
||||
static TO_ENCODING: &[u8] = b"utf-8\x00";
|
||||
let mut out = ptr::null_mut();
|
||||
let mut cur_token = 0;
|
||||
let input_c = CString::yolo(input);
|
||||
unsafe {
|
||||
let r = mailmime_encoded_phrase_parse(
|
||||
FROM_ENCODING.as_ptr().cast(),
|
||||
input_c.as_ptr(),
|
||||
input.len(),
|
||||
&mut cur_token,
|
||||
TO_ENCODING.as_ptr().cast(),
|
||||
&mut out,
|
||||
);
|
||||
if r as u32 != MAILIMF_NO_ERROR || out.is_null() {
|
||||
input.to_string()
|
||||
} else {
|
||||
let res = to_string_lossy(out);
|
||||
free(out.cast());
|
||||
res
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dc_needs_ext_header(to_check: impl AsRef<str>) -> bool {
|
||||
let to_check = to_check.as_ref();
|
||||
|
||||
@@ -156,33 +129,6 @@ pub fn dc_decode_ext_header(to_decode: &[u8]) -> Cow<str> {
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_dc_decode_header_words() {
|
||||
assert_eq!(
|
||||
dc_decode_header_words("=?utf-8?B?dGVzdMOkw7bDvC50eHQ=?="),
|
||||
std::string::String::from_utf8(b"test\xc3\xa4\xc3\xb6\xc3\xbc.txt".to_vec()).unwrap(),
|
||||
);
|
||||
|
||||
assert_eq!(dc_decode_header_words("just ascii test"), "just ascii test");
|
||||
|
||||
assert_eq!(dc_encode_header_words("abcdef"), "abcdef");
|
||||
|
||||
let r = dc_encode_header_words(
|
||||
std::string::String::from_utf8(b"test\xc3\xa4\xc3\xb6\xc3\xbc.txt".to_vec()).unwrap(),
|
||||
);
|
||||
assert!(r.starts_with("=?utf-8"));
|
||||
|
||||
assert_eq!(
|
||||
dc_decode_header_words(&r),
|
||||
std::string::String::from_utf8(b"test\xc3\xa4\xc3\xb6\xc3\xbc.txt".to_vec()).unwrap(),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
dc_decode_header_words("=?ISO-8859-1?Q?attachment=3B=0D=0A_filename=3D?= =?ISO-8859-1?Q?=22test=E4=F6=FC=2Etxt=22=3B=0D=0A_size=3D39?="),
|
||||
std::string::String::from_utf8(b"attachment;\r\n filename=\"test\xc3\xa4\xc3\xb6\xc3\xbc.txt\";\r\n size=39".to_vec()).unwrap(),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_dc_encode_ext_header() {
|
||||
let buf1 = dc_encode_ext_header("Björn Petersen");
|
||||
@@ -239,13 +185,5 @@ mod tests {
|
||||
// make sure this never panics
|
||||
let _decoded = dc_decode_ext_header(&buf);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_dc_header_roundtrip(input: String) {
|
||||
let encoded = dc_encode_header_words(&input);
|
||||
let decoded = dc_decode_header_words(&encoded);
|
||||
|
||||
assert_eq!(input, decoded);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
132
src/dc_tools.rs
132
src/dc_tools.rs
@@ -11,8 +11,6 @@ use std::{fmt, fs, ptr};
|
||||
|
||||
use chrono::{Local, TimeZone};
|
||||
use libc::{memcpy, strlen};
|
||||
use mmime::clist::*;
|
||||
use mmime::mailimf::types::*;
|
||||
use rand::{thread_rng, Rng};
|
||||
|
||||
use crate::context::Context;
|
||||
@@ -74,36 +72,12 @@ pub(crate) fn dc_truncate(buf: &str, approx_chars: usize, do_unwrap: bool) -> Co
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn dc_str_from_clist(list: *const clist, delimiter: &str) -> String {
|
||||
let mut res = String::new();
|
||||
|
||||
if !list.is_null() {
|
||||
for rfc724_mid in unsafe { (*list).into_iter() } {
|
||||
if !res.is_empty() {
|
||||
res += delimiter;
|
||||
}
|
||||
res += &to_string_lossy(rfc724_mid as *const libc::c_char);
|
||||
}
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
pub(crate) fn dc_str_to_clist(str: &str, delimiter: &str) -> *mut clist {
|
||||
unsafe {
|
||||
let list: *mut clist = clist_new();
|
||||
for cur in str.split(&delimiter) {
|
||||
clist_insert_after(list, (*list).last, cur.strdup().cast());
|
||||
}
|
||||
list
|
||||
}
|
||||
}
|
||||
|
||||
/* the colors must fulfill some criterions as:
|
||||
- contrast to black and to white
|
||||
- work as a text-color
|
||||
- being noticeable on a typical map
|
||||
- harmonize together while being different enough
|
||||
(therefore, we cannot just use random rgb colors :) */
|
||||
/// the colors must fulfill some criterions as:
|
||||
/// - contrast to black and to white
|
||||
/// - work as a text-color
|
||||
/// - being noticeable on a typical map
|
||||
/// - harmonize together while being different enough
|
||||
/// (therefore, we cannot just use random rgb colors :)
|
||||
const COLORS: [u32; 16] = [
|
||||
0xe56555, 0xf28c48, 0x8e85ee, 0x76c84d, 0x5bb6cc, 0x549cdd, 0xd25c99, 0xb37800, 0xf23030,
|
||||
0x39b249, 0xbb243b, 0x964078, 0x66874f, 0x308ab9, 0x127ed0, 0xbe450c,
|
||||
@@ -124,30 +98,30 @@ pub(crate) fn dc_str_to_color(s: impl AsRef<str>) -> u32 {
|
||||
|
||||
/* date/time tools */
|
||||
/* the result is UTC or DC_INVALID_TIMESTAMP */
|
||||
pub(crate) fn dc_timestamp_from_date(date_time: *mut mailimf_date_time) -> i64 {
|
||||
assert!(!date_time.is_null());
|
||||
let dt = unsafe { *date_time };
|
||||
// pub(crate) fn dc_timestamp_from_date(date_time: *mut mailimf_date_time) -> i64 {
|
||||
// assert!(!date_time.is_null());
|
||||
// let dt = { *date_time };
|
||||
|
||||
let sec = dt.dt_sec;
|
||||
let min = dt.dt_min;
|
||||
let hour = dt.dt_hour;
|
||||
let day = dt.dt_day;
|
||||
let month = dt.dt_month;
|
||||
let year = dt.dt_year;
|
||||
// let sec = dt.dt_sec;
|
||||
// let min = dt.dt_min;
|
||||
// let hour = dt.dt_hour;
|
||||
// let day = dt.dt_day;
|
||||
// let month = dt.dt_month;
|
||||
// let year = dt.dt_year;
|
||||
|
||||
let ts = chrono::NaiveDateTime::new(
|
||||
chrono::NaiveDate::from_ymd(year, month as u32, day as u32),
|
||||
chrono::NaiveTime::from_hms(hour as u32, min as u32, sec as u32),
|
||||
);
|
||||
// let ts = chrono::NaiveDateTime::new(
|
||||
// chrono::NaiveDate::from_ymd(year, month as u32, day as u32),
|
||||
// chrono::NaiveTime::from_hms(hour as u32, min as u32, sec as u32),
|
||||
// );
|
||||
|
||||
let (zone_hour, zone_min) = if dt.dt_zone >= 0 {
|
||||
(dt.dt_zone / 100, dt.dt_zone % 100)
|
||||
} else {
|
||||
(-(-dt.dt_zone / 100), -(-dt.dt_zone % 100))
|
||||
};
|
||||
// let (zone_hour, zone_min) = if dt.dt_zone >= 0 {
|
||||
// (dt.dt_zone / 100, dt.dt_zone % 100)
|
||||
// } else {
|
||||
// (-(-dt.dt_zone / 100), -(-dt.dt_zone % 100))
|
||||
// };
|
||||
|
||||
ts.timestamp() - (zone_hour * 3600 + zone_min * 60) as i64
|
||||
}
|
||||
// ts.timestamp() - (zone_hour * 3600 + zone_min * 60) as i64
|
||||
// }
|
||||
|
||||
/* ******************************************************************************
|
||||
* date/time tools
|
||||
@@ -318,22 +292,6 @@ pub(crate) fn dc_extract_grpid_from_rfc724_mid(mid: &str) -> Option<&str> {
|
||||
None
|
||||
}
|
||||
|
||||
pub(crate) fn dc_extract_grpid_from_rfc724_mid_list(list: *const clist) -> *mut libc::c_char {
|
||||
if !list.is_null() {
|
||||
unsafe {
|
||||
for cur in (*list).into_iter() {
|
||||
let mid = to_string_lossy(cur as *const libc::c_char);
|
||||
|
||||
if let Some(grpid) = dc_extract_grpid_from_rfc724_mid(&mid) {
|
||||
return grpid.strdup();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ptr::null_mut()
|
||||
}
|
||||
|
||||
pub(crate) fn dc_ensure_no_slash_safe(path: &str) -> &str {
|
||||
if path.ends_with('/') || path.ends_with('\\') {
|
||||
return &path[..path.len() - 1];
|
||||
@@ -1009,44 +967,6 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
/* calls free() for each item content */
|
||||
unsafe fn clist_free_content(haystack: *const clist) {
|
||||
let mut iter = (*haystack).first;
|
||||
|
||||
while !iter.is_null() {
|
||||
free((*iter).data);
|
||||
(*iter).data = ptr::null_mut();
|
||||
iter = if !iter.is_null() {
|
||||
(*iter).next
|
||||
} else {
|
||||
ptr::null_mut()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_dc_str_to_clist_1() {
|
||||
unsafe {
|
||||
let list = dc_str_to_clist("", " ");
|
||||
assert_eq!((*list).count, 1);
|
||||
clist_free_content(list);
|
||||
clist_free(list);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_dc_str_to_clist_4() {
|
||||
unsafe {
|
||||
let list: *mut clist = dc_str_to_clist("foo bar test", " ");
|
||||
assert_eq!((*list).count, 3);
|
||||
let str = dc_str_from_clist(list, " ");
|
||||
assert_eq!(str, "foo bar test");
|
||||
|
||||
clist_free_content(list);
|
||||
clist_free(list);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_dc_create_id() {
|
||||
let buf = dc_create_id();
|
||||
|
||||
816
src/e2ee.rs
816
src/e2ee.rs
@@ -1,28 +1,12 @@
|
||||
//! End-to-end encryption support.
|
||||
|
||||
use std::collections::HashSet;
|
||||
use std::ptr;
|
||||
use std::str::FromStr;
|
||||
|
||||
use libc::strlen;
|
||||
use mmime::clist::*;
|
||||
use mmime::mailimf::types::*;
|
||||
use mmime::mailimf::types_helper::*;
|
||||
use mmime::mailimf::*;
|
||||
use mmime::mailmime::content::*;
|
||||
use mmime::mailmime::types::*;
|
||||
use mmime::mailmime::types_helper::*;
|
||||
use mmime::mailmime::write_mem::*;
|
||||
use mmime::mailmime::*;
|
||||
use mmime::mailprivacy_prepare_mime;
|
||||
use mmime::mmapstring::*;
|
||||
use mmime::{mailmime_substitute, MAILIMF_NO_ERROR, MAIL_NO_ERROR};
|
||||
use num_traits::FromPrimitive;
|
||||
|
||||
use crate::aheader::*;
|
||||
use crate::config::Config;
|
||||
use crate::context::Context;
|
||||
use crate::dc_tools::*;
|
||||
use crate::error::*;
|
||||
use crate::key::*;
|
||||
use crate::keyring::*;
|
||||
@@ -31,7 +15,6 @@ use crate::peerstate::*;
|
||||
use crate::pgp;
|
||||
use crate::securejoin::handle_degrade_event;
|
||||
use crate::wrapmime;
|
||||
use crate::wrapmime::*;
|
||||
|
||||
// standard mime-version header aka b"Version: 1\r\n\x00"
|
||||
static mut VERSION_CONTENT: [libc::c_char; 13] =
|
||||
@@ -77,203 +60,203 @@ impl EncryptHelper {
|
||||
e2ee_guaranteed: bool,
|
||||
min_verified: PeerstateVerifiedStatus,
|
||||
do_gossip: bool,
|
||||
mut in_out_message: *mut Mailmime,
|
||||
imffields_unprotected: *mut mailimf_fields,
|
||||
) -> Result<bool> {
|
||||
// libEtPan's pgp_encrypt_mime() takes the parent as the new root.
|
||||
// We just expect the root as being given to this function.
|
||||
ensure!(
|
||||
!in_out_message.is_null() && unsafe { (*in_out_message).mm_parent.is_null() },
|
||||
"corrupted inputs"
|
||||
);
|
||||
mut in_out_message: lettre_email::Email,
|
||||
imffields_unprotected: Vec<lettre_email::Header>,
|
||||
) -> Result<lettre_email::Email> {
|
||||
unimplemented!()
|
||||
// // libEtPan's pgp_encrypt_mime() takes the parent as the new root.
|
||||
// // We just expect the root as being given to this function.
|
||||
// ensure!(
|
||||
// !in_out_message.is_null() && { (*in_out_message).mm_parent.is_null() },
|
||||
// "corrupted inputs"
|
||||
// );
|
||||
|
||||
if !(self.prefer_encrypt == EncryptPreference::Mutual || e2ee_guaranteed) {
|
||||
return Ok(false);
|
||||
}
|
||||
// if !(self.prefer_encrypt == EncryptPreference::Mutual || e2ee_guaranteed) {
|
||||
// return Ok(false);
|
||||
// }
|
||||
|
||||
let context = &factory.context;
|
||||
let mut keyring = Keyring::default();
|
||||
let mut gossip_headers: Vec<String> = Vec::with_capacity(factory.recipients_addr.len());
|
||||
// let context = &factory.context;
|
||||
// let mut keyring = Keyring::default();
|
||||
// let mut gossip_headers: Vec<String> = Vec::with_capacity(factory.recipients_addr.len());
|
||||
|
||||
// determine if we can and should encrypt
|
||||
for recipient_addr in factory.recipients_addr.iter() {
|
||||
if recipient_addr == &self.addr {
|
||||
continue;
|
||||
}
|
||||
let peerstate = match Peerstate::from_addr(context, &context.sql, recipient_addr) {
|
||||
Some(peerstate) => peerstate,
|
||||
None => {
|
||||
let msg = format!("peerstate for {} missing, cannot encrypt", recipient_addr);
|
||||
if e2ee_guaranteed {
|
||||
return Err(format_err!("{}", msg));
|
||||
} else {
|
||||
info!(context, "{}", msg);
|
||||
return Ok(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
// // determine if we can and should encrypt
|
||||
// for recipient_addr in factory.recipients_addr.iter() {
|
||||
// if recipient_addr == &self.addr {
|
||||
// continue;
|
||||
// }
|
||||
// let peerstate = match Peerstate::from_addr(context, &context.sql, recipient_addr) {
|
||||
// Some(peerstate) => peerstate,
|
||||
// None => {
|
||||
// let msg = format!("peerstate for {} missing, cannot encrypt", recipient_addr);
|
||||
// if e2ee_guaranteed {
|
||||
// return Err(format_err!("{}", msg));
|
||||
// } else {
|
||||
// info!(context, "{}", msg);
|
||||
// return Ok(false);
|
||||
// }
|
||||
// }
|
||||
// };
|
||||
|
||||
if peerstate.prefer_encrypt != EncryptPreference::Mutual && !e2ee_guaranteed {
|
||||
info!(context, "peerstate for {} is no-encrypt", recipient_addr);
|
||||
return Ok(false);
|
||||
}
|
||||
// if peerstate.prefer_encrypt != EncryptPreference::Mutual && !e2ee_guaranteed {
|
||||
// info!(context, "peerstate for {} is no-encrypt", recipient_addr);
|
||||
// return Ok(false);
|
||||
// }
|
||||
|
||||
if let Some(key) = peerstate.peek_key(min_verified) {
|
||||
keyring.add_owned(key.clone());
|
||||
if do_gossip {
|
||||
if let Some(header) = peerstate.render_gossip_header(min_verified) {
|
||||
gossip_headers.push(header.to_string());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
bail!(
|
||||
"proper enc-key for {} missing, cannot encrypt",
|
||||
recipient_addr
|
||||
);
|
||||
}
|
||||
}
|
||||
// if let Some(key) = peerstate.peek_key(min_verified) {
|
||||
// keyring.add_owned(key.clone());
|
||||
// if do_gossip {
|
||||
// if let Some(header) = peerstate.render_gossip_header(min_verified) {
|
||||
// gossip_headers.push(header.to_string());
|
||||
// }
|
||||
// }
|
||||
// } else {
|
||||
// bail!(
|
||||
// "proper enc-key for {} missing, cannot encrypt",
|
||||
// recipient_addr
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
|
||||
let sign_key = {
|
||||
keyring.add_ref(&self.public_key);
|
||||
let key = Key::from_self_private(context, self.addr.clone(), &context.sql);
|
||||
ensure!(key.is_some(), "no own private key found");
|
||||
// let sign_key = {
|
||||
// keyring.add_ref(&self.public_key);
|
||||
// let key = Key::from_self_private(context, self.addr.clone(), &context.sql);
|
||||
// ensure!(key.is_some(), "no own private key found");
|
||||
|
||||
key
|
||||
};
|
||||
// key
|
||||
// };
|
||||
|
||||
// encrypt message
|
||||
unsafe {
|
||||
mailprivacy_prepare_mime(in_out_message);
|
||||
let mut part_to_encrypt = (*in_out_message).mm_data.mm_message.mm_msg_mime;
|
||||
(*part_to_encrypt).mm_parent = ptr::null_mut();
|
||||
let imffields_encrypted = mailimf_fields_new_empty();
|
||||
// // encrypt message
|
||||
// {
|
||||
// mailprivacy_prepare_mime(in_out_message);
|
||||
// let mut part_to_encrypt = (*in_out_message).mm_data.mm_message.mm_msg_mime;
|
||||
// (*part_to_encrypt).mm_parent = ptr::null_mut();
|
||||
// let imffields_encrypted = mailimf_fields_new_empty();
|
||||
|
||||
// mailmime_new_message_data() calls mailmime_fields_new_with_version()
|
||||
// which would add the unwanted MIME-Version:-header
|
||||
let message_to_encrypt = mailmime_new_simple(
|
||||
MAILMIME_MESSAGE as libc::c_int,
|
||||
mailmime_fields_new_empty(),
|
||||
mailmime_get_content_message(),
|
||||
imffields_encrypted,
|
||||
part_to_encrypt,
|
||||
);
|
||||
// // mailmime_new_message_data() calls mailmime_fields_new_with_version()
|
||||
// // which would add the unwanted MIME-Version:-header
|
||||
// let message_to_encrypt = mailmime_new_simple(
|
||||
// MAILMIME_MESSAGE as libc::c_int,
|
||||
// mailmime_fields_new_empty(),
|
||||
// mailmime_get_content_message(),
|
||||
// imffields_encrypted,
|
||||
// part_to_encrypt,
|
||||
// );
|
||||
|
||||
for header in &gossip_headers {
|
||||
wrapmime::new_custom_field(imffields_encrypted, "Autocrypt-Gossip", &header)
|
||||
}
|
||||
// for header in &gossip_headers {
|
||||
// wrapmime::new_custom_field(imffields_encrypted, "Autocrypt-Gossip", &header)
|
||||
// }
|
||||
|
||||
// memoryhole headers: move some headers into encrypted part
|
||||
// XXX note we can't use clist's into_iter() because the loop body also removes items
|
||||
let mut cur = (*(*imffields_unprotected).fld_list).first;
|
||||
while !cur.is_null() {
|
||||
let field = (*cur).data as *mut mailimf_field;
|
||||
let mut move_to_encrypted = false;
|
||||
// // memoryhole headers: move some headers into encrypted part
|
||||
// // XXX note we can't use clist's into_iter() because the loop body also removes items
|
||||
// let mut cur = (*(*imffields_unprotected).fld_list).first;
|
||||
// while !cur.is_null() {
|
||||
// let field = (*cur).data as *mut mailimf_field;
|
||||
// let mut move_to_encrypted = false;
|
||||
|
||||
if !field.is_null() {
|
||||
if (*field).fld_type == MAILIMF_FIELD_SUBJECT as libc::c_int {
|
||||
move_to_encrypted = true;
|
||||
} else if (*field).fld_type == MAILIMF_FIELD_OPTIONAL_FIELD as libc::c_int {
|
||||
let opt_field = (*field).fld_data.fld_optional_field;
|
||||
if !opt_field.is_null() && !(*opt_field).fld_name.is_null() {
|
||||
let fld_name = to_string_lossy((*opt_field).fld_name);
|
||||
if fld_name.starts_with("Secure-Join")
|
||||
|| (fld_name.starts_with("Chat-") && fld_name != "Chat-Version")
|
||||
{
|
||||
move_to_encrypted = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// if !field.is_null() {
|
||||
// if (*field).fld_type == MAILIMF_FIELD_SUBJECT as libc::c_int {
|
||||
// move_to_encrypted = true;
|
||||
// } else if (*field).fld_type == MAILIMF_FIELD_OPTIONAL_FIELD as libc::c_int {
|
||||
// let opt_field = (*field).fld_data.fld_optional_field;
|
||||
// if !opt_field.is_null() && !(*opt_field).fld_name.is_null() {
|
||||
// let fld_name = to_string_lossy((*opt_field).fld_name);
|
||||
// if fld_name.starts_with("Secure-Join")
|
||||
// || (fld_name.starts_with("Chat-") && fld_name != "Chat-Version")
|
||||
// {
|
||||
// move_to_encrypted = true;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
if move_to_encrypted {
|
||||
mailimf_fields_add(imffields_encrypted, field);
|
||||
cur = clist_delete((*imffields_unprotected).fld_list, cur);
|
||||
} else {
|
||||
cur = (*cur).next;
|
||||
}
|
||||
}
|
||||
// if move_to_encrypted {
|
||||
// mailimf_fields_add(imffields_encrypted, field);
|
||||
// cur = clist_delete((*imffields_unprotected).fld_list, cur);
|
||||
// } else {
|
||||
// cur = (*cur).next;
|
||||
// }
|
||||
// }
|
||||
|
||||
let subject = mailimf_subject_new("...".strdup());
|
||||
mailimf_fields_add(imffields_unprotected, mailimf_field_new_subject(subject));
|
||||
// let subject = mailimf_subject_new("...".strdup());
|
||||
// mailimf_fields_add(imffields_unprotected, mailimf_field_new_subject(subject));
|
||||
|
||||
wrapmime::append_ct_param(
|
||||
(*part_to_encrypt).mm_content_type,
|
||||
"protected-headers",
|
||||
"v1",
|
||||
)?;
|
||||
let plain = mmap_string_new(b"\x00" as *const u8 as *const libc::c_char);
|
||||
let mut col = 0;
|
||||
mailmime_write_mem(plain, &mut col, message_to_encrypt);
|
||||
mailmime_free(message_to_encrypt);
|
||||
// wrapmime::append_ct_param(
|
||||
// (*part_to_encrypt).mm_content_type,
|
||||
// "protected-headers",
|
||||
// "v1",
|
||||
// )?;
|
||||
// let plain = mmap_string_new(b"\x00" as *const u8 as *const libc::c_char);
|
||||
// let mut col = 0;
|
||||
// mailmime_write_mem(plain, &mut col, message_to_encrypt);
|
||||
// mailmime_free(message_to_encrypt);
|
||||
|
||||
ensure!(
|
||||
!(*plain).str_0.is_null() && (*plain).len > 0,
|
||||
"could not write/allocate"
|
||||
);
|
||||
// ensure!(
|
||||
// !(*plain).str_0.is_null() && (*plain).len > 0,
|
||||
// "could not write/allocate"
|
||||
// );
|
||||
|
||||
let ctext = pgp::pk_encrypt(
|
||||
std::slice::from_raw_parts((*plain).str_0 as *const u8, (*plain).len),
|
||||
&keyring,
|
||||
sign_key.as_ref(),
|
||||
);
|
||||
mmap_string_free(plain);
|
||||
// let ctext = pgp::pk_encrypt(
|
||||
// std::slice::from_raw_parts((*plain).str_0 as *const u8, (*plain).len),
|
||||
// &keyring,
|
||||
// sign_key.as_ref(),
|
||||
// );
|
||||
// mmap_string_free(plain);
|
||||
|
||||
let ctext_v = ctext?;
|
||||
// let ctext_v = ctext?;
|
||||
|
||||
// create MIME-structure that will contain the encrypted text
|
||||
let mut encrypted_part = new_data_part(
|
||||
ptr::null_mut(),
|
||||
0 as libc::size_t,
|
||||
"multipart/encrypted",
|
||||
MAILMIME_MECHANISM_BASE64,
|
||||
)?;
|
||||
let content = (*encrypted_part).mm_content_type;
|
||||
wrapmime::append_ct_param(content, "protocol", "application/pgp-encrypted")?;
|
||||
// // create MIME-structure that will contain the encrypted text
|
||||
// let mut encrypted_part = new_data_part(
|
||||
// ptr::null_mut(),
|
||||
// 0 as libc::size_t,
|
||||
// "multipart/encrypted",
|
||||
// MAILMIME_MECHANISM_BASE64,
|
||||
// )?;
|
||||
// let content = (*encrypted_part).mm_content_type;
|
||||
// wrapmime::append_ct_param(content, "protocol", "application/pgp-encrypted")?;
|
||||
|
||||
let version_mime = new_data_part(
|
||||
VERSION_CONTENT.as_mut_ptr() as *mut libc::c_void,
|
||||
strlen(VERSION_CONTENT.as_mut_ptr()),
|
||||
"application/pgp-encrypted",
|
||||
MAILMIME_MECHANISM_7BIT,
|
||||
)?;
|
||||
mailmime_smart_add_part(encrypted_part, version_mime);
|
||||
// let version_mime = new_data_part(
|
||||
// VERSION_CONTENT.as_mut_ptr() as *mut libc::c_void,
|
||||
// strlen(VERSION_CONTENT.as_mut_ptr()),
|
||||
// "application/pgp-encrypted",
|
||||
// MAILMIME_MECHANISM_7BIT,
|
||||
// )?;
|
||||
// mailmime_smart_add_part(encrypted_part, version_mime);
|
||||
|
||||
// we assume that ctext_v is not dropped until the end
|
||||
// of this if-scope
|
||||
let ctext_part = new_data_part(
|
||||
ctext_v.as_ptr() as *mut libc::c_void,
|
||||
ctext_v.len(),
|
||||
"application/octet-stream",
|
||||
MAILMIME_MECHANISM_7BIT,
|
||||
)?;
|
||||
// // we assume that ctext_v is not dropped until the end
|
||||
// // of this if-scope
|
||||
// let ctext_part = new_data_part(
|
||||
// ctext_v.as_ptr() as *mut libc::c_void,
|
||||
// ctext_v.len(),
|
||||
// "application/octet-stream",
|
||||
// MAILMIME_MECHANISM_7BIT,
|
||||
// )?;
|
||||
|
||||
mailmime_smart_add_part(encrypted_part, ctext_part);
|
||||
(*in_out_message).mm_data.mm_message.mm_msg_mime = encrypted_part;
|
||||
(*encrypted_part).mm_parent = in_out_message;
|
||||
let gossiped = !&gossip_headers.is_empty();
|
||||
factory.finalize_mime_message(in_out_message, true, gossiped)?;
|
||||
// mailmime_smart_add_part(encrypted_part, ctext_part);
|
||||
// (*in_out_message).mm_data.mm_message.mm_msg_mime = encrypted_part;
|
||||
// (*encrypted_part).mm_parent = in_out_message;
|
||||
// let gossiped = !&gossip_headers.is_empty();
|
||||
// factory.finalize_mime_message(in_out_message, true, gossiped)?;
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
// Ok(true)
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn try_decrypt(
|
||||
context: &Context,
|
||||
in_out_message: *mut Mailmime,
|
||||
) -> Result<(bool, HashSet<String>, HashSet<String>)> {
|
||||
// just a pointer into mailmime structure, must not be freed
|
||||
let imffields = mailmime_find_mailimf_fields(in_out_message);
|
||||
ensure!(
|
||||
!in_out_message.is_null() && !imffields.is_null(),
|
||||
"corrupt invalid mime inputs"
|
||||
);
|
||||
mail: &mailparse::ParsedMail<'_>,
|
||||
) -> Result<(Option<Vec<u8>>, HashSet<String>, HashSet<String>)> {
|
||||
use mailparse::MailHeaderMap;
|
||||
|
||||
let from = wrapmime::get_field_from(imffields)?;
|
||||
let message_time = wrapmime::get_field_date(imffields)?;
|
||||
let from = mail.headers.get_first_value("From")?.unwrap_or_default();
|
||||
let message_time = mail
|
||||
.headers
|
||||
.get_first_value("Date")?
|
||||
.and_then(|v| v.parse().ok())
|
||||
.unwrap_or_default();
|
||||
|
||||
let mut peerstate = None;
|
||||
let autocryptheader = Aheader::from_imffields(&from, imffields);
|
||||
let autocryptheader = Aheader::from_imffields(&from, &mail.headers);
|
||||
|
||||
if message_time > 0 {
|
||||
peerstate = Peerstate::from_addr(context, &context.sql, &from);
|
||||
@@ -282,9 +265,7 @@ pub fn try_decrypt(
|
||||
if let Some(ref header) = autocryptheader {
|
||||
peerstate.apply_header(&header, message_time);
|
||||
peerstate.save_to_db(&context.sql, false)?;
|
||||
} else if message_time > peerstate.last_seen_autocrypt
|
||||
&& !contains_report(in_out_message)
|
||||
{
|
||||
} else if message_time > peerstate.last_seen_autocrypt && !contains_report(mail) {
|
||||
peerstate.degrade_encryption(message_time);
|
||||
peerstate.save_to_db(&context.sql, false)?;
|
||||
}
|
||||
@@ -297,10 +278,9 @@ pub fn try_decrypt(
|
||||
/* possibly perform decryption */
|
||||
let mut private_keyring = Keyring::default();
|
||||
let mut public_keyring_for_validate = Keyring::default();
|
||||
let mut encrypted = false;
|
||||
let mut signatures = HashSet::default();
|
||||
let mut out_mail = None;
|
||||
let mut gossipped_addr = HashSet::default();
|
||||
|
||||
let mut signatures = HashSet::default();
|
||||
let self_addr = context.get_config(Config::ConfiguredAddr);
|
||||
|
||||
if let Some(self_addr) = self_addr {
|
||||
@@ -320,61 +300,60 @@ pub fn try_decrypt(
|
||||
}
|
||||
}
|
||||
|
||||
let mut gossip_headers = ptr::null_mut();
|
||||
encrypted = decrypt_if_autocrypt_message(
|
||||
out_mail = decrypt_if_autocrypt_message(
|
||||
context,
|
||||
in_out_message,
|
||||
mail,
|
||||
&private_keyring,
|
||||
&public_keyring_for_validate,
|
||||
&mut signatures,
|
||||
&mut gossip_headers,
|
||||
)?;
|
||||
if !gossip_headers.is_null() {
|
||||
gossipped_addr =
|
||||
update_gossip_peerstates(context, message_time, imffields, gossip_headers)?;
|
||||
unsafe { mailimf_fields_free(gossip_headers) };
|
||||
}
|
||||
|
||||
// TODO:
|
||||
// if !gossip_headers.is_empty() {
|
||||
// gossipped_addr =
|
||||
// update_gossip_peerstates(context, message_time, imffields, gossip_headers)?;
|
||||
// }
|
||||
}
|
||||
}
|
||||
Ok((encrypted, signatures, gossipped_addr))
|
||||
Ok((out_mail, signatures, gossipped_addr))
|
||||
}
|
||||
|
||||
fn new_data_part(
|
||||
data: *mut libc::c_void,
|
||||
data_bytes: libc::size_t,
|
||||
content_type: &str,
|
||||
default_encoding: u32,
|
||||
) -> Result<*mut Mailmime> {
|
||||
let content = new_content_type(&content_type)?;
|
||||
let mut encoding = ptr::null_mut();
|
||||
if wrapmime::content_type_needs_encoding(content) {
|
||||
encoding = unsafe { mailmime_mechanism_new(default_encoding as i32, ptr::null_mut()) };
|
||||
ensure!(!encoding.is_null(), "failed to create encoding");
|
||||
}
|
||||
let mime_fields = {
|
||||
unsafe {
|
||||
mailmime_fields_new_with_data(
|
||||
encoding,
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
)
|
||||
}
|
||||
};
|
||||
ensure!(!mime_fields.is_null(), "internal mime error");
|
||||
|
||||
let mime = unsafe { mailmime_new_empty(content, mime_fields) };
|
||||
ensure!(!mime.is_null(), "internal mime error");
|
||||
|
||||
if unsafe { (*mime).mm_type } == MAILMIME_SINGLE as libc::c_int {
|
||||
if !data.is_null() && data_bytes > 0 {
|
||||
unsafe { mailmime_set_body_text(mime, data as *mut libc::c_char, data_bytes) };
|
||||
}
|
||||
}
|
||||
|
||||
Ok(mime)
|
||||
}
|
||||
// fn new_data_part(
|
||||
// data: *mut libc::c_void,
|
||||
// data_bytes: libc::size_t,
|
||||
// content_type: &str,
|
||||
// default_encoding: u32,
|
||||
// ) -> Result<*mut Mailmime> {
|
||||
// let content = new_content_type(&content_type)?;
|
||||
// let mut encoding = ptr::null_mut();
|
||||
// if wrapmime::content_type_needs_encoding(content) {
|
||||
// encoding = { mailmime_mechanism_new(default_encoding as i32, ptr::null_mut()) };
|
||||
// ensure!(!encoding.is_null(), "failed to create encoding");
|
||||
// }
|
||||
// let mime_fields = {
|
||||
// {
|
||||
// mailmime_fields_new_with_data(
|
||||
// encoding,
|
||||
// ptr::null_mut(),
|
||||
// ptr::null_mut(),
|
||||
// ptr::null_mut(),
|
||||
// ptr::null_mut(),
|
||||
// )
|
||||
// }
|
||||
// };
|
||||
// ensure!(!mime_fields.is_null(), "internal mime error");
|
||||
//
|
||||
// let mime = { mailmime_new_empty(content, mime_fields) };
|
||||
// ensure!(!mime.is_null(), "internal mime error");
|
||||
//
|
||||
// if { (*mime).mm_type } == MAILMIME_SINGLE as libc::c_int {
|
||||
// if !data.is_null() && data_bytes > 0 {
|
||||
// { mailmime_set_body_text(mime, data as *mut libc::c_char, data_bytes) };
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// Ok(mime)
|
||||
// }
|
||||
|
||||
/// Load public key from database or generate a new one.
|
||||
///
|
||||
@@ -423,204 +402,158 @@ fn load_or_generate_self_public_key(context: &Context, self_addr: impl AsRef<str
|
||||
}
|
||||
}
|
||||
|
||||
fn update_gossip_peerstates(
|
||||
// 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>(
|
||||
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 unsafe { (*(*gossip_headers).fld_list).into_iter() } {
|
||||
let field = cur_data as *mut mailimf_field;
|
||||
if field.is_null() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let field = unsafe { *field };
|
||||
|
||||
if field.fld_type == MAILIMF_FIELD_OPTIONAL_FIELD as libc::c_int {
|
||||
let optional_field = unsafe { field.fld_data.fld_optional_field };
|
||||
if optional_field.is_null() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let optional_field = unsafe { *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(
|
||||
context: &Context,
|
||||
mime_undetermined: *mut Mailmime,
|
||||
mail: &mailparse::ParsedMail<'a>,
|
||||
private_keyring: &Keyring,
|
||||
public_keyring_for_validate: &Keyring,
|
||||
ret_valid_signatures: &mut HashSet<String>,
|
||||
ret_gossip_headers: *mut *mut mailimf_fields,
|
||||
) -> Result<bool> {
|
||||
/* The returned bool is true if we detected an Autocrypt-encrypted
|
||||
message and successfully decrypted it. Decryption then modifies the
|
||||
passed in mime structure in place. The returned bool is false
|
||||
if it was not an Autocrypt message.
|
||||
) -> Result<Option<Vec<u8>>> {
|
||||
// The returned bool is true if we detected an Autocrypt-encrypted
|
||||
// message and successfully decrypted it. Decryption then modifies the
|
||||
// passed in mime structure in place. The returned bool is false
|
||||
// if it was not an Autocrypt message.
|
||||
//
|
||||
// Errors are returned for failures related to decryption of AC-messages.
|
||||
|
||||
Errors are returned for failures related to decryption of AC-messages.
|
||||
*/
|
||||
ensure!(!mime_undetermined.is_null(), "Invalid mime reference");
|
||||
|
||||
let (mime, encrypted_data_part) = match wrapmime::get_autocrypt_mime(mime_undetermined) {
|
||||
let encrypted_data_part = match wrapmime::get_autocrypt_mime(mail) {
|
||||
Err(_) => {
|
||||
// not a proper autocrypt message, abort and ignore
|
||||
return Ok(false);
|
||||
return Ok(None);
|
||||
}
|
||||
Ok(res) => res,
|
||||
};
|
||||
|
||||
let decrypted_mime = decrypt_part(
|
||||
let decrypted = decrypt_part(
|
||||
context,
|
||||
encrypted_data_part,
|
||||
private_keyring,
|
||||
public_keyring_for_validate,
|
||||
ret_valid_signatures,
|
||||
)?;
|
||||
// decrypted_mime is a dangling pointer which we now put into mailmime's Ownership
|
||||
unsafe {
|
||||
mailmime_substitute(mime, decrypted_mime);
|
||||
mailmime_free(mime);
|
||||
}
|
||||
|
||||
// finally, let's also return gossip headers
|
||||
// XXX better return parsed headers so that upstream
|
||||
// does not need to dive into mmime-stuff again.
|
||||
unsafe {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
// {
|
||||
// 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(true)
|
||||
Ok(decrypted)
|
||||
}
|
||||
|
||||
/// Returns Ok(None) if nothing encrypted was found.
|
||||
fn decrypt_part(
|
||||
_context: &Context,
|
||||
mime: *mut Mailmime,
|
||||
mail: &mailparse::ParsedMail<'_>,
|
||||
private_keyring: &Keyring,
|
||||
public_keyring_for_validate: &Keyring,
|
||||
ret_valid_signatures: &mut HashSet<String>,
|
||||
) -> Result<*mut Mailmime> {
|
||||
let mime_data: *mut mailmime_data;
|
||||
let mut mime_transfer_encoding = MAILMIME_MECHANISM_BINARY as libc::c_int;
|
||||
) -> Result<Option<Vec<u8>>> {
|
||||
ensure!(
|
||||
wrapmime::has_decryptable_data(mail),
|
||||
"No decryptable data found"
|
||||
);
|
||||
|
||||
unsafe {
|
||||
mime_data = (*mime).mm_data.mm_single;
|
||||
}
|
||||
if !wrapmime::has_decryptable_data(mime_data) {
|
||||
return Ok(ptr::null_mut());
|
||||
}
|
||||
|
||||
if let Some(enc) = wrapmime::get_mime_transfer_encoding(mime) {
|
||||
mime_transfer_encoding = enc;
|
||||
}
|
||||
|
||||
let data: Vec<u8> = wrapmime::decode_dt_data(mime_data, mime_transfer_encoding)?;
|
||||
|
||||
let mut ret_decrypted_mime = ptr::null_mut();
|
||||
let data = mail.get_body_raw()?;
|
||||
|
||||
if has_decrypted_pgp_armor(&data) {
|
||||
// we should only have one decryption happening
|
||||
ensure!(ret_valid_signatures.is_empty(), "corrupt signatures");
|
||||
|
||||
let plain = match pgp::pk_decrypt(
|
||||
let plain = pgp::pk_decrypt(
|
||||
&data,
|
||||
&private_keyring,
|
||||
&public_keyring_for_validate,
|
||||
Some(ret_valid_signatures),
|
||||
) {
|
||||
Ok(plain) => {
|
||||
ensure!(!ret_valid_signatures.is_empty(), "no valid signatures");
|
||||
plain
|
||||
}
|
||||
Err(err) => bail!("could not decrypt: {}", err),
|
||||
};
|
||||
let plain_bytes = plain.len();
|
||||
let plain_buf = plain.as_ptr() as *const libc::c_char;
|
||||
)?;
|
||||
|
||||
let mut index = 0;
|
||||
let mut decrypted_mime = ptr::null_mut();
|
||||
if unsafe {
|
||||
mailmime_parse(
|
||||
plain_buf as *const _,
|
||||
plain_bytes,
|
||||
&mut index,
|
||||
&mut decrypted_mime,
|
||||
)
|
||||
} != MAIL_NO_ERROR as libc::c_int
|
||||
|| decrypted_mime.is_null()
|
||||
{
|
||||
if !decrypted_mime.is_null() {
|
||||
unsafe { mailmime_free(decrypted_mime) };
|
||||
}
|
||||
} else {
|
||||
// decrypted_mime points into `plain`.
|
||||
// FIXME(@dignifiedquire): this still leaks memory I believe, as mailmime_free
|
||||
// does not free the underlying buffer. But for now we have to live with it
|
||||
std::mem::forget(plain);
|
||||
ret_decrypted_mime = decrypted_mime;
|
||||
}
|
||||
ensure!(!ret_valid_signatures.is_empty(), "no valid signatures");
|
||||
return Ok(Some(plain));
|
||||
}
|
||||
|
||||
Ok(ret_decrypted_mime)
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn has_decrypted_pgp_armor(input: &[u8]) -> bool {
|
||||
@@ -644,36 +577,8 @@ fn has_decrypted_pgp_armor(input: &[u8]) -> bool {
|
||||
/// However, Delta Chat itself has no problem with encrypted multipart/report
|
||||
/// parts and MUAs should be encouraged to encrpyt multipart/reports as well so
|
||||
/// that we could use the normal Autocrypt processing.
|
||||
fn contains_report(mime: *mut Mailmime) -> bool {
|
||||
assert!(!mime.is_null());
|
||||
let mime = unsafe { *mime };
|
||||
|
||||
if mime.mm_type == MAILMIME_MULTIPLE as libc::c_int {
|
||||
let tp_type = unsafe { (*(*mime.mm_content_type).ct_type).tp_type };
|
||||
let ct_type =
|
||||
unsafe { (*(*(*mime.mm_content_type).ct_type).tp_data.tp_composite_type).ct_type };
|
||||
|
||||
if tp_type == MAILMIME_TYPE_COMPOSITE_TYPE as libc::c_int
|
||||
&& ct_type == MAILMIME_COMPOSITE_TYPE_MULTIPART as libc::c_int
|
||||
&& to_string_lossy(unsafe { (*mime.mm_content_type).ct_subtype }) == "report"
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
for cur_data in unsafe { (*(*mime.mm_mime_fields).fld_list).into_iter() } {
|
||||
if contains_report(cur_data as *mut Mailmime) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else if mime.mm_type == MAILMIME_MESSAGE as libc::c_int {
|
||||
let m = unsafe { mime.mm_data.mm_message.mm_msg_mime };
|
||||
|
||||
if contains_report(m) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
fn contains_report(mail: &mailparse::ParsedMail<'_>) -> bool {
|
||||
mail.ctype.mimetype == "multipart/report"
|
||||
}
|
||||
|
||||
/// Ensures a private key exists for the configured user.
|
||||
@@ -699,7 +604,6 @@ pub fn ensure_secret_key_exists(context: &Context) -> Result<String> {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use libc::free;
|
||||
|
||||
use crate::test_utils::*;
|
||||
|
||||
@@ -720,46 +624,46 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mailmime_parse() {
|
||||
let plain = b"Chat-Disposition-Notification-To: holger@deltachat.de
|
||||
Chat-Group-ID: CovhGgau8M-
|
||||
Chat-Group-Name: Delta Chat Dev
|
||||
Subject: =?utf-8?Q?Chat=3A?= Delta Chat =?utf-8?Q?Dev=3A?= sidenote for
|
||||
=?utf-8?Q?all=3A?= rust core master ...
|
||||
Content-Type: text/plain; charset=\"utf-8\"; protected-headers=\"v1\"
|
||||
Content-Transfer-Encoding: quoted-printable
|
||||
// #[test]
|
||||
// fn test_mailmime_parse() {
|
||||
// let plain = b"Chat-Disposition-Notification-To: holger@deltachat.de
|
||||
// Chat-Group-ID: CovhGgau8M-
|
||||
// Chat-Group-Name: Delta Chat Dev
|
||||
// Subject: =?utf-8?Q?Chat=3A?= Delta Chat =?utf-8?Q?Dev=3A?= sidenote for
|
||||
// =?utf-8?Q?all=3A?= rust core master ...
|
||||
// Content-Type: text/plain; charset=\"utf-8\"; protected-headers=\"v1\"
|
||||
// Content-Transfer-Encoding: quoted-printable
|
||||
|
||||
sidenote for all: rust core master is broken currently ... so dont recomm=
|
||||
end to try to run with desktop or ios unless you are ready to hunt bugs
|
||||
// sidenote for all: rust core master is broken currently ... so dont recomm=
|
||||
// end to try to run with desktop or ios unless you are ready to hunt bugs
|
||||
|
||||
-- =20
|
||||
Sent with my Delta Chat Messenger: https://delta.chat";
|
||||
let plain_bytes = plain.len();
|
||||
let plain_buf = plain.as_ptr() as *const libc::c_char;
|
||||
// -- =20
|
||||
// Sent with my Delta Chat Messenger: https://delta.chat";
|
||||
// let plain_bytes = plain.len();
|
||||
// let plain_buf = plain.as_ptr() as *const libc::c_char;
|
||||
|
||||
let mut index = 0;
|
||||
let mut decrypted_mime = std::ptr::null_mut();
|
||||
// let mut index = 0;
|
||||
// let mut decrypted_mime = std::ptr::null_mut();
|
||||
|
||||
let res = unsafe {
|
||||
mailmime_parse(
|
||||
plain_buf as *const _,
|
||||
plain_bytes,
|
||||
&mut index,
|
||||
&mut decrypted_mime,
|
||||
)
|
||||
};
|
||||
unsafe {
|
||||
let msg1 = (*decrypted_mime).mm_data.mm_message.mm_msg_mime;
|
||||
let data = mailmime_transfer_decode(msg1).unwrap();
|
||||
println!("{:?}", String::from_utf8_lossy(&data));
|
||||
}
|
||||
// let res = {
|
||||
// mailmime_parse(
|
||||
// plain_buf as *const _,
|
||||
// plain_bytes,
|
||||
// &mut index,
|
||||
// &mut decrypted_mime,
|
||||
// )
|
||||
// };
|
||||
// {
|
||||
// let msg1 = (*decrypted_mime).mm_data.mm_message.mm_msg_mime;
|
||||
// let data = mailmime_transfer_decode(msg1).unwrap();
|
||||
// println!("{:?}", String::from_utf8_lossy(&data));
|
||||
// }
|
||||
|
||||
assert_eq!(res, 0);
|
||||
assert!(!decrypted_mime.is_null());
|
||||
// assert_eq!(res, 0);
|
||||
// assert!(!decrypted_mime.is_null());
|
||||
|
||||
unsafe { free(decrypted_mime as *mut _) };
|
||||
}
|
||||
// { free(decrypted_mime as *mut _) };
|
||||
// }
|
||||
|
||||
mod load_or_generate_self_public_key {
|
||||
use super::*;
|
||||
|
||||
@@ -28,6 +28,8 @@ pub enum Error {
|
||||
InvalidMsgId,
|
||||
#[fail(display = "Watch folder not found {:?}", _0)]
|
||||
WatchFolderNotFound(String),
|
||||
#[fail(display = "Inalid Email: {:?}", _0)]
|
||||
MailParseError(mailparse::MailParseError),
|
||||
}
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
@@ -98,6 +100,12 @@ impl From<crate::message::InvalidMsgId> for Error {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<mailparse::MailParseError> for Error {
|
||||
fn from(err: mailparse::MailParseError) -> Error {
|
||||
Error::MailParseError(err)
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! bail {
|
||||
($e:expr) => {
|
||||
|
||||
@@ -825,9 +825,7 @@ impl Imap {
|
||||
|
||||
if !is_deleted && msg.body().is_some() {
|
||||
let body = msg.body().unwrap_or_default();
|
||||
unsafe {
|
||||
dc_receive_imf(context, &body, folder.as_ref(), server_uid, flags as u32);
|
||||
}
|
||||
dc_receive_imf(context, &body, folder.as_ref(), server_uid, flags as u32);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@ use crate::config::Config;
|
||||
use crate::configure::*;
|
||||
use crate::constants::*;
|
||||
use crate::context::Context;
|
||||
use crate::dc_mimeparser::SystemMessage;
|
||||
use crate::dc_tools::*;
|
||||
use crate::e2ee;
|
||||
use crate::error::*;
|
||||
@@ -20,6 +19,7 @@ use crate::events::Event;
|
||||
use crate::job::*;
|
||||
use crate::key::*;
|
||||
use crate::message::{Message, MsgId};
|
||||
use crate::mimeparser::SystemMessage;
|
||||
use crate::param::*;
|
||||
use crate::pgp;
|
||||
use crate::sql::{self, Sql};
|
||||
|
||||
11
src/job.rs
11
src/job.rs
@@ -608,7 +608,7 @@ pub fn job_send_msg(context: &Context, msg_id: MsgId) -> Result<(), Error> {
|
||||
mimefactory.msg.try_calc_and_set_dimensions(context).ok();
|
||||
|
||||
/* create message */
|
||||
if let Err(msg) = unsafe { mimefactory.render() } {
|
||||
if let Err(msg) = mimefactory.render() {
|
||||
let e = msg.to_string();
|
||||
message::set_msg_failed(context, msg_id, Some(e));
|
||||
return Err(msg);
|
||||
@@ -867,7 +867,7 @@ fn suspend_smtp_thread(context: &Context, suspend: bool) {
|
||||
|
||||
fn send_mdn(context: &Context, msg_id: MsgId) -> Result<(), Error> {
|
||||
let mut mimefactory = MimeFactory::load_mdn(context, msg_id)?;
|
||||
unsafe { mimefactory.render()? };
|
||||
mimefactory.render()?;
|
||||
add_smtp_job(context, Action::SendMdn, &mut mimefactory)?;
|
||||
|
||||
Ok(())
|
||||
@@ -880,12 +880,7 @@ fn add_smtp_job(context: &Context, action: Action, mimefactory: &MimeFactory) ->
|
||||
"no recipients for smtp job set"
|
||||
);
|
||||
let mut param = Params::new();
|
||||
let bytes = unsafe {
|
||||
std::slice::from_raw_parts(
|
||||
(*mimefactory.out).str_0 as *const u8,
|
||||
(*mimefactory.out).len,
|
||||
)
|
||||
};
|
||||
let bytes = &mimefactory.out;
|
||||
let blob = BlobObject::create(context, &mimefactory.rfc724_mid, bytes)?;
|
||||
let recipients = mimefactory.recipients_addr.join("\x1e");
|
||||
param.set(Param::File, blob.as_name());
|
||||
|
||||
@@ -51,6 +51,7 @@ mod login_param;
|
||||
pub mod lot;
|
||||
pub mod message;
|
||||
mod mimefactory;
|
||||
pub mod mimeparser;
|
||||
pub mod oauth2;
|
||||
mod param;
|
||||
pub mod peerstate;
|
||||
@@ -63,14 +64,13 @@ pub mod stock;
|
||||
mod token;
|
||||
#[macro_use]
|
||||
mod wrapmime;
|
||||
mod dehtml;
|
||||
|
||||
pub mod dc_array;
|
||||
pub mod dc_mimeparser;
|
||||
pub mod dc_receive_imf;
|
||||
mod dc_simplify;
|
||||
mod dc_strencode;
|
||||
pub mod dc_tools;
|
||||
mod dehtml;
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_utils;
|
||||
|
||||
@@ -8,12 +8,12 @@ use crate::chat;
|
||||
use crate::config::Config;
|
||||
use crate::constants::*;
|
||||
use crate::context::*;
|
||||
use crate::dc_mimeparser::SystemMessage;
|
||||
use crate::dc_tools::*;
|
||||
use crate::error::Error;
|
||||
use crate::events::Event;
|
||||
use crate::job::*;
|
||||
use crate::message::{Message, MsgId};
|
||||
use crate::mimeparser::SystemMessage;
|
||||
use crate::param::*;
|
||||
use crate::sql;
|
||||
use crate::stock::StockMessage;
|
||||
|
||||
@@ -7,12 +7,12 @@ use crate::chat::{self, Chat};
|
||||
use crate::constants::*;
|
||||
use crate::contact::*;
|
||||
use crate::context::*;
|
||||
use crate::dc_mimeparser::SystemMessage;
|
||||
use crate::dc_tools::*;
|
||||
use crate::error::Error;
|
||||
use crate::events::Event;
|
||||
use crate::job::*;
|
||||
use crate::lot::{Lot, LotState, Meaning};
|
||||
use crate::mimeparser::SystemMessage;
|
||||
use crate::param::*;
|
||||
use crate::pgp::*;
|
||||
use crate::sql;
|
||||
|
||||
1198
src/mimefactory.rs
1198
src/mimefactory.rs
File diff suppressed because it is too large
Load Diff
1053
src/mimeparser.rs
Normal file
1053
src/mimeparser.rs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -7,8 +7,8 @@ use num_traits::FromPrimitive;
|
||||
|
||||
use crate::blob::{BlobError, BlobObject};
|
||||
use crate::context::Context;
|
||||
use crate::dc_mimeparser::SystemMessage;
|
||||
use crate::error;
|
||||
use crate::mimeparser::SystemMessage;
|
||||
|
||||
/// Available param keys.
|
||||
#[derive(PartialEq, Eq, Debug, Clone, Copy, Hash, PartialOrd, Ord, FromPrimitive)]
|
||||
|
||||
@@ -8,13 +8,13 @@ use crate::config::*;
|
||||
use crate::constants::*;
|
||||
use crate::contact::*;
|
||||
use crate::context::Context;
|
||||
use crate::dc_mimeparser::*;
|
||||
use crate::e2ee::*;
|
||||
use crate::error::Error;
|
||||
use crate::events::Event;
|
||||
use crate::key::*;
|
||||
use crate::lot::LotState;
|
||||
use crate::message::Message;
|
||||
use crate::mimeparser::*;
|
||||
use crate::param::*;
|
||||
use crate::peerstate::*;
|
||||
use crate::qr::check_qr;
|
||||
|
||||
680
src/wrapmime.rs
680
src/wrapmime.rs
@@ -1,289 +1,79 @@
|
||||
use std::collections::HashSet;
|
||||
use std::ffi::CString;
|
||||
use std::ptr;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
use mailparse::ParsedMail;
|
||||
|
||||
use crate::contact::addr_normalize;
|
||||
use crate::dc_strencode::*;
|
||||
use crate::dc_tools::*;
|
||||
use crate::error::Error;
|
||||
use mmime::clist::*;
|
||||
// use mmime::display::*;
|
||||
use mmime::mailimf::mailimf_msg_id_parse;
|
||||
use mmime::mailimf::types::*;
|
||||
use mmime::mailimf::types_helper::*;
|
||||
use mmime::mailmime::content::*;
|
||||
use mmime::mailmime::disposition::*;
|
||||
use mmime::mailmime::types::*;
|
||||
use mmime::mailmime::types_helper::*;
|
||||
use mmime::mailmime::*;
|
||||
use mmime::mmapstring::*;
|
||||
use mmime::other::*;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! clist_append {
|
||||
($clist:expr, $item:expr) => {
|
||||
if clist_insert_after(
|
||||
$clist as *mut clist,
|
||||
(*$clist).last,
|
||||
$item as *mut libc::c_void,
|
||||
) != 0
|
||||
{
|
||||
bail!("could not allocate or append list item");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**************************************
|
||||
* mime parsing API
|
||||
**************************************/
|
||||
|
||||
pub fn get_ct_subtype(mime: *mut Mailmime) -> Option<String> {
|
||||
unsafe {
|
||||
let ct: *mut mailmime_content = (*mime).mm_content_type;
|
||||
|
||||
if !ct.is_null() && !(*ct).ct_subtype.is_null() {
|
||||
Some(to_string_lossy((*ct).ct_subtype))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_message_id(message_id: &[u8]) -> Result<String, Error> {
|
||||
let mut dummy = 0;
|
||||
let mut rfc724_mid_c = std::ptr::null_mut();
|
||||
if unsafe {
|
||||
mailimf_msg_id_parse(
|
||||
message_id.as_ptr() as *const libc::c_char,
|
||||
message_id.len(),
|
||||
&mut dummy,
|
||||
&mut rfc724_mid_c,
|
||||
)
|
||||
} == MAIL_NO_ERROR as libc::c_int
|
||||
&& !rfc724_mid_c.is_null()
|
||||
{
|
||||
let res = to_string_lossy(rfc724_mid_c);
|
||||
unsafe { libc::free(rfc724_mid_c.cast()) };
|
||||
Ok(res)
|
||||
} else {
|
||||
bail!(
|
||||
"could not parse message_id: {}",
|
||||
String::from_utf8_lossy(message_id)
|
||||
);
|
||||
let value = std::str::from_utf8(message_id)?;
|
||||
let addrs = mailparse::addrparse(value)
|
||||
.map_err(|err| format_err!("failed to parse message id {:?}", err))?;
|
||||
|
||||
if let Some(info) = addrs.extract_single_info() {
|
||||
return Ok(info.addr);
|
||||
}
|
||||
|
||||
bail!(
|
||||
"could not parse message_id: {}",
|
||||
String::from_utf8_lossy(message_id)
|
||||
);
|
||||
}
|
||||
|
||||
pub fn get_autocrypt_mime(
|
||||
mime_undetermined: *mut Mailmime,
|
||||
) -> Result<(*mut Mailmime, *mut Mailmime), Error> {
|
||||
/* return Result with two mime pointers:
|
||||
/// Returns a reference to the encrypted payload and validates the autocrypt structure.
|
||||
pub fn get_autocrypt_mime<'a, 'b>(mail: &'a ParsedMail<'b>) -> Result<&'a ParsedMail<'b>, Error> {
|
||||
ensure!(
|
||||
mail.ctype.mimetype == "multipart/encrypted",
|
||||
"Not a multipart/encrypted message"
|
||||
);
|
||||
ensure!(
|
||||
mail.subparts.len() == 2,
|
||||
"Invalid Autocrypt Level 1 Mime Parts"
|
||||
);
|
||||
|
||||
First mime pointer is to the multipart-mime message
|
||||
(which is replaced with a decrypted version later)
|
||||
ensure!(
|
||||
mail.subparts[0].ctype.mimetype == "application/pgp-encrypted",
|
||||
"Invalid Autocrypt Level 1 version part"
|
||||
);
|
||||
|
||||
Second one is to the encrypted payload.
|
||||
For non-autocrypt message an Error is returned.
|
||||
*/
|
||||
unsafe {
|
||||
ensure!(
|
||||
(*mime_undetermined).mm_type == MAILMIME_MESSAGE as libc::c_int,
|
||||
"Not a root mime message"
|
||||
);
|
||||
let mime = (*mime_undetermined).mm_data.mm_message.mm_msg_mime;
|
||||
ensure!(
|
||||
mail.subparts[1].ctype.mimetype == "application/octetstream",
|
||||
"Invalid Autocrypt Level 1 encrypted part"
|
||||
);
|
||||
|
||||
ensure!(
|
||||
(*mime).mm_type == MAILMIME_MULTIPLE as libc::c_int
|
||||
&& "encrypted" == get_ct_subtype(mime).unwrap_or_default(),
|
||||
"Not a multipart/encrypted message"
|
||||
);
|
||||
let parts: Vec<_> = (*(*mime).mm_data.mm_multipart.mm_mp_list)
|
||||
.into_iter()
|
||||
.map(|c| c as *mut Mailmime)
|
||||
.collect();
|
||||
ensure!(parts.len() == 2, "Invalid Autocrypt Level 1 Mime Parts");
|
||||
// XXX ensure protocol-parameter "application/pgp-encrypted")
|
||||
// XXX ensure wrapmime::get_content_type(parts[1])) == "application/octetstream"
|
||||
// a proper OpenPGP multipart/encrypted Autocrypt Level 1 message
|
||||
// https://tools.ietf.org/html/rfc3156.html#section-4
|
||||
Ok((mime, parts[1]))
|
||||
}
|
||||
Ok(&mail.subparts[1])
|
||||
}
|
||||
|
||||
pub fn has_decryptable_data(mime_data: *mut mailmime_data) -> bool {
|
||||
/* MAILMIME_DATA_FILE indicates, the data is in a file; AFAIK this is not used on parsing */
|
||||
unsafe {
|
||||
(*mime_data).dt_type == MAILMIME_DATA_TEXT as libc::c_int
|
||||
&& !(*mime_data).dt_data.dt_text.dt_data.is_null()
|
||||
&& (*mime_data).dt_data.dt_text.dt_length > 0
|
||||
}
|
||||
pub fn has_decryptable_data(mail: &ParsedMail<'_>) -> bool {
|
||||
false
|
||||
// /* MAILMIME_DATA_FILE indicates, the data is in a file; AFAIK this is not used on parsing */
|
||||
// {
|
||||
// (*mime_data).dt_type == MAILMIME_DATA_TEXT as libc::c_int
|
||||
// && !(*mime_data).dt_data.dt_text.dt_data.is_null()
|
||||
// && (*mime_data).dt_data.dt_text.dt_length > 0
|
||||
// }
|
||||
}
|
||||
|
||||
pub fn get_field_from(imffields: *mut mailimf_fields) -> Result<String, Error> {
|
||||
let field = mailimf_find_field(imffields, MAILIMF_FIELD_FROM as libc::c_int);
|
||||
if !field.is_null() && unsafe { !(*field).fld_data.fld_from.is_null() } {
|
||||
let mb_list = unsafe { (*(*field).fld_data.fld_from).frm_mb_list };
|
||||
if let Some(addr) = mailimf_find_first_addr(mb_list) {
|
||||
return Ok(addr);
|
||||
}
|
||||
}
|
||||
bail!("not From field found");
|
||||
}
|
||||
|
||||
pub fn get_field_date(imffields: *mut mailimf_fields) -> Result<i64, Error> {
|
||||
let field = mailimf_find_field(imffields, MAILIMF_FIELD_ORIG_DATE as libc::c_int);
|
||||
let mut message_time = 0;
|
||||
|
||||
if !field.is_null() && unsafe { !(*field).fld_data.fld_orig_date.is_null() } {
|
||||
let orig_date = unsafe { (*field).fld_data.fld_orig_date };
|
||||
|
||||
if !orig_date.is_null() {
|
||||
let dt = unsafe { (*orig_date).dt_date_time };
|
||||
message_time = dc_timestamp_from_date(dt);
|
||||
if message_time != 0 && message_time > time() {
|
||||
message_time = time()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(message_time)
|
||||
}
|
||||
|
||||
fn mailimf_get_recipients_add_addr(recipients: &mut HashSet<String>, mb: *mut mailimf_mailbox) {
|
||||
if !mb.is_null() {
|
||||
let addr = to_string_lossy(unsafe { (*mb).mb_addr_spec });
|
||||
let addr_norm = addr_normalize(&addr);
|
||||
recipients.insert(addr_norm.into());
|
||||
}
|
||||
}
|
||||
|
||||
/*the result is a pointer to mime, must not be freed*/
|
||||
pub fn mailimf_find_field(
|
||||
header: *mut mailimf_fields,
|
||||
wanted_fld_type: libc::c_int,
|
||||
) -> *mut mailimf_field {
|
||||
if header.is_null() {
|
||||
return ptr::null_mut();
|
||||
}
|
||||
|
||||
let header = unsafe { (*header) };
|
||||
if header.fld_list.is_null() {
|
||||
return ptr::null_mut();
|
||||
}
|
||||
|
||||
for cur in unsafe { &(*header.fld_list) } {
|
||||
let field = cur as *mut mailimf_field;
|
||||
if !field.is_null() {
|
||||
if unsafe { (*field).fld_type } == wanted_fld_type {
|
||||
return field;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ptr::null_mut()
|
||||
}
|
||||
|
||||
/*the result is a pointer to mime, must not be freed*/
|
||||
pub fn mailmime_find_mailimf_fields(mime: *mut Mailmime) -> *mut mailimf_fields {
|
||||
if mime.is_null() {
|
||||
return ptr::null_mut();
|
||||
}
|
||||
|
||||
match unsafe { (*mime).mm_type as _ } {
|
||||
MAILMIME_MULTIPLE => {
|
||||
for cur_data in unsafe { (*(*mime).mm_data.mm_multipart.mm_mp_list).into_iter() } {
|
||||
let header = mailmime_find_mailimf_fields(cur_data as *mut _);
|
||||
if !header.is_null() {
|
||||
return header;
|
||||
}
|
||||
}
|
||||
}
|
||||
MAILMIME_MESSAGE => return unsafe { (*mime).mm_data.mm_message.mm_fields },
|
||||
_ => {}
|
||||
}
|
||||
|
||||
ptr::null_mut()
|
||||
}
|
||||
|
||||
pub unsafe fn mailimf_find_optional_field(
|
||||
header: *mut mailimf_fields,
|
||||
wanted_fld_name: *const libc::c_char,
|
||||
) -> *mut mailimf_optional_field {
|
||||
if header.is_null() || (*header).fld_list.is_null() {
|
||||
return ptr::null_mut();
|
||||
}
|
||||
for cur_data in (*(*header).fld_list).into_iter() {
|
||||
let field: *mut mailimf_field = cur_data as *mut _;
|
||||
|
||||
if (*field).fld_type == MAILIMF_FIELD_OPTIONAL_FIELD as libc::c_int {
|
||||
let optional_field: *mut mailimf_optional_field = (*field).fld_data.fld_optional_field;
|
||||
if !optional_field.is_null()
|
||||
&& !(*optional_field).fld_name.is_null()
|
||||
&& !(*optional_field).fld_value.is_null()
|
||||
&& strcasecmp((*optional_field).fld_name, wanted_fld_name) == 0i32
|
||||
{
|
||||
return optional_field;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ptr::null_mut()
|
||||
}
|
||||
|
||||
pub fn mailimf_get_recipients(imffields: *mut mailimf_fields) -> HashSet<String> {
|
||||
/* returned addresses are normalized. */
|
||||
// returned addresses are normalized.
|
||||
pub fn mailimf_get_recipients(headers: &HashMap<String, String>) -> HashSet<String> {
|
||||
let mut recipients: HashSet<String> = Default::default();
|
||||
|
||||
for cur in unsafe { (*(*imffields).fld_list).into_iter() } {
|
||||
let fld = cur as *mut mailimf_field;
|
||||
|
||||
let fld_to: *mut mailimf_to;
|
||||
let fld_cc: *mut mailimf_cc;
|
||||
|
||||
let mut addr_list: *mut mailimf_address_list = ptr::null_mut();
|
||||
if fld.is_null() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let fld = unsafe { *fld };
|
||||
|
||||
// TODO match on enums /rtn
|
||||
match fld.fld_type {
|
||||
13 => {
|
||||
fld_to = unsafe { fld.fld_data.fld_to };
|
||||
if !fld_to.is_null() {
|
||||
addr_list = unsafe { (*fld_to).to_addr_list };
|
||||
}
|
||||
}
|
||||
14 => {
|
||||
fld_cc = unsafe { fld.fld_data.fld_cc };
|
||||
if !fld_cc.is_null() {
|
||||
addr_list = unsafe { (*fld_cc).cc_addr_list };
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
if !addr_list.is_null() {
|
||||
for cur2 in unsafe { &(*(*addr_list).ad_list) } {
|
||||
let adr = cur2 as *mut mailimf_address;
|
||||
|
||||
if adr.is_null() {
|
||||
continue;
|
||||
}
|
||||
let adr = unsafe { *adr };
|
||||
|
||||
if adr.ad_type == MAILIMF_ADDRESS_MAILBOX as libc::c_int {
|
||||
mailimf_get_recipients_add_addr(&mut recipients, unsafe {
|
||||
adr.ad_data.ad_mailbox
|
||||
});
|
||||
} else if adr.ad_type == MAILIMF_ADDRESS_GROUP as libc::c_int {
|
||||
let group = unsafe { adr.ad_data.ad_group };
|
||||
if !group.is_null() && unsafe { !(*group).grp_mb_list.is_null() } {
|
||||
for cur3 in unsafe { &(*(*(*group).grp_mb_list).mb_list) } {
|
||||
mailimf_get_recipients_add_addr(
|
||||
&mut recipients,
|
||||
cur3 as *mut mailimf_mailbox,
|
||||
);
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -294,258 +84,154 @@ pub fn mailimf_get_recipients(imffields: *mut mailimf_fields) -> HashSet<String>
|
||||
recipients
|
||||
}
|
||||
|
||||
pub fn mailmime_transfer_decode(mime: *mut Mailmime) -> Result<Vec<u8>, Error> {
|
||||
ensure!(!mime.is_null(), "invalid inputs");
|
||||
|
||||
let mime_transfer_encoding =
|
||||
get_mime_transfer_encoding(mime).unwrap_or(MAILMIME_MECHANISM_BINARY as i32);
|
||||
|
||||
let mime_data = unsafe { (*mime).mm_data.mm_single };
|
||||
|
||||
decode_dt_data(mime_data, mime_transfer_encoding)
|
||||
}
|
||||
|
||||
pub fn get_mime_transfer_encoding(mime: *mut Mailmime) -> Option<libc::c_int> {
|
||||
unsafe {
|
||||
let mm_mime_fields = (*mime).mm_mime_fields;
|
||||
if !mm_mime_fields.is_null() {
|
||||
for cur_data in (*(*mm_mime_fields).fld_list).into_iter() {
|
||||
let field: *mut mailmime_field = cur_data as *mut _;
|
||||
if (*field).fld_type == MAILMIME_FIELD_TRANSFER_ENCODING as libc::c_int
|
||||
&& !(*field).fld_data.fld_encoding.is_null()
|
||||
{
|
||||
return Some((*(*field).fld_data.fld_encoding).enc_type);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn decode_dt_data(
|
||||
mime_data: *mut mailmime_data,
|
||||
mime_transfer_encoding: libc::c_int,
|
||||
) -> Result<Vec<u8>, Error> {
|
||||
// Decode data according to mime_transfer_encoding
|
||||
// returns Ok with a (decoded_data,decoded_data_bytes) pointer
|
||||
// where the caller must make sure to free it.
|
||||
// It may return Ok(ptr::null_mut(), 0)
|
||||
if mime_transfer_encoding == MAILMIME_MECHANISM_7BIT as libc::c_int
|
||||
|| mime_transfer_encoding == MAILMIME_MECHANISM_8BIT as libc::c_int
|
||||
|| mime_transfer_encoding == MAILMIME_MECHANISM_BINARY as libc::c_int
|
||||
{
|
||||
let decoded_data = unsafe { (*mime_data).dt_data.dt_text.dt_data };
|
||||
let decoded_data_bytes = unsafe { (*mime_data).dt_data.dt_text.dt_length };
|
||||
|
||||
if decoded_data.is_null() || decoded_data_bytes == 0 {
|
||||
bail!("No data to decode found");
|
||||
} else {
|
||||
let result = unsafe {
|
||||
std::slice::from_raw_parts(decoded_data as *const u8, decoded_data_bytes)
|
||||
};
|
||||
return Ok(result.to_vec());
|
||||
}
|
||||
}
|
||||
// unsafe { display_mime_data(mime_data) };
|
||||
|
||||
let mut current_index = 0;
|
||||
let mut transfer_decoding_buffer = ptr::null_mut();
|
||||
let mut decoded_data_bytes = 0;
|
||||
|
||||
let r = unsafe {
|
||||
mailmime_part_parse(
|
||||
(*mime_data).dt_data.dt_text.dt_data,
|
||||
(*mime_data).dt_data.dt_text.dt_length,
|
||||
&mut current_index,
|
||||
mime_transfer_encoding,
|
||||
&mut transfer_decoding_buffer,
|
||||
&mut decoded_data_bytes,
|
||||
)
|
||||
};
|
||||
|
||||
if r == MAILIMF_NO_ERROR as libc::c_int
|
||||
&& !transfer_decoding_buffer.is_null()
|
||||
&& decoded_data_bytes > 0
|
||||
{
|
||||
let result = unsafe {
|
||||
std::slice::from_raw_parts(transfer_decoding_buffer as *const u8, decoded_data_bytes)
|
||||
}
|
||||
.to_vec();
|
||||
// we return a fresh vec and transfer_decoding_buffer is not used or passed anywhere
|
||||
// so it's safe to free it right away, as mailman_part_parse has
|
||||
// allocated it fresh.
|
||||
unsafe { mmap_string_unref(transfer_decoding_buffer) };
|
||||
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
Err(format_err!("Failed to to decode"))
|
||||
}
|
||||
|
||||
pub fn mailimf_find_first_addr(mb_list: *const mailimf_mailbox_list) -> Option<String> {
|
||||
if mb_list.is_null() {
|
||||
return None;
|
||||
}
|
||||
|
||||
for cur in unsafe { (*(*mb_list).mb_list).into_iter() } {
|
||||
let mb = cur as *mut mailimf_mailbox;
|
||||
if !mb.is_null() && !unsafe { (*mb).mb_addr_spec.is_null() } {
|
||||
let addr = unsafe { to_string_lossy((*mb).mb_addr_spec) };
|
||||
return Some(addr_normalize(&addr).to_string());
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
/**************************************
|
||||
* 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");
|
||||
unsafe {
|
||||
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 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) {
|
||||
unsafe {
|
||||
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 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;
|
||||
// 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")?;
|
||||
// let content = new_content_type("text/plain")?;
|
||||
// append_ct_param(content, "charset", "utf-8")?;
|
||||
|
||||
unsafe {
|
||||
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)?;
|
||||
// {
|
||||
// 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)
|
||||
}
|
||||
// Ok(message_part)
|
||||
// }
|
||||
|
||||
pub fn append_ct_param(
|
||||
content: *mut mailmime_content,
|
||||
name: &str,
|
||||
value: &str,
|
||||
) -> Result<(), Error> {
|
||||
unsafe {
|
||||
let name_c = CString::new(name).unwrap_or_default();
|
||||
let value_c = CString::new(value).unwrap_or_default();
|
||||
// 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(())
|
||||
}
|
||||
// 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
|
||||
unsafe {
|
||||
content = mailmime_content_new_with_str(ct.as_ptr());
|
||||
}
|
||||
ensure!(!content.is_null(), "mailimf failed to allocate");
|
||||
Ok(content)
|
||||
}
|
||||
// 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;
|
||||
unsafe {
|
||||
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 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 {
|
||||
unsafe {
|
||||
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 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 = unsafe { mailimf_mailbox_list_new_empty() };
|
||||
unsafe {
|
||||
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
|
||||
}
|
||||
// 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)]
|
||||
mod tests {
|
||||
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]
|
||||
// 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]
|
||||
fn test_parse_message_id() {
|
||||
|
||||
Reference in New Issue
Block a user