Files
chatmail-core/src/dc_mimeparser.rs
2019-09-16 07:59:41 +02:00

1801 lines
71 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

use std::collections::{HashMap, HashSet};
use std::ffi::CStr;
use std::ptr;
use charset::Charset;
use mmime::clist::*;
use mmime::mailimf::*;
use mmime::mailimf_types::*;
use mmime::mailmime::*;
use mmime::mailmime_content::*;
use mmime::mailmime_disposition::*;
use mmime::mailmime_types::*;
use mmime::mmapstring::*;
use mmime::other::*;
use crate::constants::Viewtype;
use crate::contact::*;
use crate::context::Context;
use crate::dc_simplify::*;
use crate::dc_strencode::*;
use crate::dc_tools::*;
use crate::e2ee::*;
use crate::location;
use crate::param::*;
use crate::stock::StockMessage;
use crate::x::*;
/* Parse MIME body; this is the text part of an IMF, see https://tools.ietf.org/html/rfc5322
dc_mimeparser_t has no deep dependencies to Context or to the database
(Context is used for logging only). */
#[derive(Debug, Clone)]
#[repr(C)]
pub struct dc_mimepart_t {
pub type_0: Viewtype,
pub is_meta: bool,
pub int_mimetype: libc::c_int,
pub msg: Option<String>,
pub msg_raw: Option<String>,
pub bytes: libc::c_int,
pub param: Params,
}
/* *
* @class dc_mimeparser_t
*/
#[allow(non_camel_case_types, missing_debug_implementations)]
pub struct dc_mimeparser_t<'a> {
pub parts: Vec<dc_mimepart_t>,
pub mimeroot: *mut mailmime,
pub header: HashMap<String, *mut mailimf_field>,
pub header_root: *mut mailimf_fields,
pub header_protected: *mut mailimf_fields,
pub subject: Option<String>,
pub is_send_by_messenger: bool,
pub decrypting_failed: bool,
pub e2ee_helper: E2eeHelper,
pub is_forwarded: bool,
pub context: &'a Context,
pub reports: Vec<*mut mailmime>,
pub is_system_message: libc::c_int,
pub location_kml: Option<location::Kml>,
pub message_kml: Option<location::Kml>,
}
pub fn dc_mimeparser_new(context: &Context) -> dc_mimeparser_t {
dc_mimeparser_t {
parts: Vec::new(),
mimeroot: std::ptr::null_mut(),
header: Default::default(),
header_root: std::ptr::null_mut(),
header_protected: std::ptr::null_mut(),
subject: None,
is_send_by_messenger: false,
decrypting_failed: false,
e2ee_helper: Default::default(),
is_forwarded: false,
context,
reports: Vec::new(),
is_system_message: 0,
location_kml: None,
message_kml: None,
}
}
pub unsafe fn dc_mimeparser_unref(mimeparser: &mut dc_mimeparser_t) {
dc_mimeparser_empty(mimeparser);
}
unsafe fn dc_mimeparser_empty(mimeparser: &mut dc_mimeparser_t) {
mimeparser.parts = vec![];
mimeparser.header_root = ptr::null_mut();
mimeparser.header.clear();
if !mimeparser.header_protected.is_null() {
mailimf_fields_free(mimeparser.header_protected);
mimeparser.header_protected = ptr::null_mut()
}
mimeparser.is_send_by_messenger = false;
mimeparser.is_system_message = 0i32;
mimeparser.subject = None;
if !mimeparser.mimeroot.is_null() {
mailmime_free(mimeparser.mimeroot);
mimeparser.mimeroot = ptr::null_mut()
}
mimeparser.is_forwarded = false;
mimeparser.reports.clear();
mimeparser.decrypting_failed = false;
mimeparser.e2ee_helper.thanks();
mimeparser.location_kml = None;
mimeparser.message_kml = None;
}
const DC_MIMETYPE_AC_SETUP_FILE: i32 = 111;
pub unsafe fn dc_mimeparser_parse<'a>(context: &'a Context, body: &[u8]) -> dc_mimeparser_t<'a> {
let r: libc::c_int;
let mut index: libc::size_t = 0;
let optional_field: *mut mailimf_optional_field;
let mut mimeparser = dc_mimeparser_new(context);
r = mailmime_parse(
body.as_ptr() as *const libc::c_char,
body.len(),
&mut index,
&mut mimeparser.mimeroot,
);
if r == MAILIMF_NO_ERROR as libc::c_int && !mimeparser.mimeroot.is_null() {
mimeparser
.e2ee_helper
.decrypt(mimeparser.context, mimeparser.mimeroot);
let mimeparser_ref = &mut mimeparser;
dc_mimeparser_parse_mime_recursive(mimeparser_ref, mimeparser_ref.mimeroot);
let field: *mut mailimf_field = dc_mimeparser_lookup_field(&mimeparser, "Subject");
if !field.is_null() && (*field).fld_type == MAILIMF_FIELD_SUBJECT as libc::c_int {
let subj = (*(*field).fld_data.fld_subject).sbj_value;
mimeparser.subject = as_opt_str(subj).map(dc_decode_header_words_safe);
}
if !dc_mimeparser_lookup_optional_field(&mut mimeparser, "Chat-Version").is_null() {
mimeparser.is_send_by_messenger = true
}
if !dc_mimeparser_lookup_field(&mut mimeparser, "Autocrypt-Setup-Message").is_null() {
let has_setup_file = mimeparser
.parts
.iter()
.any(|p| p.int_mimetype == DC_MIMETYPE_AC_SETUP_FILE);
if has_setup_file {
mimeparser.is_system_message = 6i32;
// TODO: replace the following code with this
// once drain_filter stabilizes.
//
// See https://doc.rust-lang.org/std/vec/struct.Vec.html#method.drain_filter
// and https://github.com/rust-lang/rust/issues/43244
//
// mimeparser
// .parts
// .drain_filter(|part| part.int_mimetype != 111)
// .for_each(|part| dc_mimepart_unref(part));
let mut i = 0;
while i != mimeparser.parts.len() {
if mimeparser.parts[i].int_mimetype != 111 {
mimeparser.parts.remove(i);
} else {
i += 1;
}
}
}
} else {
optional_field = dc_mimeparser_lookup_optional_field(&mut mimeparser, "Chat-Content");
if !optional_field.is_null() && !(*optional_field).fld_value.is_null() {
if strcmp(
(*optional_field).fld_value,
b"location-streaming-enabled\x00" as *const u8 as *const libc::c_char,
) == 0i32
{
mimeparser.is_system_message = 8i32
}
}
}
if !dc_mimeparser_lookup_field(&mut mimeparser, "Chat-Group-Image").is_null()
&& !mimeparser.parts.is_empty()
{
let textpart = &mimeparser.parts[0];
if textpart.type_0 == Viewtype::Text {
if mimeparser.parts.len() >= 2 {
let imgpart = &mut mimeparser.parts[1];
if imgpart.type_0 == Viewtype::Image {
imgpart.is_meta = true;
}
}
}
}
if mimeparser.is_send_by_messenger && mimeparser.parts.len() == 2 {
let need_drop = {
let textpart = &mimeparser.parts[0];
let filepart = &mimeparser.parts[1];
textpart.type_0 == Viewtype::Text
&& (filepart.type_0 == Viewtype::Image
|| filepart.type_0 == Viewtype::Gif
|| filepart.type_0 == Viewtype::Audio
|| filepart.type_0 == Viewtype::Voice
|| filepart.type_0 == Viewtype::Video
|| filepart.type_0 == Viewtype::File)
&& !filepart.is_meta
};
if need_drop {
let mut filepart = mimeparser.parts.swap_remove(1);
// insert new one
filepart.msg = mimeparser.parts[0].msg.as_ref().map(|s| s.to_string());
// forget the one we use now
mimeparser.parts[0].msg = None;
// swap new with old
std::mem::replace(&mut mimeparser.parts[0], filepart);
}
}
if let Some(ref subject) = mimeparser.subject {
let mut prepend_subject: libc::c_int = 1i32;
if !mimeparser.decrypting_failed {
let colon = subject.find(':');
if colon == Some(2)
|| colon == Some(3)
|| mimeparser.is_send_by_messenger
|| subject.contains("Chat:")
{
prepend_subject = 0i32
}
}
if 0 != prepend_subject {
let subj = if let Some(n) = subject.find('[') {
&subject[0..n]
} else {
subject
}
.trim();
if !subj.is_empty() {
for part in mimeparser.parts.iter_mut() {
if part.type_0 == Viewtype::Text {
let new_txt = format!(
"{} {}",
subj,
part.msg.as_ref().expect("missing msg part")
);
part.msg = Some(new_txt);
break;
}
}
}
}
}
if mimeparser.is_forwarded {
for part in mimeparser.parts.iter_mut() {
part.param.set_int(Param::Forwarded, 1);
}
}
if mimeparser.parts.len() == 1 {
if mimeparser.parts[0].type_0 == Viewtype::Audio {
if !dc_mimeparser_lookup_optional_field(&mimeparser, "Chat-Voice-Message").is_null()
{
let part_mut = &mut mimeparser.parts[0];
part_mut.type_0 = Viewtype::Voice;
}
}
let part = &mimeparser.parts[0];
if part.type_0 == Viewtype::Audio
|| part.type_0 == Viewtype::Voice
|| part.type_0 == Viewtype::Video
{
let field_0 = dc_mimeparser_lookup_optional_field(&mimeparser, "Chat-Duration");
if !field_0.is_null() {
let duration_ms: libc::c_int = dc_atoi_null_is_0((*field_0).fld_value);
if duration_ms > 0i32 && duration_ms < 24i32 * 60i32 * 60i32 * 1000i32 {
let part_mut = &mut mimeparser.parts[0];
part_mut.param.set_int(Param::Duration, duration_ms);
}
}
}
}
if !mimeparser.decrypting_failed {
let dn_field: *const mailimf_optional_field = dc_mimeparser_lookup_optional_field(
&mimeparser,
"Chat-Disposition-Notification-To",
);
if !dn_field.is_null() && dc_mimeparser_get_last_nonmeta(&mut mimeparser).is_some() {
let mut mb_list: *mut mailimf_mailbox_list = ptr::null_mut();
let mut index_0: libc::size_t = 0;
if mailimf_mailbox_list_parse(
(*dn_field).fld_value,
strlen((*dn_field).fld_value),
&mut index_0,
&mut mb_list,
) == MAILIMF_NO_ERROR as libc::c_int
&& !mb_list.is_null()
{
let dn_to_addr: *mut libc::c_char = mailimf_find_first_addr(mb_list);
if !dn_to_addr.is_null() {
let from_field: *mut mailimf_field =
dc_mimeparser_lookup_field(&mimeparser, "From");
if !from_field.is_null()
&& (*from_field).fld_type == MAILIMF_FIELD_FROM as libc::c_int
&& !(*from_field).fld_data.fld_from.is_null()
{
let from_addr: *mut libc::c_char = mailimf_find_first_addr(
(*(*from_field).fld_data.fld_from).frm_mb_list,
);
if !from_addr.is_null() {
if strcmp(from_addr, dn_to_addr) == 0i32 {
if let Some(part_4) =
dc_mimeparser_get_last_nonmeta(&mut mimeparser)
{
part_4.param.set_int(Param::WantsMdn, 1);
}
}
free(from_addr as *mut libc::c_void);
}
}
free(dn_to_addr as *mut libc::c_void);
}
mailimf_mailbox_list_free(mb_list);
}
}
}
}
/* Cleanup - and try to create at least an empty part if there are no parts yet */
if dc_mimeparser_get_last_nonmeta(&mut mimeparser).is_none() && mimeparser.reports.is_empty() {
let mut part_5 = dc_mimepart_new();
part_5.type_0 = Viewtype::Text;
part_5.msg = Some("".into());
if let Some(ref subject) = mimeparser.subject {
if !mimeparser.is_send_by_messenger {
part_5.msg = Some(subject.to_string())
}
}
mimeparser.parts.push(part_5);
};
mimeparser
}
/*******************************************************************************
* a MIME part
******************************************************************************/
unsafe fn dc_mimepart_new() -> dc_mimepart_t {
dc_mimepart_t {
type_0: Viewtype::Unknown,
is_meta: false,
int_mimetype: 0,
msg: None,
msg_raw: None,
bytes: 0,
param: Params::new(),
}
}
pub fn dc_mimeparser_get_last_nonmeta<'a>(
mimeparser: &'a mut dc_mimeparser_t,
) -> Option<&'a mut dc_mimepart_t> {
mimeparser.parts.iter_mut().rev().find(|part| !part.is_meta)
}
/*the result must be freed*/
pub unsafe fn mailimf_find_first_addr(mb_list: *const mailimf_mailbox_list) -> *mut libc::c_char {
if mb_list.is_null() {
return ptr::null_mut();
}
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() && !(*mb).mb_addr_spec.is_null() {
return addr_normalize(as_str((*mb).mb_addr_spec)).strdup();
}
cur = if !cur.is_null() {
(*cur).next
} else {
ptr::null_mut()
}
}
ptr::null_mut()
}
/* the following functions can be used only after a call to dc_mimeparser_parse() */
pub fn dc_mimeparser_lookup_field(
mimeparser: &dc_mimeparser_t,
field_name: &str,
) -> *mut mailimf_field {
mimeparser
.header
.get(field_name)
.map(|v| *v)
.unwrap_or_else(|| std::ptr::null_mut())
}
pub unsafe fn dc_mimeparser_lookup_optional_field(
mimeparser: &dc_mimeparser_t,
field_name: &str,
) -> *mut mailimf_optional_field {
let field = mimeparser
.header
.get(field_name)
.map(|v| *v)
.unwrap_or_else(|| std::ptr::null_mut());
if !field.is_null() && (*field).fld_type == MAILIMF_FIELD_OPTIONAL_FIELD as libc::c_int {
return (*field).fld_data.fld_optional_field;
}
ptr::null_mut()
}
unsafe fn dc_mimeparser_parse_mime_recursive(
mimeparser: &mut dc_mimeparser_t,
mime: *mut mailmime,
) -> libc::c_int {
let mut any_part_added: libc::c_int = 0i32;
let mut cur: *mut clistiter;
if mime.is_null() {
return 0i32;
}
if !mailmime_find_ct_parameter(
mime,
b"protected-headers\x00" as *const u8 as *const libc::c_char,
)
.is_null()
{
if (*mime).mm_type == MAILMIME_SINGLE as libc::c_int
&& (*(*(*mime).mm_content_type).ct_type).tp_type
== MAILMIME_TYPE_DISCRETE_TYPE as libc::c_int
&& (*(*(*(*mime).mm_content_type).ct_type)
.tp_data
.tp_discrete_type)
.dt_type
== MAILMIME_DISCRETE_TYPE_TEXT as libc::c_int
&& !(*(*mime).mm_content_type).ct_subtype.is_null()
&& strcmp(
(*(*mime).mm_content_type).ct_subtype,
b"rfc822-headers\x00" as *const u8 as *const libc::c_char,
) == 0i32
{
info!(
mimeparser.context,
"Protected headers found in text/rfc822-headers attachment: Will be ignored.",
);
return 0i32;
}
if mimeparser.header_protected.is_null() {
let mut dummy: libc::size_t = 0;
if mailimf_envelope_and_optional_fields_parse(
(*mime).mm_mime_start,
(*mime).mm_length,
&mut dummy,
&mut mimeparser.header_protected,
) != MAILIMF_NO_ERROR as libc::c_int
|| mimeparser.header_protected.is_null()
{
warn!(mimeparser.context, "Protected headers parsing error.",);
} else {
hash_header(
&mut mimeparser.header,
mimeparser.header_protected,
mimeparser.context,
);
}
} else {
info!(
mimeparser.context,
"Protected headers found in MIME header: Will be ignored as we already found an outer one."
);
}
}
match (*mime).mm_type {
// TODO match on enums /rtn
1 => any_part_added = dc_mimeparser_add_single_part_if_known(mimeparser, mime),
2 => {
match mailmime_get_mime_type(mime, ptr::null_mut(), ptr::null_mut()) {
10 => {
cur = (*(*mime).mm_data.mm_multipart.mm_mp_list).first;
while !cur.is_null() {
let childmime: *mut mailmime = (if !cur.is_null() {
(*cur).data
} else {
ptr::null_mut()
}) as *mut mailmime;
if mailmime_get_mime_type(childmime, ptr::null_mut(), ptr::null_mut())
== 30i32
{
any_part_added =
dc_mimeparser_parse_mime_recursive(mimeparser, childmime);
break;
} else {
cur = if !cur.is_null() {
(*cur).next
} else {
ptr::null_mut()
}
}
}
if 0 == any_part_added {
cur = (*(*mime).mm_data.mm_multipart.mm_mp_list).first;
while !cur.is_null() {
let childmime_0: *mut mailmime = (if !cur.is_null() {
(*cur).data
} else {
ptr::null_mut()
})
as *mut mailmime;
if mailmime_get_mime_type(childmime_0, ptr::null_mut(), ptr::null_mut())
== 60i32
{
any_part_added =
dc_mimeparser_parse_mime_recursive(mimeparser, childmime_0);
break;
} else {
cur = if !cur.is_null() {
(*cur).next
} else {
ptr::null_mut()
}
}
}
}
if 0 == any_part_added {
cur = (*(*mime).mm_data.mm_multipart.mm_mp_list).first;
while !cur.is_null() {
if 0 != dc_mimeparser_parse_mime_recursive(
mimeparser,
(if !cur.is_null() {
(*cur).data
} else {
ptr::null_mut()
}) as *mut mailmime,
) {
any_part_added = 1i32;
/* out of for() */
break;
} else {
cur = if !cur.is_null() {
(*cur).next
} else {
ptr::null_mut()
}
}
}
}
}
20 => {
cur = (*(*mime).mm_data.mm_multipart.mm_mp_list).first;
if !cur.is_null() {
any_part_added = dc_mimeparser_parse_mime_recursive(
mimeparser,
(if !cur.is_null() {
(*cur).data
} else {
ptr::null_mut()
}) as *mut mailmime,
)
}
}
40 => {
let mut part = dc_mimepart_new();
part.type_0 = Viewtype::Text;
let msg_body = mimeparser
.context
.stock_str(StockMessage::CantDecryptMsgBody);
let txt = format!("[{}]", msg_body);
part.msg_raw = Some(txt.clone());
part.msg = Some(txt);
mimeparser.parts.push(part);
any_part_added = 1i32;
mimeparser.decrypting_failed = true;
}
46 => {
cur = (*(*mime).mm_data.mm_multipart.mm_mp_list).first;
if !cur.is_null() {
any_part_added = dc_mimeparser_parse_mime_recursive(
mimeparser,
(if !cur.is_null() {
(*cur).data
} else {
ptr::null_mut()
}) as *mut mailmime,
)
}
}
45 => {
if (*(*mime).mm_data.mm_multipart.mm_mp_list).count >= 2i32 {
let report_type: *mut mailmime_parameter = mailmime_find_ct_parameter(
mime,
b"report-type\x00" as *const u8 as *const libc::c_char,
);
if !report_type.is_null()
&& !(*report_type).pa_value.is_null()
&& strcmp(
(*report_type).pa_value,
b"disposition-notification\x00" as *const u8 as *const libc::c_char,
) == 0i32
{
mimeparser.reports.push(mime);
} else {
any_part_added = dc_mimeparser_parse_mime_recursive(
mimeparser,
(if !(*(*mime).mm_data.mm_multipart.mm_mp_list).first.is_null() {
(*(*(*mime).mm_data.mm_multipart.mm_mp_list).first).data
} else {
ptr::null_mut()
}) as *mut mailmime,
)
}
}
}
_ => {
let mut skip_part: *mut mailmime = ptr::null_mut();
let mut html_part: *mut mailmime = ptr::null_mut();
let mut plain_cnt: libc::c_int = 0i32;
let mut html_cnt: libc::c_int = 0i32;
cur = (*(*mime).mm_data.mm_multipart.mm_mp_list).first;
while !cur.is_null() {
let childmime_1: *mut mailmime = (if !cur.is_null() {
(*cur).data
} else {
ptr::null_mut()
})
as *mut mailmime;
if mailmime_get_mime_type(childmime_1, ptr::null_mut(), ptr::null_mut())
== 60i32
{
plain_cnt += 1
} else if mailmime_get_mime_type(
childmime_1,
ptr::null_mut(),
ptr::null_mut(),
) == 70i32
{
html_part = childmime_1;
html_cnt += 1
}
cur = if !cur.is_null() {
(*cur).next
} else {
ptr::null_mut()
}
}
if plain_cnt == 1i32 && html_cnt == 1i32 {
warn!(
mimeparser.context,
"HACK: multipart/mixed message found with PLAIN and HTML, we\'ll skip the HTML part as this seems to be unwanted."
);
skip_part = html_part
}
cur = (*(*mime).mm_data.mm_multipart.mm_mp_list).first;
while !cur.is_null() {
let childmime_2: *mut mailmime = (if !cur.is_null() {
(*cur).data
} else {
ptr::null_mut()
})
as *mut mailmime;
if childmime_2 != skip_part {
if 0 != dc_mimeparser_parse_mime_recursive(mimeparser, childmime_2) {
any_part_added = 1i32
}
}
cur = if !cur.is_null() {
(*cur).next
} else {
ptr::null_mut()
}
}
}
}
}
3 => {
if mimeparser.header_root.is_null() {
mimeparser.header_root = (*mime).mm_data.mm_message.mm_fields;
hash_header(
&mut mimeparser.header,
mimeparser.header_root,
mimeparser.context,
);
}
if !(*mime).mm_data.mm_message.mm_msg_mime.is_null() {
any_part_added = dc_mimeparser_parse_mime_recursive(
mimeparser,
(*mime).mm_data.mm_message.mm_msg_mime,
)
}
}
_ => {}
}
any_part_added
}
unsafe fn hash_header(
out: &mut HashMap<String, *mut mailimf_field>,
in_0: *const mailimf_fields,
_context: &Context,
) {
if in_0.is_null() {
return;
}
let mut cur1: *mut clistiter = (*(*in_0).fld_list).first;
while !cur1.is_null() {
let field: *mut mailimf_field = (if !cur1.is_null() {
(*cur1).data
} else {
ptr::null_mut()
}) as *mut mailimf_field;
let mut key: *const libc::c_char = ptr::null();
// TODO match on enums /rtn
match (*field).fld_type {
1 => key = b"Return-Path\x00" as *const u8 as *const libc::c_char,
9 => key = b"Date\x00" as *const u8 as *const libc::c_char,
10 => key = b"From\x00" as *const u8 as *const libc::c_char,
11 => key = b"Sender\x00" as *const u8 as *const libc::c_char,
12 => key = b"Reply-To\x00" as *const u8 as *const libc::c_char,
13 => key = b"To\x00" as *const u8 as *const libc::c_char,
14 => key = b"Cc\x00" as *const u8 as *const libc::c_char,
15 => key = b"Bcc\x00" as *const u8 as *const libc::c_char,
16 => key = b"Message-ID\x00" as *const u8 as *const libc::c_char,
17 => key = b"In-Reply-To\x00" as *const u8 as *const libc::c_char,
18 => key = b"References\x00" as *const u8 as *const libc::c_char,
19 => key = b"Subject\x00" as *const u8 as *const libc::c_char,
22 => {
// MAILIMF_FIELD_OPTIONAL_FIELD
let optional_field: *const mailimf_optional_field =
(*field).fld_data.fld_optional_field;
if !optional_field.is_null() {
key = (*optional_field).fld_name
}
}
_ => {}
}
if !key.is_null() {
// XXX the optional field sometimes contains invalid UTF8
// which should not happen (according to the mime standard).
// This might point to a bug in our mime parsing/processing
// logic. As mmime/dc_mimeparser is scheduled fore replacement
// anyway we just use a lossy conversion.
let key_r = &to_string_lossy(key);
if !out.contains_key(key_r) || // key already exists, only overwrite known types (protected headers)
(*field).fld_type != MAILIMF_FIELD_OPTIONAL_FIELD as i32 || key_r.starts_with("Chat-")
{
out.insert(key_r.to_string(), field);
}
}
cur1 = if !cur1.is_null() {
(*cur1).next
} else {
ptr::null_mut()
}
}
}
unsafe fn mailmime_get_mime_type(
mime: *mut mailmime,
mut msg_type: *mut Viewtype,
raw_mime: *mut *mut libc::c_char,
) -> libc::c_int {
let c: *mut mailmime_content = (*mime).mm_content_type;
let mut dummy = Viewtype::Unknown;
if msg_type.is_null() {
msg_type = &mut dummy
}
*msg_type = Viewtype::Unknown;
if c.is_null() || (*c).ct_type.is_null() {
return 0i32;
}
// TODO match on enums /rtn
match (*(*c).ct_type).tp_type {
1 => match (*(*(*c).ct_type).tp_data.tp_discrete_type).dt_type {
1 => {
if !(0 != mailmime_is_attachment_disposition(mime)) {
if strcmp(
(*c).ct_subtype,
b"plain\x00" as *const u8 as *const libc::c_char,
) == 0i32
{
*msg_type = Viewtype::Text;
return 60i32;
} else {
if strcmp(
(*c).ct_subtype,
b"html\x00" as *const u8 as *const libc::c_char,
) == 0i32
{
*msg_type = Viewtype::Text;
return 70i32;
}
}
}
*msg_type = Viewtype::File;
if !raw_mime.is_null() {
*raw_mime = reconcat_mime(Some("text"), as_opt_str((*c).ct_subtype)).strdup();
}
return 110i32;
}
2 => {
if strcmp(
(*c).ct_subtype,
b"gif\x00" as *const u8 as *const libc::c_char,
) == 0i32
{
*msg_type = Viewtype::Gif;
} else if strcmp(
(*c).ct_subtype,
b"svg+xml\x00" as *const u8 as *const libc::c_char,
) == 0i32
{
*msg_type = Viewtype::File;
if !raw_mime.is_null() {
*raw_mime =
reconcat_mime(Some("image"), as_opt_str((*c).ct_subtype)).strdup();
}
return 110i32;
} else {
*msg_type = Viewtype::Image;
}
if !raw_mime.is_null() {
*raw_mime = reconcat_mime(Some("image"), as_opt_str((*c).ct_subtype)).strdup();
}
return 80i32;
}
3 => {
*msg_type = Viewtype::Audio;
if !raw_mime.is_null() {
*raw_mime = reconcat_mime(Some("audio"), as_opt_str((*c).ct_subtype)).strdup();
}
return 90i32;
}
4 => {
*msg_type = Viewtype::Video;
if !raw_mime.is_null() {
*raw_mime = reconcat_mime(Some("video"), as_opt_str((*c).ct_subtype)).strdup();
}
return 100i32;
}
_ => {
*msg_type = Viewtype::File;
if (*(*(*c).ct_type).tp_data.tp_discrete_type).dt_type
== MAILMIME_DISCRETE_TYPE_APPLICATION as libc::c_int
&& strcmp(
(*c).ct_subtype,
b"autocrypt-setup\x00" as *const u8 as *const libc::c_char,
) == 0i32
{
if !raw_mime.is_null() {
*raw_mime = reconcat_mime(None, as_opt_str((*c).ct_subtype)).strdup();
}
return 111i32;
}
if !raw_mime.is_null() {
*raw_mime = reconcat_mime(
as_opt_str((*(*(*c).ct_type).tp_data.tp_discrete_type).dt_extension),
as_opt_str((*c).ct_subtype),
)
.strdup();
}
return 110i32;
}
},
2 => {
if (*(*(*c).ct_type).tp_data.tp_composite_type).ct_type
== MAILMIME_COMPOSITE_TYPE_MULTIPART as libc::c_int
{
if strcmp(
(*c).ct_subtype,
b"alternative\x00" as *const u8 as *const libc::c_char,
) == 0i32
{
return 10i32;
} else if strcmp(
(*c).ct_subtype,
b"related\x00" as *const u8 as *const libc::c_char,
) == 0i32
{
return 20i32;
} else if strcmp(
(*c).ct_subtype,
b"encrypted\x00" as *const u8 as *const libc::c_char,
) == 0i32
{
return 40i32;
} else if strcmp(
(*c).ct_subtype,
b"signed\x00" as *const u8 as *const libc::c_char,
) == 0i32
{
return 46i32;
} else if strcmp(
(*c).ct_subtype,
b"mixed\x00" as *const u8 as *const libc::c_char,
) == 0i32
{
return 30i32;
} else if strcmp(
(*c).ct_subtype,
b"report\x00" as *const u8 as *const libc::c_char,
) == 0i32
{
return 45i32;
} else {
return 50i32;
}
} else {
if (*(*(*c).ct_type).tp_data.tp_composite_type).ct_type
== MAILMIME_COMPOSITE_TYPE_MESSAGE as libc::c_int
{
return 0i32;
}
}
}
_ => {}
}
0
}
fn reconcat_mime(type_0: Option<&str>, subtype: Option<&str>) -> String {
let type_0 = type_0.unwrap_or("application");
let subtype = subtype.unwrap_or("octet-stream");
format!("{}/{}", type_0, subtype)
}
unsafe fn mailmime_is_attachment_disposition(mime: *mut mailmime) -> libc::c_int {
if !(*mime).mm_mime_fields.is_null() {
let mut cur: *mut clistiter = (*(*(*mime).mm_mime_fields).fld_list).first;
while !cur.is_null() {
let field: *mut mailmime_field = (if !cur.is_null() {
(*cur).data
} else {
ptr::null_mut()
}) as *mut mailmime_field;
if !field.is_null()
&& (*field).fld_type == MAILMIME_FIELD_DISPOSITION as libc::c_int
&& !(*field).fld_data.fld_disposition.is_null()
{
if !(*(*field).fld_data.fld_disposition).dsp_type.is_null()
&& (*(*(*field).fld_data.fld_disposition).dsp_type).dsp_type
== MAILMIME_DISPOSITION_TYPE_ATTACHMENT as libc::c_int
{
return 1i32;
}
}
cur = if !cur.is_null() {
(*cur).next
} else {
ptr::null_mut()
}
}
}
0
}
/* low-level-tools for working with mailmime structures directly */
pub unsafe fn mailmime_find_ct_parameter(
mime: *mut mailmime,
name: *const libc::c_char,
) -> *mut mailmime_parameter {
if mime.is_null()
|| name.is_null()
|| (*mime).mm_content_type.is_null()
|| (*(*mime).mm_content_type).ct_parameters.is_null()
{
return ptr::null_mut();
}
let mut cur: *mut clistiter;
cur = (*(*(*mime).mm_content_type).ct_parameters).first;
while !cur.is_null() {
let param: *mut mailmime_parameter = (if !cur.is_null() {
(*cur).data
} else {
ptr::null_mut()
}) as *mut mailmime_parameter;
if !param.is_null() && !(*param).pa_name.is_null() {
if strcmp((*param).pa_name, name) == 0i32 {
return param;
}
}
cur = if !cur.is_null() {
(*cur).next
} else {
ptr::null_mut()
}
}
ptr::null_mut()
}
unsafe fn dc_mimeparser_add_single_part_if_known(
mimeparser: &mut dc_mimeparser_t,
mime: *mut mailmime,
) -> libc::c_int {
let mut ok_to_continue = true;
let old_part_count = mimeparser.parts.len();
let mime_type: libc::c_int;
let mime_data: *mut mailmime_data;
let file_suffix: *mut libc::c_char = ptr::null_mut();
let mut desired_filename: *mut libc::c_char = ptr::null_mut();
let mut msg_type = Viewtype::Unknown;
let mut raw_mime: *mut libc::c_char = ptr::null_mut();
/* mmap_string_unref()'d if set */
let mut transfer_decoding_buffer: *mut libc::c_char = ptr::null_mut();
/* must not be free()'d */
let mut decoded_data: *const libc::c_char = ptr::null();
let mut decoded_data_bytes = 0;
let mut simplifier: Option<Simplify> = None;
if !(mime.is_null() || (*mime).mm_data.mm_single.is_null()) {
mime_type = mailmime_get_mime_type(mime, &mut msg_type, &mut raw_mime);
mime_data = (*mime).mm_data.mm_single;
/* MAILMIME_DATA_FILE indicates, the data is in a file; AFAIK this is not used on parsing */
if !((*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)
{
/* regard `Content-Transfer-Encoding:` */
if mailmime_transfer_decode(
mime,
&mut decoded_data,
&mut decoded_data_bytes,
&mut transfer_decoding_buffer,
) {
/* no always error - but no data */
match mime_type {
60 | 70 => {
if simplifier.is_none() {
simplifier = Some(Simplify::new());
}
/* get from `Content-Type: text/...; charset=utf-8`; must not be free()'d */
let charset = mailmime_content_charset_get((*mime).mm_content_type);
if !charset.is_null()
&& strcmp(charset, b"utf-8\x00" as *const u8 as *const libc::c_char)
!= 0i32
&& strcmp(charset, b"UTF-8\x00" as *const u8 as *const libc::c_char)
!= 0i32
{
if let Some(encoding) = Charset::for_label(
CStr::from_ptr(charset).to_str().unwrap().as_bytes(),
) {
let data = std::slice::from_raw_parts(
decoded_data as *const u8,
decoded_data_bytes,
);
let (res, _, _) = encoding.decode(data);
info!(mimeparser.context, "decoded message: '{}'", res);
if res.is_empty() {
/* no error - but nothing to add */
ok_to_continue = false;
} else {
let b = res.as_bytes();
decoded_data = b.as_ptr() as *const libc::c_char;
decoded_data_bytes = b.len();
std::mem::forget(res);
}
} else {
warn!(
mimeparser.context,
"Cannot convert {} bytes from \"{}\" to \"utf-8\".",
decoded_data_bytes as libc::c_int,
as_str(charset),
);
}
}
if ok_to_continue {
/* check header directly as is_send_by_messenger is not yet set up */
let is_msgrmsg =
!dc_mimeparser_lookup_optional_field(&mimeparser, "Chat-Version")
.is_null();
let simplified_txt =
if decoded_data_bytes <= 0 || decoded_data.is_null() {
"".into()
} else {
let input_c = strndup(decoded_data, decoded_data_bytes as _);
let input = to_string_lossy(input_c);
let is_html = mime_type == 70;
free(input_c as *mut _);
simplifier.unwrap().simplify(&input, is_html, is_msgrmsg)
};
if !simplified_txt.is_empty() {
let mut part = dc_mimepart_new();
part.type_0 = Viewtype::Text;
part.int_mimetype = mime_type;
part.msg = Some(simplified_txt);
part.msg_raw = {
let raw_c =
strndup(decoded_data, decoded_data_bytes as libc::c_ulong);
let raw = to_string_lossy(raw_c);
free(raw_c.cast());
Some(raw)
};
do_add_single_part(mimeparser, part);
}
if simplifier.unwrap().is_forwarded {
mimeparser.is_forwarded = true;
}
}
}
80 | 90 | 100 | 110 | 111 => {
/* try to get file name from
`Content-Disposition: ... filename*=...`
or `Content-Disposition: ... filename*0*=... filename*1*=... filename*2*=...`
or `Content-Disposition: ... filename=...` */
let mut filename_parts = String::new();
let mut cur1: *mut clistiter = (*(*(*mime).mm_mime_fields).fld_list).first;
while !cur1.is_null() {
let field: *mut mailmime_field = (if !cur1.is_null() {
(*cur1).data
} else {
ptr::null_mut()
})
as *mut mailmime_field;
if !field.is_null()
&& (*field).fld_type == MAILMIME_FIELD_DISPOSITION as libc::c_int
&& !(*field).fld_data.fld_disposition.is_null()
{
let file_disposition: *mut mailmime_disposition =
(*field).fld_data.fld_disposition;
if !file_disposition.is_null() {
let mut cur2: *mut clistiter =
(*(*file_disposition).dsp_parms).first;
while !cur2.is_null() {
let dsp_param: *mut mailmime_disposition_parm =
(if !cur2.is_null() {
(*cur2).data
} else {
ptr::null_mut()
})
as *mut mailmime_disposition_parm;
if !dsp_param.is_null() {
if (*dsp_param).pa_type
== MAILMIME_DISPOSITION_PARM_PARAMETER
as libc::c_int
&& !(*dsp_param).pa_data.pa_parameter.is_null()
&& !(*(*dsp_param).pa_data.pa_parameter)
.pa_name
.is_null()
&& strncmp(
(*(*dsp_param).pa_data.pa_parameter).pa_name,
b"filename*\x00" as *const u8
as *const libc::c_char,
9,
) == 0i32
{
filename_parts += &to_string_lossy(
(*(*dsp_param).pa_data.pa_parameter).pa_value,
);
} else if (*dsp_param).pa_type
== MAILMIME_DISPOSITION_PARM_FILENAME as libc::c_int
{
desired_filename = dc_decode_header_words(
(*dsp_param).pa_data.pa_filename,
)
}
}
cur2 = if !cur2.is_null() {
(*cur2).next
} else {
ptr::null_mut()
}
}
}
break;
} else {
cur1 = if !cur1.is_null() {
(*cur1).next
} else {
ptr::null_mut()
}
}
}
if !filename_parts.is_empty() {
free(desired_filename as *mut libc::c_void);
desired_filename =
dc_decode_ext_header(filename_parts.as_bytes()).strdup();
}
if desired_filename.is_null() {
let param = mailmime_find_ct_parameter(
mime,
b"name\x00" as *const u8 as *const libc::c_char,
);
if !param.is_null()
&& !(*param).pa_value.is_null()
&& 0 != *(*param).pa_value.offset(0isize) as libc::c_int
{
desired_filename = dc_strdup((*param).pa_value)
}
}
/* if there is still no filename, guess one */
if desired_filename.is_null() {
if !(*mime).mm_content_type.is_null()
&& !(*(*mime).mm_content_type).ct_subtype.is_null()
{
desired_filename = format!(
"file.{}",
as_str((*(*mime).mm_content_type).ct_subtype)
)
.strdup();
} else {
ok_to_continue = false;
}
}
if ok_to_continue {
if strncmp(
desired_filename,
b"location\x00" as *const u8 as *const libc::c_char,
8,
) == 0i32
&& strncmp(
desired_filename
.offset(strlen(desired_filename) as isize)
.offset(-4isize),
b".kml\x00" as *const u8 as *const libc::c_char,
4,
) == 0i32
{
if !decoded_data.is_null() && decoded_data_bytes > 0 {
let d =
dc_null_terminate(decoded_data, decoded_data_bytes as i32);
mimeparser.location_kml =
location::Kml::parse(mimeparser.context, as_str(d)).ok();
free(d.cast());
}
} else if strncmp(
desired_filename,
b"message\x00" as *const u8 as *const libc::c_char,
7,
) == 0i32
&& strncmp(
desired_filename
.offset(strlen(desired_filename) as isize)
.offset(-4isize),
b".kml\x00" as *const u8 as *const libc::c_char,
4,
) == 0i32
{
if !decoded_data.is_null() && decoded_data_bytes > 0 {
let d =
dc_null_terminate(decoded_data, decoded_data_bytes as i32);
mimeparser.message_kml =
location::Kml::parse(mimeparser.context, as_str(d)).ok();
free(d.cast());
}
} else {
dc_replace_bad_utf8_chars(desired_filename);
do_add_single_file_part(
mimeparser,
msg_type,
mime_type,
as_str(raw_mime),
decoded_data,
decoded_data_bytes,
desired_filename,
);
}
}
}
_ => {}
}
}
}
}
/* add object? (we do not add all objects, eg. signatures etc. are ignored) */
if !transfer_decoding_buffer.is_null() {
mmap_string_unref(transfer_decoding_buffer);
}
free(file_suffix as *mut libc::c_void);
free(desired_filename as *mut libc::c_void);
free(raw_mime as *mut libc::c_void);
(mimeparser.parts.len() > old_part_count) as libc::c_int
}
#[allow(non_snake_case)]
unsafe fn do_add_single_file_part(
parser: &mut dc_mimeparser_t,
msg_type: Viewtype,
mime_type: libc::c_int,
raw_mime: &str,
decoded_data: *const libc::c_char,
decoded_data_bytes: libc::size_t,
desired_filename: *const libc::c_char,
) {
let path_filename =
dc_get_fine_path_filename((*parser).context, "$BLOBDIR", as_str(desired_filename));
let bytes = std::slice::from_raw_parts(decoded_data as *const u8, decoded_data_bytes);
/* copy data to file */
if dc_write_file((*parser).context, &path_filename, bytes) {
let mut part = dc_mimepart_new();
part.type_0 = msg_type;
part.int_mimetype = mime_type;
part.bytes = decoded_data_bytes as libc::c_int;
part.param.set(Param::File, path_filename.to_string_lossy());
part.param.set(Param::MimeType, raw_mime);
if mime_type == 80 {
assert!(!decoded_data.is_null(), "invalid image data");
let data =
std::slice::from_raw_parts(decoded_data as *const u8, decoded_data_bytes as usize);
if let Ok((width, height)) = dc_get_filemeta(data) {
part.param.set_int(Param::Width, width as i32);
part.param.set_int(Param::Height, height as i32);
}
}
do_add_single_part(parser, part);
}
}
unsafe fn do_add_single_part(parser: &mut dc_mimeparser_t, mut part: dc_mimepart_t) {
if (*parser).e2ee_helper.encrypted && (*parser).e2ee_helper.signatures.len() > 0 {
part.param.set_int(Param::GuranteeE2ee, 1);
} else if (*parser).e2ee_helper.encrypted {
part.param.set_int(Param::ErroneousE2ee, 0x2);
}
parser.parts.push(part);
}
pub unsafe fn mailmime_transfer_decode(
mime: *mut mailmime,
ret_decoded_data: *mut *const libc::c_char,
ret_decoded_data_bytes: *mut libc::size_t,
ret_to_mmap_string_unref: *mut *mut libc::c_char,
) -> bool {
let mut mime_transfer_encoding: libc::c_int = MAILMIME_MECHANISM_BINARY as libc::c_int;
let mime_data: *mut mailmime_data;
/* must not be free()'d */
let decoded_data: *const libc::c_char;
let mut decoded_data_bytes: libc::size_t = 0;
/* mmap_string_unref()'d if set */
let mut transfer_decoding_buffer: *mut libc::c_char = ptr::null_mut();
if mime.is_null()
|| ret_decoded_data.is_null()
|| ret_decoded_data_bytes.is_null()
|| ret_to_mmap_string_unref.is_null()
|| !(*ret_decoded_data).is_null()
|| *ret_decoded_data_bytes != 0
|| !(*ret_to_mmap_string_unref).is_null()
{
return false;
}
mime_data = (*mime).mm_data.mm_single;
if !(*mime).mm_mime_fields.is_null() {
let mut cur: *mut clistiter;
cur = (*(*(*mime).mm_mime_fields).fld_list).first;
while !cur.is_null() {
let field: *mut mailmime_field = (if !cur.is_null() {
(*cur).data
} else {
ptr::null_mut()
}) as *mut mailmime_field;
if !field.is_null()
&& (*field).fld_type == MAILMIME_FIELD_TRANSFER_ENCODING as libc::c_int
&& !(*field).fld_data.fld_encoding.is_null()
{
mime_transfer_encoding = (*(*field).fld_data.fld_encoding).enc_type;
break;
} else {
cur = if !cur.is_null() {
(*cur).next
} else {
ptr::null_mut()
}
}
}
}
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
{
decoded_data = (*mime_data).dt_data.dt_text.dt_data;
decoded_data_bytes = (*mime_data).dt_data.dt_text.dt_length;
if decoded_data.is_null() || decoded_data_bytes <= 0 {
return false;
}
} else {
let r: libc::c_int;
let mut current_index = 0;
r = 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
{
return false;
}
decoded_data = transfer_decoding_buffer
}
*ret_decoded_data = decoded_data;
*ret_decoded_data_bytes = decoded_data_bytes;
*ret_to_mmap_string_unref = transfer_decoding_buffer;
true
}
pub unsafe fn dc_mimeparser_is_mailinglist_message(mimeparser: &dc_mimeparser_t) -> bool {
if !dc_mimeparser_lookup_field(&mimeparser, "List-Id").is_null() {
return true;
}
let precedence: *mut mailimf_optional_field =
dc_mimeparser_lookup_optional_field(mimeparser, "Precedence");
if !precedence.is_null() {
if strcasecmp(
(*precedence).fld_value,
b"list\x00" as *const u8 as *const libc::c_char,
) == 0i32
|| strcasecmp(
(*precedence).fld_value,
b"bulk\x00" as *const u8 as *const libc::c_char,
) == 0i32
{
return true;
}
}
false
}
pub unsafe fn dc_mimeparser_sender_equals_recipient(mimeparser: &dc_mimeparser_t) -> libc::c_int {
let mut sender_equals_recipient: libc::c_int = 0i32;
let fld: *const mailimf_field;
let mut fld_from: *const mailimf_from = ptr::null();
let mb: *mut mailimf_mailbox;
if !mimeparser.header_root.is_null() {
/* get From: and check there is exactly one sender */
fld = mailimf_find_field(mimeparser.header_root, MAILIMF_FIELD_FROM as libc::c_int);
if !(fld.is_null()
|| {
fld_from = (*fld).fld_data.fld_from;
fld_from.is_null()
}
|| (*fld_from).frm_mb_list.is_null()
|| (*(*fld_from).frm_mb_list).mb_list.is_null()
|| (*(*(*fld_from).frm_mb_list).mb_list).count != 1i32)
{
mb = (if !(*(*(*fld_from).frm_mb_list).mb_list).first.is_null() {
(*(*(*(*fld_from).frm_mb_list).mb_list).first).data
} else {
ptr::null_mut()
}) as *mut mailimf_mailbox;
if !mb.is_null() {
let from_addr_norm = addr_normalize(as_str((*mb).mb_addr_spec));
let recipients = mailimf_get_recipients(mimeparser.header_root);
if recipients.len() == 1 {
if recipients.contains(from_addr_norm) {
sender_equals_recipient = 1i32;
}
}
}
}
}
sender_equals_recipient
}
pub unsafe fn mailimf_get_recipients(imffields: *mut mailimf_fields) -> HashSet<String> {
/* returned addresses are normalized. */
let mut recipients: HashSet<String> = Default::default();
let mut cur1: *mut clistiter;
cur1 = (*(*imffields).fld_list).first;
while !cur1.is_null() {
let fld: *mut mailimf_field = (if !cur1.is_null() {
(*cur1).data
} else {
ptr::null_mut()
}) 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();
// TODO match on enums /rtn
match (*fld).fld_type {
13 => {
fld_to = (*fld).fld_data.fld_to;
if !fld_to.is_null() {
addr_list = (*fld_to).to_addr_list
}
}
14 => {
fld_cc = (*fld).fld_data.fld_cc;
if !fld_cc.is_null() {
addr_list = (*fld_cc).cc_addr_list
}
}
_ => {}
}
if !addr_list.is_null() {
let mut cur2: *mut clistiter;
cur2 = (*(*addr_list).ad_list).first;
while !cur2.is_null() {
let adr: *mut mailimf_address = (if !cur2.is_null() {
(*cur2).data
} else {
ptr::null_mut()
}) as *mut mailimf_address;
if !adr.is_null() {
if (*adr).ad_type == MAILIMF_ADDRESS_MAILBOX as libc::c_int {
mailimf_get_recipients__add_addr(
&mut recipients,
(*adr).ad_data.ad_mailbox,
);
} 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() {
let mut cur3: *mut clistiter;
cur3 = (*(*(*group).grp_mb_list).mb_list).first;
while !cur3.is_null() {
mailimf_get_recipients__add_addr(
&mut recipients,
(if !cur3.is_null() {
(*cur3).data
} else {
ptr::null_mut()
}) as *mut mailimf_mailbox,
);
cur3 = if !cur3.is_null() {
(*cur3).next
} else {
ptr::null_mut()
}
}
}
}
}
cur2 = if !cur2.is_null() {
(*cur2).next
} else {
ptr::null_mut()
}
}
}
cur1 = if !cur1.is_null() {
(*cur1).next
} else {
ptr::null_mut()
}
}
recipients
}
/* ******************************************************************************
* debug output
******************************************************************************/
/* DEBUG_MIME_OUTPUT */
/* ******************************************************************************
* low-level-tools for getting a list of all recipients
******************************************************************************/
#[allow(non_snake_case)]
unsafe fn mailimf_get_recipients__add_addr(
recipients: &mut HashSet<String>,
mb: *mut mailimf_mailbox,
) {
if !mb.is_null() {
let addr_norm = addr_normalize(as_str((*mb).mb_addr_spec));
recipients.insert(addr_norm.into());
};
}
/*the result is a pointer to mime, must not be freed*/
pub unsafe fn mailimf_find_field(
header: *mut mailimf_fields,
wanted_fld_type: libc::c_int,
) -> *mut mailimf_field {
if header.is_null() || (*header).fld_list.is_null() {
return ptr::null_mut();
}
let mut cur1: *mut clistiter = (*(*header).fld_list).first;
while !cur1.is_null() {
let field: *mut mailimf_field = (if !cur1.is_null() {
(*cur1).data
} else {
ptr::null_mut()
}) as *mut mailimf_field;
if !field.is_null() {
if (*field).fld_type == wanted_fld_type {
return field;
}
}
cur1 = if !cur1.is_null() {
(*cur1).next
} else {
ptr::null_mut()
}
}
ptr::null_mut()
}
pub unsafe fn dc_mimeparser_repl_msg_by_error(
mimeparser: &mut dc_mimeparser_t,
error_msg: *const libc::c_char,
) {
if mimeparser.parts.is_empty() {
return;
}
let part = &mut mimeparser.parts[0];
part.type_0 = Viewtype::Text;
part.msg = Some(format!("[{}]", to_string_lossy(error_msg)));
mimeparser.parts.truncate(1);
assert_eq!(mimeparser.parts.len(), 1);
}
/*the result is a pointer to mime, must not be freed*/
pub unsafe fn mailmime_find_mailimf_fields(mime: *mut mailmime) -> *mut mailimf_fields {
if mime.is_null() {
return ptr::null_mut();
}
match (*mime).mm_type as _ {
MAILMIME_MULTIPLE => {
let mut cur: *mut clistiter = (*(*mime).mm_data.mm_multipart.mm_mp_list).first;
while !cur.is_null() {
let header: *mut mailimf_fields = mailmime_find_mailimf_fields(
(if !cur.is_null() {
(*cur).data
} else {
ptr::null_mut()
}) as *mut mailmime,
);
if !header.is_null() {
return header;
}
cur = if !cur.is_null() {
(*cur).next
} else {
ptr::null_mut()
}
}
}
MAILMIME_MESSAGE => return (*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();
}
let mut cur1: *mut clistiter = (*(*header).fld_list).first;
while !cur1.is_null() {
let field: *mut mailimf_field = (if !cur1.is_null() {
(*cur1).data
} else {
ptr::null_mut()
}) as *mut mailimf_field;
if !field.is_null() && (*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;
}
}
cur1 = if !cur1.is_null() {
(*cur1).next
} else {
ptr::null_mut()
}
}
ptr::null_mut()
}
#[cfg(test)]
mod tests {
use super::*;
use crate::test_utils::*;
use std::ffi::CStr;
#[test]
fn test_mailmime_parse() {
unsafe {
let txt: *const libc::c_char =
b"FieldA: ValueA\nFieldB: ValueB\n\x00" as *const u8 as *const libc::c_char;
let mut mime: *mut mailmime = ptr::null_mut();
let mut dummy = 0;
let res = mailmime_parse(txt, strlen(txt), &mut dummy, &mut mime);
assert_eq!(res, MAIL_NO_ERROR as libc::c_int);
assert!(!mime.is_null());
let fields: *mut mailimf_fields = mailmime_find_mailimf_fields(mime);
assert!(!fields.is_null());
let mut of_a: *mut mailimf_optional_field = mailimf_find_optional_field(
fields,
b"fielda\x00" as *const u8 as *const libc::c_char,
);
assert!(!of_a.is_null());
assert!(!(*of_a).fld_value.is_null());
assert_eq!(
CStr::from_ptr((*of_a).fld_name as *const libc::c_char)
.to_str()
.unwrap(),
"FieldA",
);
assert_eq!(
CStr::from_ptr((*of_a).fld_value as *const libc::c_char)
.to_str()
.unwrap(),
"ValueA",
);
of_a = mailimf_find_optional_field(
fields,
b"FIELDA\x00" as *const u8 as *const libc::c_char,
);
assert!(!of_a.is_null());
assert!(!(*of_a).fld_value.is_null());
assert_eq!(
CStr::from_ptr((*of_a).fld_name as *const libc::c_char)
.to_str()
.unwrap(),
"FieldA",
);
assert_eq!(
CStr::from_ptr((*of_a).fld_value as *const libc::c_char)
.to_str()
.unwrap(),
"ValueA",
);
let of_b: *mut mailimf_optional_field = mailimf_find_optional_field(
fields,
b"FieldB\x00" as *const u8 as *const libc::c_char,
);
assert!(!of_b.is_null());
assert!(!(*of_b).fld_value.is_null());
assert_eq!(
CStr::from_ptr((*of_b).fld_value as *const libc::c_char)
.to_str()
.unwrap(),
"ValueB",
);
mailmime_free(mime);
}
}
#[test]
fn test_dc_mimeparser_with_context() {
unsafe {
let context = dummy_context();
let raw = b"Content-Type: multipart/mixed; boundary=\"==break==\";\nSubject: outer-subject\nX-Special-A: special-a\nFoo: Bar\nChat-Version: 0.0\n\n--==break==\nContent-Type: text/plain; protected-headers=\"v1\";\nSubject: inner-subject\nX-Special-B: special-b\nFoo: Xy\nChat-Version: 1.0\n\ntest1\n\n--==break==--\n\n\x00";
let mut mimeparser = dc_mimeparser_parse(&context.ctx, &raw[..]);
assert_eq!(mimeparser.subject, Some("inner-subject".into()));
let mut of: *mut mailimf_optional_field =
dc_mimeparser_lookup_optional_field(&mimeparser, "X-Special-A");
assert_eq!(
&to_string((*of).fld_value as *const libc::c_char),
"special-a",
);
of = dc_mimeparser_lookup_optional_field(&mimeparser, "Foo");
assert_eq!(&to_string((*of).fld_value as *const libc::c_char), "Bar",);
of = dc_mimeparser_lookup_optional_field(&mimeparser, "Chat-Version");
assert_eq!(&to_string((*of).fld_value as *const libc::c_char), "1.0",);
assert_eq!(mimeparser.parts.len(), 1);
dc_mimeparser_unref(&mut mimeparser);
}
}
}