create wrapmime module and simplify some mailmime code

This commit is contained in:
holger krekel
2019-09-23 01:43:12 +02:00
parent cb784615ee
commit e1d541b02e
4 changed files with 408 additions and 390 deletions

View File

@@ -26,6 +26,8 @@ use crate::location;
use crate::message::{self, Message}; use crate::message::{self, Message};
use crate::param::*; use crate::param::*;
use crate::stock::StockMessage; use crate::stock::StockMessage;
use crate::wrapmime;
use crate::wrapmime::*;
#[derive(Clone, Copy, Eq, PartialEq)] #[derive(Clone, Copy, Eq, PartialEq)]
pub enum Loaded { pub enum Loaded {
@@ -319,13 +321,13 @@ pub unsafe fn dc_mimefactory_render(
/* Add a X-Mailer header. /* Add a X-Mailer header.
This is only informational for debugging and may be removed in the release. This is only informational for debugging and may be removed in the release.
We do not rely on this header as it may be removed by MTAs. */ We do not rely on this header as it may be removed by MTAs. */
add_mailimf_field(imf_fields, "X-Mailer", &headerval); new_custom_field(imf_fields, "X-Mailer", &headerval);
add_mailimf_field(imf_fields, "Chat-Version", "1.0"); new_custom_field(imf_fields, "Chat-Version", "1.0");
if factory.req_mdn { if factory.req_mdn {
/* we use "Chat-Disposition-Notification-To" /* we use "Chat-Disposition-Notification-To"
because replies to "Disposition-Notification-To" are weird in many cases because replies to "Disposition-Notification-To" are weird in many cases
eg. are just freetext and/or do not follow any standard. */ eg. are just freetext and/or do not follow any standard. */
add_mailimf_field( new_custom_field(
imf_fields, imf_fields,
"Chat-Disposition-Notification-To", "Chat-Disposition-Notification-To",
&factory.from_addr, &factory.from_addr,
@@ -355,7 +357,7 @@ pub unsafe fn dc_mimefactory_render(
let mut placeholdertext = None; let mut placeholdertext = None;
if chat.typ == Chattype::VerifiedGroup { if chat.typ == Chattype::VerifiedGroup {
add_mailimf_field(imf_fields, "Chat-Verified", "1"); new_custom_field(imf_fields, "Chat-Verified", "1");
force_plaintext = 0; force_plaintext = 0;
e2ee_guaranteed = true; e2ee_guaranteed = true;
min_verified = 2 min_verified = 2
@@ -385,16 +387,16 @@ pub unsafe fn dc_mimefactory_render(
/* build header etc. */ /* build header etc. */
let command = factory.msg.param.get_cmd(); let command = factory.msg.param.get_cmd();
if chat.typ == Chattype::Group || chat.typ == Chattype::VerifiedGroup { if chat.typ == Chattype::Group || chat.typ == Chattype::VerifiedGroup {
add_mailimf_field(imf_fields, "Chat-Group-ID", &chat.grpid); new_custom_field(imf_fields, "Chat-Group-ID", &chat.grpid);
let encoded = dc_encode_header_words(&chat.name); let encoded = dc_encode_header_words(&chat.name);
add_mailimf_field(imf_fields, "Chat-Group-Name", &encoded); new_custom_field(imf_fields, "Chat-Group-Name", &encoded);
match command { match command {
SystemMessage::MemberRemovedFromGroup => { SystemMessage::MemberRemovedFromGroup => {
let email_to_remove = factory.msg.param.get(Param::Arg).unwrap_or_default(); let email_to_remove = factory.msg.param.get(Param::Arg).unwrap_or_default();
if !email_to_remove.is_empty() { if !email_to_remove.is_empty() {
add_mailimf_field( new_custom_field(
imf_fields, imf_fields,
"Chat-Group-Member-Removed", "Chat-Group-Member-Removed",
&email_to_remove, &email_to_remove,
@@ -406,7 +408,7 @@ pub unsafe fn dc_mimefactory_render(
do_gossip = true; do_gossip = true;
let email_to_add = msg.param.get(Param::Arg).unwrap_or_default(); let email_to_add = msg.param.get(Param::Arg).unwrap_or_default();
if !email_to_add.is_empty() { if !email_to_add.is_empty() {
add_mailimf_field(imf_fields, "Chat-Group-Member-Added", &email_to_add); new_custom_field(imf_fields, "Chat-Group-Member-Added", &email_to_add);
grpimage = chat.param.get(Param::ProfileImage); grpimage = chat.param.get(Param::ProfileImage);
} }
if 0 != msg.param.get_int(Param::Arg2).unwrap_or_default() & 0x1 { if 0 != msg.param.get_int(Param::Arg2).unwrap_or_default() & 0x1 {
@@ -415,20 +417,20 @@ pub unsafe fn dc_mimefactory_render(
"sending secure-join message \'{}\' >>>>>>>>>>>>>>>>>>>>>>>>>", "sending secure-join message \'{}\' >>>>>>>>>>>>>>>>>>>>>>>>>",
"vg-member-added", "vg-member-added",
); );
add_mailimf_field(imf_fields, "Secure-Join", "vg-member-added"); new_custom_field(imf_fields, "Secure-Join", "vg-member-added");
} }
} }
SystemMessage::GroupNameChanged => { SystemMessage::GroupNameChanged => {
let msg = &factory.msg; let msg = &factory.msg;
let value_to_add = msg.param.get(Param::Arg).unwrap_or_default(); let value_to_add = msg.param.get(Param::Arg).unwrap_or_default();
add_mailimf_field(imf_fields, "Chat-Group-Name-Changed", &value_to_add); new_custom_field(imf_fields, "Chat-Group-Name-Changed", &value_to_add);
} }
SystemMessage::GroupImageChanged => { SystemMessage::GroupImageChanged => {
let msg = &factory.msg; let msg = &factory.msg;
grpimage = msg.param.get(Param::Arg); grpimage = msg.param.get(Param::Arg);
if grpimage.is_none() { if grpimage.is_none() {
add_mailimf_field(imf_fields, "Chat-Group-Image", "0"); new_custom_field(imf_fields, "Chat-Group-Image", "0");
} }
} }
_ => {} _ => {}
@@ -437,10 +439,10 @@ pub unsafe fn dc_mimefactory_render(
match command { match command {
SystemMessage::LocationStreamingEnabled => { SystemMessage::LocationStreamingEnabled => {
add_mailimf_field(imf_fields, "Chat-Content", "location-streaming-enabled"); new_custom_field(imf_fields, "Chat-Content", "location-streaming-enabled");
} }
SystemMessage::AutocryptSetupMessage => { SystemMessage::AutocryptSetupMessage => {
add_mailimf_field(imf_fields, "Autocrypt-Setup-Message", "v1"); new_custom_field(imf_fields, "Autocrypt-Setup-Message", "v1");
placeholdertext = Some( placeholdertext = Some(
factory factory
.context .context
@@ -456,10 +458,10 @@ pub unsafe fn dc_mimefactory_render(
context, context,
"sending secure-join message \'{}\' >>>>>>>>>>>>>>>>>>>>>>>>>", step, "sending secure-join message \'{}\' >>>>>>>>>>>>>>>>>>>>>>>>>", step,
); );
add_mailimf_field(imf_fields, "Secure-Join", &step); new_custom_field(imf_fields, "Secure-Join", &step);
let param2 = msg.param.get(Param::Arg2).unwrap_or_default(); let param2 = msg.param.get(Param::Arg2).unwrap_or_default();
if !param2.is_empty() { if !param2.is_empty() {
add_mailimf_field( new_custom_field(
imf_fields, imf_fields,
if step == "vg-request-with-auth" || step == "vc-request-with-auth" if step == "vg-request-with-auth" || step == "vc-request-with-auth"
{ {
@@ -472,11 +474,11 @@ pub unsafe fn dc_mimefactory_render(
} }
let fingerprint = msg.param.get(Param::Arg3).unwrap_or_default(); let fingerprint = msg.param.get(Param::Arg3).unwrap_or_default();
if !fingerprint.is_empty() { if !fingerprint.is_empty() {
add_mailimf_field(imf_fields, "Secure-Join-Fingerprint", &fingerprint); new_custom_field(imf_fields, "Secure-Join-Fingerprint", &fingerprint);
} }
match msg.param.get(Param::Arg4) { match msg.param.get(Param::Arg4) {
Some(id) => { Some(id) => {
add_mailimf_field(imf_fields, "Secure-Join-Group", &id); new_custom_field(imf_fields, "Secure-Join-Group", &id);
} }
None => {} None => {}
}; };
@@ -495,7 +497,7 @@ pub unsafe fn dc_mimefactory_render(
meta_part = res.0; meta_part = res.0;
let filename_as_sent = res.1; let filename_as_sent = res.1;
if !meta_part.is_null() { if !meta_part.is_null() {
add_mailimf_field(imf_fields, "Chat-Group-Image", &filename_as_sent) new_custom_field(imf_fields, "Chat-Group-Image", &filename_as_sent)
} }
} }
@@ -504,7 +506,7 @@ pub unsafe fn dc_mimefactory_render(
|| factory.msg.type_0 == Viewtype::Video || factory.msg.type_0 == Viewtype::Video
{ {
if factory.msg.type_0 == Viewtype::Voice { if factory.msg.type_0 == Viewtype::Voice {
add_mailimf_field(imf_fields, "Chat-Voice-Message", "1"); new_custom_field(imf_fields, "Chat-Voice-Message", "1");
} }
let duration_ms = factory let duration_ms = factory
.msg .msg
@@ -513,7 +515,7 @@ pub unsafe fn dc_mimefactory_render(
.unwrap_or_default(); .unwrap_or_default();
if duration_ms > 0 { if duration_ms > 0 {
let dur = duration_ms.to_string(); let dur = duration_ms.to_string();
add_mailimf_field(imf_fields, "Chat-Duration", &dur); new_custom_field(imf_fields, "Chat-Duration", &dur);
} }
} }
@@ -554,7 +556,7 @@ pub unsafe fn dc_mimefactory_render(
if !footer.is_empty() { "-- \r\n" } else { "" }, if !footer.is_empty() { "-- \r\n" } else { "" },
footer footer
); );
let text_part = build_body_text(&message_text); let text_part = build_body_text(&message_text)?;
mailmime_smart_add_part(message, text_part); mailmime_smart_add_part(message, text_part);
/* add attachment part */ /* add attachment part */
@@ -582,35 +584,24 @@ pub unsafe fn dc_mimefactory_render(
param.get_float(Param::SetLatitude).unwrap_or_default(), param.get_float(Param::SetLatitude).unwrap_or_default(),
param.get_float(Param::SetLongitude).unwrap_or_default(), param.get_float(Param::SetLongitude).unwrap_or_default(),
); );
let content_type = mailmime_content_new_with_str( add_filename_part(
b"application/vnd.google-earth.kml+xml\x00" as *const u8 as *const libc::c_char, message,
); "message.kml",
let mime_fields = mailmime_fields_new_filename( "application/vnd.google-earth.kml+xml",
MAILMIME_DISPOSITION_TYPE_ATTACHMENT as libc::c_int, &kml_file,
"message.kml".strdup(), )?;
MAILMIME_MECHANISM_8BIT as libc::c_int,
);
let kml_mime_part = mailmime_new_empty(content_type, mime_fields);
set_body_text(kml_mime_part, &kml_file);
mailmime_smart_add_part(message, kml_mime_part);
} }
if location::is_sending_locations_to_chat(context, factory.msg.chat_id) { if location::is_sending_locations_to_chat(context, factory.msg.chat_id) {
if let Ok((kml_file, last_added_location_id)) = if let Ok((kml_file, last_added_location_id)) =
location::get_kml(context, factory.msg.chat_id) location::get_kml(context, factory.msg.chat_id)
{ {
let content_type = mailmime_content_new_with_str( add_filename_part(
b"application/vnd.google-earth.kml+xml\x00" as *const u8 message,
as *const libc::c_char, "location.kml",
); "application/vnd.google-earth.kml+xml",
let mime_fields = mailmime_fields_new_filename( &kml_file,
MAILMIME_DISPOSITION_TYPE_ATTACHMENT as libc::c_int, )?;
"location.kml".strdup(),
MAILMIME_MECHANISM_8BIT as libc::c_int,
);
let kml_mime_part = mailmime_new_empty(content_type, mime_fields);
set_body_text(kml_mime_part, &kml_file);
mailmime_smart_add_part(message, kml_mime_part);
if !factory.msg.param.exists(Param::SetLatitude) { if !factory.msg.param.exists(Param::SetLatitude) {
// otherwise, the independent location is already filed // otherwise, the independent location is already filed
factory.out_last_added_location_id = last_added_location_id; factory.out_last_added_location_id = last_added_location_id;
@@ -624,18 +615,14 @@ pub unsafe fn dc_mimefactory_render(
*********************************************************************/ *********************************************************************/
/* RFC 6522, this also requires the `report-type` parameter which is equal /* RFC 6522, this also requires the `report-type` parameter which is equal
to the MIME subtype of the second body part of the multipart/report */ to the MIME subtype of the second body part of the multipart/report */
let multipart: *mut mailmime = let multipart =
mailmime_multiple_new(b"multipart/report\x00" as *const u8 as *const libc::c_char); mailmime_multiple_new(b"multipart/report\x00" as *const u8 as *const libc::c_char);
let content: *mut mailmime_content = (*multipart).mm_content_type; wrapmime::append_ct_param(
clist_insert_after( (*multipart).mm_content_type,
(*content).ct_parameters, "report-type",
(*(*content).ct_parameters).last, "disposition-notification",
mailmime_param_new_with_data( )?;
b"report-type\x00" as *const u8 as *const libc::c_char as *mut libc::c_char,
b"disposition-notification\x00" as *const u8 as *const libc::c_char
as *mut libc::c_char,
) as *mut libc::c_void,
);
mailmime_add_part(message, multipart); mailmime_add_part(message, multipart);
/* first body part: always human-readable, always REQUIRED by RFC 6522 */ /* first body part: always human-readable, always REQUIRED by RFC 6522 */
@@ -657,7 +644,7 @@ pub unsafe fn dc_mimefactory_render(
.context .context
.stock_string_repl_str(StockMessage::ReadRcptMailBody, p1); .stock_string_repl_str(StockMessage::ReadRcptMailBody, p1);
let message_text = format!("{}\r\n", p2); let message_text = format!("{}\r\n", p2);
let human_mime_part: *mut mailmime = build_body_text(&message_text); let human_mime_part = build_body_text(&message_text)?;
mailmime_add_part(multipart, human_mime_part); mailmime_add_part(multipart, human_mime_part);
/* second body part: machine-readable, always REQUIRED by RFC 6522 */ /* second body part: machine-readable, always REQUIRED by RFC 6522 */
@@ -670,13 +657,11 @@ pub unsafe fn dc_mimefactory_render(
factory.msg.rfc724_mid factory.msg.rfc724_mid
); );
let content_type_0: *mut mailmime_content = mailmime_content_new_with_str( let content_type_0 = new_mailmime_content_type("message/disposition-notification");
b"message/disposition-notification\x00" as *const u8 as *const libc::c_char,
);
let mime_fields_0: *mut mailmime_fields = let mime_fields_0: *mut mailmime_fields =
mailmime_fields_new_encoding(MAILMIME_MECHANISM_8BIT as libc::c_int); mailmime_fields_new_encoding(MAILMIME_MECHANISM_8BIT as libc::c_int);
let mach_mime_part: *mut mailmime = mailmime_new_empty(content_type_0, mime_fields_0); let mach_mime_part: *mut mailmime = mailmime_new_empty(content_type_0, mime_fields_0);
set_body_text(mach_mime_part, &message_text2); set_body_text(mach_mime_part, &message_text2)?;
mailmime_add_part(multipart, mach_mime_part); mailmime_add_part(multipart, mach_mime_part);
force_plaintext = DC_FP_NO_AUTOCRYPT_HEADER; force_plaintext = DC_FP_NO_AUTOCRYPT_HEADER;
/* currently, we do not send MDNs encrypted: /* currently, we do not send MDNs encrypted:
@@ -730,7 +715,7 @@ pub unsafe fn dc_mimefactory_render(
let mut e2ee_helper = E2eeHelper::default(); let mut e2ee_helper = E2eeHelper::default();
if force_plaintext != DC_FP_NO_AUTOCRYPT_HEADER { if force_plaintext != DC_FP_NO_AUTOCRYPT_HEADER {
e2ee_helper.encrypt( if e2ee_helper.encrypt(
factory.context, factory.context,
&factory.recipients_addr, &factory.recipients_addr,
force_plaintext == DC_FP_ADD_AUTOCRYPT_HEADER, force_plaintext == DC_FP_ADD_AUTOCRYPT_HEADER,
@@ -738,12 +723,11 @@ pub unsafe fn dc_mimefactory_render(
min_verified, min_verified,
do_gossip, do_gossip,
message, message,
); )? {
} factory.out_encrypted = true;
if e2ee_helper.encryption_successfull { if do_gossip {
factory.out_encrypted = true; factory.out_gossiped = true;
if do_gossip { }
factory.out_gossiped = true;
} }
} }
factory.out = mmap_string_new(b"\x00" as *const u8 as *const libc::c_char); factory.out = mmap_string_new(b"\x00" as *const u8 as *const libc::c_char);
@@ -781,48 +765,6 @@ fn get_subject(
} }
} }
pub fn add_mailimf_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"
);
}
}
fn build_body_text(text: &str) -> *mut mailmime {
let mime_fields: *mut mailmime_fields;
let message_part: *mut mailmime;
let content: *mut mailmime_content;
unsafe {
content =
mailmime_content_new_with_str(b"text/plain\x00" as *const u8 as *const libc::c_char);
clist_insert_after(
(*content).ct_parameters,
(*(*content).ct_parameters).last,
mailmime_param_new_with_data(
b"charset\x00" as *const u8 as *const libc::c_char as *mut libc::c_char,
b"utf-8\x00" as *const u8 as *const libc::c_char as *mut libc::c_char,
) as *mut libc::c_void,
);
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);
message_part
}
fn set_body_text(part: *mut mailmime, text: &str) {
use libc::strlen;
unsafe {
let text_c = text.strdup();
mailmime_set_body_text(part, text_c, strlen(text_c));
}
}
#[allow(non_snake_case)] #[allow(non_snake_case)]
fn build_body_file(context: &Context, msg: &Message, base_name: &str) -> (*mut mailmime, String) { fn build_body_file(context: &Context, msg: &Message, base_name: &str) -> (*mut mailmime, String) {
let path_filename = match msg.param.get(Param::File) { let path_filename = match msg.param.get(Param::File) {
@@ -921,7 +863,7 @@ fn build_body_file(context: &Context, msg: &Message, base_name: &str) -> (*mut m
} }
} }
} }
let content = mailmime_content_new_with_str(mimetype.strdup()); let content = new_mailmime_content_type(&mimetype);
let filename_encoded = dc_encode_header_words(&filename_to_send).strdup(); let filename_encoded = dc_encode_header_words(&filename_to_send).strdup();
clist_insert_after( clist_insert_after(
(*content).ct_parameters, (*content).ct_parameters,

View File

@@ -31,10 +31,11 @@ use crate::keyring::*;
use crate::peerstate::*; use crate::peerstate::*;
use crate::pgp::*; use crate::pgp::*;
use crate::securejoin::handle_degrade_event; use crate::securejoin::handle_degrade_event;
use crate::wrapmime;
use crate::wrapmime::*;
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct E2eeHelper { pub struct E2eeHelper {
pub encryption_successfull: bool,
cdata_to_free: Option<Box<dyn Any>>, cdata_to_free: Option<Box<dyn Any>>,
pub encrypted: bool, pub encrypted: bool,
pub signatures: HashSet<String>, pub signatures: HashSet<String>,
@@ -59,289 +60,248 @@ impl E2eeHelper {
min_verified: libc::c_int, min_verified: libc::c_int,
do_gossip: bool, do_gossip: bool,
mut in_out_message: *mut mailmime, mut in_out_message: *mut mailmime,
) { ) -> Result<bool> {
let mut ok_to_continue = true;
let mut col: libc::c_int = 0i32; let mut col: libc::c_int = 0i32;
let mut do_encrypt: libc::c_int = 0i32; let mut do_encrypt = false;
/*just a pointer into mailmime structure, must not be freed*/
let imffields_unprotected: *mut mailimf_fields;
let mut keyring = Keyring::default(); let mut keyring = Keyring::default();
let plain: *mut MMAPString = mmap_string_new(b"\x00" as *const u8 as *const libc::c_char);
let mut peerstates: Vec<Peerstate> = Vec::new(); let mut peerstates: Vec<Peerstate> = Vec::new();
if !(in_out_message.is_null() || !(*in_out_message).mm_parent.is_null() || plain.is_null()) if in_out_message.is_null() || !(*in_out_message).mm_parent.is_null() {
bail!("invalid inputs");
}
let addr = match context.get_config(Config::ConfiguredAddr) {
Some(addr) => addr,
None => {
bail!("addr not configured");
}
};
let public_key = match load_or_generate_self_public_key(context, &addr) {
Err(err) => {
bail!("Failed to load own public key: {}", err);
}
Ok(public_key) => public_key
};
/* libEtPan's pgp_encrypt_mime() takes the parent as the new root. We just expect the root as being given to this function. */
let prefer_encrypt = if 0
!= context
.sql
.get_config_int(context, "e2ee_enabled")
.unwrap_or_default()
{ {
/* libEtPan's pgp_encrypt_mime() takes the parent as the new root. We just expect the root as being given to this function. */ EncryptPreference::Mutual
let prefer_encrypt = if 0 } else {
!= context EncryptPreference::NoPreference
.sql };
.get_config_int(context, "e2ee_enabled")
.unwrap_or_default()
{
EncryptPreference::Mutual
} else {
EncryptPreference::NoPreference
};
let addr = context.get_config(Config::ConfiguredAddr);
if let Some(addr) = addr { let plain: *mut MMAPString = mmap_string_new(b"\x00" as *const u8 as *const libc::c_char);
let pubkey_ret = load_or_generate_self_public_key(context, &addr).map_err(|err| { /*only for random-seed*/
error!(context, "Failed to load public key: {}", err); if prefer_encrypt == EncryptPreference::Mutual || e2ee_guaranteed {
err do_encrypt = true;
}); for recipient_addr in recipients_addr.iter() {
if let Ok(public_key) = pubkey_ret { if *recipient_addr != addr {
/*only for random-seed*/ let peerstate =
if prefer_encrypt == EncryptPreference::Mutual || e2ee_guaranteed { Peerstate::from_addr(context, &context.sql, &recipient_addr);
do_encrypt = 1i32; if peerstate.is_some()
for recipient_addr in recipients_addr.iter() { && (peerstate.as_ref().unwrap().prefer_encrypt
if *recipient_addr != addr { == EncryptPreference::Mutual
let peerstate = || e2ee_guaranteed)
Peerstate::from_addr(context, &context.sql, &recipient_addr); {
if peerstate.is_some() let peerstate = peerstate.unwrap();
&& (peerstate.as_ref().unwrap().prefer_encrypt info!(context, "dc_e2ee_encrypt {} has peerstate", recipient_addr);
== EncryptPreference::Mutual if let Some(key) = peerstate.peek_key(min_verified as usize) {
|| e2ee_guaranteed) keyring.add_owned(key.clone());
{ peerstates.push(peerstate);
let peerstate = peerstate.unwrap();
info!(
context,
"dc_e2ee_encrypt {} has peerstate", recipient_addr
);
if let Some(key) = peerstate.peek_key(min_verified as usize) {
keyring.add_owned(key.clone());
peerstates.push(peerstate);
}
} else {
info!(
context,
"dc_e2ee_encrypt {} HAS NO peerstate {}",
recipient_addr,
peerstate.is_some()
);
do_encrypt = 0i32;
/* if we cannot encrypt to a single recipient, we cannot encrypt the message at all */
break;
}
} }
} } else {
} info!(
let sign_key = if 0 != do_encrypt { context,
keyring.add_ref(&public_key); "dc_e2ee_encrypt {} HAS NO peerstate {}",
let key = Key::from_self_private(context, addr.clone(), &context.sql); recipient_addr,
peerstate.is_some()
if key.is_none() {
do_encrypt = 0i32;
}
key
} else {
None
};
if force_unencrypted {
do_encrypt = 0i32
}
imffields_unprotected = mailmime_find_mailimf_fields(in_out_message);
if !imffields_unprotected.is_null() {
/* encrypt message, if possible */
if 0 != do_encrypt {
mailprivacy_prepare_mime(in_out_message);
let mut part_to_encrypt: *mut mailmime =
(*in_out_message).mm_data.mm_message.mm_msg_mime;
(*part_to_encrypt).mm_parent = ptr::null_mut();
let imffields_encrypted: *mut mailimf_fields =
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: *mut mailmime = mailmime_new(
MAILMIME_MESSAGE as libc::c_int,
ptr::null(),
0 as libc::size_t,
mailmime_fields_new_empty(),
mailmime_get_content_message(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
imffields_encrypted,
part_to_encrypt,
);
if do_gossip {
let i_cnt = peerstates.len() as libc::c_int;
if i_cnt > 1 {
let mut i = 0;
while i < i_cnt {
let p = peerstates[i as usize]
.render_gossip_header(min_verified as usize);
if let Some(header) = p {
mailimf_fields_add(
imffields_encrypted,
mailimf_field_new_custom(
"Autocrypt-Gossip".strdup(),
header.strdup(),
),
);
}
i += 1
}
}
}
/* memoryhole headers */
// XXX we can't use clist's into_iter()
// because the loop body also removes items
let mut cur: *mut clistiter =
(*(*imffields_unprotected).fld_list).first;
while !cur.is_null() {
let field: *mut mailimf_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-")
{
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;
}
}
let subject: *mut mailimf_subject = mailimf_subject_new("...".strdup());
mailimf_fields_add(
imffields_unprotected,
mailimf_field_new(
MAILIMF_FIELD_SUBJECT as libc::c_int,
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
subject,
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
),
);
clist_insert_after(
(*(*part_to_encrypt).mm_content_type).ct_parameters,
(*(*(*part_to_encrypt).mm_content_type).ct_parameters).last,
mailmime_param_new_with_data(
b"protected-headers\x00" as *const u8 as *const libc::c_char
as *mut libc::c_char,
b"v1\x00" as *const u8 as *const libc::c_char
as *mut libc::c_char,
) as *mut libc::c_void,
);
mailmime_write_mem(plain, &mut col, message_to_encrypt);
if (*plain).str_0.is_null() || (*plain).len <= 0 {
ok_to_continue = false;
} else {
if let Ok(ctext_v) = dc_pgp_pk_encrypt(
std::slice::from_raw_parts(
(*plain).str_0 as *const u8,
(*plain).len,
),
&keyring,
sign_key.as_ref(),
) {
let ctext_bytes = ctext_v.len();
let ctext = ctext_v.strdup();
self.cdata_to_free = Some(Box::new(ctext));
/* create MIME-structure that will contain the encrypted text */
let mut encrypted_part: *mut mailmime = new_data_part(
ptr::null_mut(),
0 as libc::size_t,
b"multipart/encrypted\x00" as *const u8
as *const libc::c_char
as *mut libc::c_char,
-1i32,
);
let content: *mut mailmime_content =
(*encrypted_part).mm_content_type;
clist_insert_after(
(*content).ct_parameters,
(*(*content).ct_parameters).last,
mailmime_param_new_with_data(
b"protocol\x00" as *const u8 as *const libc::c_char
as *mut libc::c_char,
b"application/pgp-encrypted\x00" as *const u8
as *const libc::c_char
as *mut libc::c_char,
)
as *mut libc::c_void,
);
static mut VERSION_CONTENT: [libc::c_char; 13] =
[86, 101, 114, 115, 105, 111, 110, 58, 32, 49, 13, 10, 0];
let version_mime: *mut mailmime = new_data_part(
VERSION_CONTENT.as_mut_ptr() as *mut libc::c_void,
strlen(VERSION_CONTENT.as_mut_ptr()),
b"application/pgp-encrypted\x00" as *const u8
as *const libc::c_char
as *mut libc::c_char,
MAILMIME_MECHANISM_7BIT as libc::c_int,
);
mailmime_smart_add_part(encrypted_part, version_mime);
let ctext_part: *mut mailmime = new_data_part(
ctext as *mut libc::c_void,
ctext_bytes,
b"application/octet-stream\x00" as *const u8
as *const libc::c_char
as *mut libc::c_char,
MAILMIME_MECHANISM_7BIT as libc::c_int,
);
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;
mailmime_free(message_to_encrypt);
self.encryption_successfull = true;
}
}
}
if ok_to_continue {
let aheader = Aheader::new(addr, public_key, prefer_encrypt);
mailimf_fields_add(
imffields_unprotected,
mailimf_field_new_custom(
"Autocrypt".strdup(),
aheader.to_string().strdup(),
),
); );
do_encrypt = false;
/* if we cannot encrypt to a single recipient, we cannot encrypt the message at all */
break;
} }
} }
} }
} }
} let sign_key = if do_encrypt {
keyring.add_ref(&public_key);
let key = Key::from_self_private(context, addr.clone(), &context.sql);
if key.is_none() {
do_encrypt = false;
}
key
} else {
None
};
if force_unencrypted {
do_encrypt = false;
}
/*just a pointer into mailmime structure, must not be freed*/
let imffields_unprotected = mailmime_find_mailimf_fields(in_out_message);
if !imffields_unprotected.is_null() {
/* encrypt message, if possible */
if do_encrypt {
mailprivacy_prepare_mime(in_out_message);
let mut part_to_encrypt: *mut mailmime =
(*in_out_message).mm_data.mm_message.mm_msg_mime;
(*part_to_encrypt).mm_parent = ptr::null_mut();
let imffields_encrypted: *mut mailimf_fields = 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: *mut mailmime = mailmime_new(
MAILMIME_MESSAGE as libc::c_int,
ptr::null(),
0 as libc::size_t,
mailmime_fields_new_empty(),
mailmime_get_content_message(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
imffields_encrypted,
part_to_encrypt,
);
if do_gossip {
for peerstate in peerstates {
peerstate.render_gossip_header(min_verified as usize).map(|header| {
wrapmime::new_custom_field(
imffields_encrypted,
"Autocrypt-Gossip",
&header
)
});
}
}
/* memoryhole headers */
// XXX we can't use clist's into_iter()
// because the loop body also removes items
let mut cur: *mut clistiter = (*(*imffields_unprotected).fld_list).first;
while !cur.is_null() {
let field: *mut mailimf_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-")
{
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;
}
}
let subject: *mut mailimf_subject = mailimf_subject_new("...".strdup());
mailimf_fields_add(
imffields_unprotected,
mailimf_field_new(
MAILIMF_FIELD_SUBJECT as libc::c_int,
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
subject,
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
),
);
wrapmime::append_ct_param(
(*part_to_encrypt).mm_content_type,
"protected-headers",
"v1",
)?;
mailmime_write_mem(plain, &mut col, message_to_encrypt);
if (*plain).str_0.is_null() || (*plain).len <= 0 {
bail!("could not write/allocate");
}
if let Ok(ctext_v) = dc_pgp_pk_encrypt(
std::slice::from_raw_parts((*plain).str_0 as *const u8, (*plain).len),
&keyring,
sign_key.as_ref(),
) {
let ctext_bytes = ctext_v.len();
let ctext = ctext_v.strdup();
self.cdata_to_free = Some(Box::new(ctext));
/* create MIME-structure that will contain the encrypted text */
let mut encrypted_part: *mut mailmime = new_data_part(
ptr::null_mut(),
0 as libc::size_t,
"multipart/encrypted",
-1,
);
let content: *mut mailmime_content = (*encrypted_part).mm_content_type;
wrapmime::append_ct_param(
content,
"protocol",
"application/pgp-encrypted",
)?;
static mut VERSION_CONTENT: [libc::c_char; 13] =
[86, 101, 114, 115, 105, 111, 110, 58, 32, 49, 13, 10, 0];
let version_mime: *mut mailmime = 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 as i32,
);
mailmime_smart_add_part(encrypted_part, version_mime);
let ctext_part: *mut mailmime = new_data_part(
ctext as *mut libc::c_void,
ctext_bytes,
"application/octet-stream",
MAILMIME_MECHANISM_7BIT as i32,
);
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;
mailmime_free(message_to_encrypt);
if !plain.is_null() {
mmap_string_free(plain);
}
return Ok(true);
}
}
let aheader = Aheader::new(addr, public_key, prefer_encrypt).to_string();
new_custom_field(imffields_unprotected, "Autocrypt", &aheader);
}
if !plain.is_null() { if !plain.is_null() {
mmap_string_free(plain); mmap_string_free(plain);
} }
Ok(false)
} }
pub unsafe fn decrypt(&mut self, context: &Context, in_out_message: *mut mailmime) { pub unsafe fn decrypt(&mut self, context: &Context, in_out_message: *mut mailmime) {
@@ -464,28 +424,23 @@ impl E2eeHelper {
unsafe fn new_data_part( unsafe fn new_data_part(
data: *mut libc::c_void, data: *mut libc::c_void,
data_bytes: libc::size_t, data_bytes: libc::size_t,
default_content_type: *mut libc::c_char, default_content_type: &str,
default_encoding: libc::c_int, default_encoding: libc::c_int,
) -> *mut mailmime { ) -> *mut mailmime {
let mut ok_to_continue = true; let mut ok_to_continue = true;
//char basename_buf[PATH_MAX]; let mut encoding: *mut mailmime_mechanism = ptr::null_mut();
let mut encoding: *mut mailmime_mechanism;
let content: *mut mailmime_content; let content: *mut mailmime_content;
let mime: *mut mailmime; let mime: *mut mailmime;
//int r;
//char * dup_filename;
let mime_fields: *mut mailmime_fields; let mime_fields: *mut mailmime_fields;
let encoding_type: libc::c_int; let encoding_type: libc::c_int;
let content_type_str: *mut libc::c_char;
let mut do_encoding: libc::c_int; let mut do_encoding: libc::c_int;
encoding = ptr::null_mut();
if default_content_type.is_null() { let content_type = if default_content_type.is_empty() {
content_type_str = "application/octet-stream"
b"application/octet-stream\x00" as *const u8 as *const libc::c_char as *mut libc::c_char
} else { } else {
content_type_str = default_content_type default_content_type
} };
content = mailmime_content_new_with_str(content_type_str); content = new_mailmime_content_type(&content_type);
if content.is_null() { if content.is_null() {
ok_to_continue = false; ok_to_continue = false;
} else { } else {

View File

@@ -67,6 +67,8 @@ pub mod dc_tools;
mod login_param; mod login_param;
pub mod securejoin; pub mod securejoin;
mod token; mod token;
#[macro_use]
pub(crate) mod wrapmime;
#[cfg(test)] #[cfg(test)]
mod test_utils; mod test_utils;

119
src/wrapmime.rs Normal file
View File

@@ -0,0 +1,119 @@
use std::ffi::CString;
use crate::dc_tools::*;
use crate::error::Error;
use mmime::clist::*;
use mmime::mailimf_types::*;
use mmime::mailimf_types_helper::*;
use mmime::mailmime_disposition::*;
use mmime::mailmime_types::*;
use mmime::mailmime_types_helper::*;
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");
}
};
}
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 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 build_body_text(text: &str) -> Result<*mut mailmime, Error> {
let mime_fields: *mut mailmime_fields;
let message_part: *mut mailmime;
let content = new_mailmime_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)?;
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().as_ptr();
let value_c = CString::new(value).unwrap().as_ptr();
clist_append!(
(*content).ct_parameters,
mailmime_param_new_with_data(
name_c as *const u8 as *const libc::c_char as *mut libc::c_char,
value_c as *const u8 as *const libc::c_char as *mut libc::c_char
)
);
}
Ok(())
}
pub fn new_mailmime_content_type(content_type: &str) -> *mut mailmime_content {
let ct = CString::new(content_type).unwrap();
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());
}
if content.is_null() {
panic!("mailimf failed to allocate");
}
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(())
}