remove mmime

This commit is contained in:
dignifiedquire
2019-11-28 19:20:43 +01:00
parent 1f8d2531cc
commit dbd6303829
48 changed files with 2746 additions and 25814 deletions

View File

@@ -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
}
}

View File

@@ -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;

File diff suppressed because it is too large Load Diff

View File

@@ -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);
};

View File

@@ -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);
}
}
}

View File

@@ -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();

View File

@@ -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::*;

View File

@@ -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) => {

View File

@@ -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);
}
}

View File

@@ -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};

View File

@@ -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());

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

File diff suppressed because it is too large Load Diff

1053
src/mimeparser.rs Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -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)]

View File

@@ -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;

View File

@@ -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() {