mirror of
https://github.com/chatmail/core.git
synced 2026-05-09 01:46:30 +03:00
cleanup mimeparser and use mime
This commit is contained in:
@@ -613,17 +613,16 @@ fn add_parts(
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(ref msg) = part.msg {
|
if mime_parser.location_kml.is_some()
|
||||||
if mime_parser.location_kml.is_some()
|
&& icnt == 1
|
||||||
&& icnt == 1
|
&& (part.msg == "-location-" || part.msg.is_empty())
|
||||||
&& (msg == "-location-" || msg.is_empty())
|
{
|
||||||
{
|
*hidden = 1;
|
||||||
*hidden = 1;
|
if state == MessageState::InFresh {
|
||||||
if state == MessageState::InFresh {
|
state = MessageState::InNoticed;
|
||||||
state = MessageState::InNoticed;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if part.typ == Viewtype::Text {
|
if part.typ == Viewtype::Text {
|
||||||
let msg_raw = part.msg_raw.as_ref().cloned().unwrap_or_default();
|
let msg_raw = part.msg_raw.as_ref().cloned().unwrap_or_default();
|
||||||
let subject = mime_parser
|
let subject = mime_parser
|
||||||
@@ -651,11 +650,11 @@ fn add_parts(
|
|||||||
part.typ,
|
part.typ,
|
||||||
state,
|
state,
|
||||||
msgrmsg,
|
msgrmsg,
|
||||||
part.msg.as_ref().map_or("", String::as_str),
|
&part.msg,
|
||||||
// txt_raw might contain invalid utf8
|
// txt_raw might contain invalid utf8
|
||||||
txt_raw.unwrap_or_default(),
|
txt_raw.unwrap_or_default(),
|
||||||
part.param.to_string(),
|
part.param.to_string(),
|
||||||
part.bytes,
|
part.bytes as isize,
|
||||||
*hidden,
|
*hidden,
|
||||||
if save_mime_headers {
|
if save_mime_headers {
|
||||||
Some(String::from_utf8_lossy(imf_raw))
|
Some(String::from_utf8_lossy(imf_raw))
|
||||||
@@ -1512,9 +1511,9 @@ fn set_better_msg(mime_parser: &mut MimeParser, better_msg: impl AsRef<str>) {
|
|||||||
if msg.len() > 0 && !mime_parser.parts.is_empty() {
|
if msg.len() > 0 && !mime_parser.parts.is_empty() {
|
||||||
let part = &mut mime_parser.parts[0];
|
let part = &mut mime_parser.parts[0];
|
||||||
if part.typ == Viewtype::Text {
|
if part.typ == Viewtype::Text {
|
||||||
part.msg = Some(msg.to_string());
|
part.msg = msg.to_string();
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dc_is_reply_to_known_message(context: &Context, mime_parser: &MimeParser) -> libc::c_int {
|
fn dc_is_reply_to_known_message(context: &Context, mime_parser: &MimeParser) -> libc::c_int {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
|
|
||||||
use deltachat_derive::{FromSql, ToSql};
|
use deltachat_derive::{FromSql, ToSql};
|
||||||
|
use lettre_email::mime::{self, Mime};
|
||||||
use mailparse::MailHeaderMap;
|
use mailparse::MailHeaderMap;
|
||||||
|
|
||||||
use crate::aheader::Aheader;
|
use crate::aheader::Aheader;
|
||||||
@@ -62,30 +63,7 @@ impl Default for SystemMessage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
const MIME_AC_SETUP_FILE: &str = "application/autocrypt-setup";
|
||||||
pub enum DcMimeType {
|
|
||||||
Unknown,
|
|
||||||
MpAlternative,
|
|
||||||
MpRelated,
|
|
||||||
MpMixed,
|
|
||||||
MpNotDecryptable,
|
|
||||||
MpReport,
|
|
||||||
MpSigned,
|
|
||||||
MpOther,
|
|
||||||
TextPlain,
|
|
||||||
TextHtml,
|
|
||||||
Image,
|
|
||||||
Audio,
|
|
||||||
Video,
|
|
||||||
File,
|
|
||||||
AcSetupFile,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for DcMimeType {
|
|
||||||
fn default() -> Self {
|
|
||||||
DcMimeType::Unknown
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> MimeParser<'a> {
|
impl<'a> MimeParser<'a> {
|
||||||
pub fn from_bytes(context: &'a Context, body: &[u8]) -> Result<Self> {
|
pub fn from_bytes(context: &'a Context, body: &[u8]) -> Result<Self> {
|
||||||
@@ -176,10 +154,9 @@ impl<'a> MimeParser<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(_) = self.lookup_field("Autocrypt-Setup-Message") {
|
if let Some(_) = self.lookup_field("Autocrypt-Setup-Message") {
|
||||||
let has_setup_file = self
|
let has_setup_file = self.parts.iter().any(|p| {
|
||||||
.parts
|
p.mimetype.is_some() && p.mimetype.as_ref().unwrap().as_ref() == MIME_AC_SETUP_FILE
|
||||||
.iter()
|
});
|
||||||
.any(|p| p.mimetype == DcMimeType::AcSetupFile);
|
|
||||||
|
|
||||||
if has_setup_file {
|
if has_setup_file {
|
||||||
self.is_system_message = SystemMessage::AutocryptSetupMessage;
|
self.is_system_message = SystemMessage::AutocryptSetupMessage;
|
||||||
@@ -197,7 +174,10 @@ impl<'a> MimeParser<'a> {
|
|||||||
|
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
while i != self.parts.len() {
|
while i != self.parts.len() {
|
||||||
if self.parts[i].mimetype != DcMimeType::AcSetupFile {
|
let mimetype = &self.parts[i].mimetype;
|
||||||
|
if mimetype.is_none()
|
||||||
|
|| mimetype.as_ref().unwrap().as_ref() != MIME_AC_SETUP_FILE
|
||||||
|
{
|
||||||
self.parts.remove(i);
|
self.parts.remove(i);
|
||||||
} else {
|
} else {
|
||||||
i += 1;
|
i += 1;
|
||||||
@@ -242,10 +222,10 @@ impl<'a> MimeParser<'a> {
|
|||||||
let mut filepart = self.parts.swap_remove(1);
|
let mut filepart = self.parts.swap_remove(1);
|
||||||
|
|
||||||
// insert new one
|
// insert new one
|
||||||
filepart.msg = self.parts[0].msg.as_ref().map(|s| s.to_string());
|
filepart.msg = self.parts[0].msg.clone();
|
||||||
|
|
||||||
// forget the one we use now
|
// forget the one we use now
|
||||||
self.parts[0].msg = None;
|
self.parts[0].msg = "".to_string();
|
||||||
|
|
||||||
// swap new with old
|
// swap new with old
|
||||||
std::mem::replace(&mut self.parts[0], filepart);
|
std::mem::replace(&mut self.parts[0], filepart);
|
||||||
@@ -274,12 +254,7 @@ impl<'a> MimeParser<'a> {
|
|||||||
if !subj.is_empty() {
|
if !subj.is_empty() {
|
||||||
for part in self.parts.iter_mut() {
|
for part in self.parts.iter_mut() {
|
||||||
if part.typ == Viewtype::Text {
|
if part.typ == Viewtype::Text {
|
||||||
let new_txt = format!(
|
part.msg = format!("{} – {}", subj, part.msg);
|
||||||
"{} – {}",
|
|
||||||
subj,
|
|
||||||
part.msg.as_ref().expect("missing msg part")
|
|
||||||
);
|
|
||||||
part.msg = Some(new_txt);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -345,18 +320,18 @@ impl<'a> MimeParser<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Cleanup - and try to create at least an empty part if there are no parts yet */
|
// Cleanup - and try to create at least an empty part if there are no parts yet
|
||||||
if self.get_last_nonmeta().is_none() && self.reports.is_empty() {
|
if self.get_last_nonmeta().is_none() && self.reports.is_empty() {
|
||||||
let mut part_5 = Part::default();
|
let mut part = Part::default();
|
||||||
part_5.typ = Viewtype::Text;
|
part.typ = Viewtype::Text;
|
||||||
part_5.msg = Some("".into());
|
|
||||||
|
|
||||||
if let Some(ref subject) = self.subject {
|
if let Some(ref subject) = self.subject {
|
||||||
if !self.is_send_by_messenger {
|
if !self.is_send_by_messenger {
|
||||||
part_5.msg = Some(subject.to_string())
|
part.msg = subject.to_string();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.parts.push(part_5);
|
|
||||||
|
self.parts.push(part);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -437,14 +412,15 @@ impl<'a> MimeParser<'a> {
|
|||||||
|
|
||||||
fn handle_multiple(&mut self, mail: &mailparse::ParsedMail<'_>) -> Result<bool> {
|
fn handle_multiple(&mut self, mail: &mailparse::ParsedMail<'_>) -> Result<bool> {
|
||||||
let mut any_part_added = false;
|
let mut any_part_added = false;
|
||||||
match mailmime_get_mime_type(mail) {
|
let mimetype = get_mime_type(mail)?.0;
|
||||||
|
match (mimetype.type_(), mimetype.subtype().as_str()) {
|
||||||
/* Most times, mutlipart/alternative contains true alternatives
|
/* Most times, mutlipart/alternative contains true alternatives
|
||||||
as text/plain and text/html. If we find a multipart/mixed
|
as text/plain and text/html. If we find a multipart/mixed
|
||||||
inside mutlipart/alternative, we use this (happens eg in
|
inside mutlipart/alternative, we use this (happens eg in
|
||||||
apple mail: "plaintext" as an alternative to "html+PDF attachment") */
|
apple mail: "plaintext" as an alternative to "html+PDF attachment") */
|
||||||
(DcMimeType::MpAlternative, _) => {
|
(mime::MULTIPART, "alternative") => {
|
||||||
for cur_data in &mail.subparts {
|
for cur_data in &mail.subparts {
|
||||||
if mailmime_get_mime_type(cur_data).0 == DcMimeType::MpMixed {
|
if get_mime_type(cur_data)?.0 == "multipart/mixed" {
|
||||||
any_part_added = self.parse_mime_recursive(cur_data)?;
|
any_part_added = self.parse_mime_recursive(cur_data)?;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -452,7 +428,7 @@ impl<'a> MimeParser<'a> {
|
|||||||
if !any_part_added {
|
if !any_part_added {
|
||||||
/* search for text/plain and add this */
|
/* search for text/plain and add this */
|
||||||
for cur_data in &mail.subparts {
|
for cur_data in &mail.subparts {
|
||||||
if mailmime_get_mime_type(cur_data).0 == DcMimeType::TextPlain {
|
if get_mime_type(cur_data)?.0.type_() == mime::TEXT {
|
||||||
any_part_added = self.parse_mime_recursive(cur_data)?;
|
any_part_added = self.parse_mime_recursive(cur_data)?;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -468,7 +444,7 @@ impl<'a> MimeParser<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(DcMimeType::MpRelated, _) => {
|
(mime::MULTIPART, "related") => {
|
||||||
/* add the "root part" - the other parts may be referenced which is
|
/* add the "root part" - the other parts may be referenced which is
|
||||||
not interesting for us (eg. embedded images) we assume he "root part"
|
not interesting for us (eg. embedded images) we assume he "root part"
|
||||||
being the first one, which may not be always true ...
|
being the first one, which may not be always true ...
|
||||||
@@ -477,20 +453,21 @@ impl<'a> MimeParser<'a> {
|
|||||||
any_part_added = self.parse_mime_recursive(first)?;
|
any_part_added = self.parse_mime_recursive(first)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(DcMimeType::MpNotDecryptable, _) => {
|
(mime::MULTIPART, "encrypted") => {
|
||||||
|
let msg_body = self.context.stock_str(StockMessage::CantDecryptMsgBody);
|
||||||
|
let txt = format!("[{}]", msg_body);
|
||||||
|
|
||||||
let mut part = Part::default();
|
let mut part = Part::default();
|
||||||
part.typ = Viewtype::Text;
|
part.typ = Viewtype::Text;
|
||||||
let msg_body = self.context.stock_str(StockMessage::CantDecryptMsgBody);
|
|
||||||
|
|
||||||
let txt = format!("[{}]", msg_body);
|
|
||||||
part.msg_raw = Some(txt.clone());
|
part.msg_raw = Some(txt.clone());
|
||||||
part.msg = Some(txt);
|
part.msg = txt;
|
||||||
|
|
||||||
self.parts.push(part);
|
self.parts.push(part);
|
||||||
|
|
||||||
any_part_added = true;
|
any_part_added = true;
|
||||||
self.decrypting_failed = true;
|
self.decrypting_failed = true;
|
||||||
}
|
}
|
||||||
(DcMimeType::MpSigned, _) => {
|
(mime::MULTIPART, "signed") => {
|
||||||
/* RFC 1847: "The multipart/signed content type
|
/* RFC 1847: "The multipart/signed content type
|
||||||
contains exactly two body parts. The first body
|
contains exactly two body parts. The first body
|
||||||
part is the body part over which the digital signature was created [...]
|
part is the body part over which the digital signature was created [...]
|
||||||
@@ -503,7 +480,7 @@ impl<'a> MimeParser<'a> {
|
|||||||
any_part_added = self.parse_mime_recursive(first)?;
|
any_part_added = self.parse_mime_recursive(first)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(DcMimeType::MpReport, _) => {
|
(mime::MULTIPART, "report") => {
|
||||||
info!(self.context, "got report {}", mail.subparts.len());
|
info!(self.context, "got report {}", mail.subparts.len());
|
||||||
/* RFC 6522: the first part is for humans, the second for machines */
|
/* RFC 6522: the first part is for humans, the second for machines */
|
||||||
if mail.subparts.len() >= 2 {
|
if mail.subparts.len() >= 2 {
|
||||||
@@ -525,25 +502,25 @@ impl<'a> MimeParser<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
/* eg. DcMimeType::MpMixed - add all parts (in fact,
|
// Add all parts (in fact,
|
||||||
AddSinglePartIfKnown() later check if the parts are really supported)
|
// AddSinglePartIfKnown() later check if the parts are really supported)
|
||||||
HACK: the following lines are a hack for clients who use
|
// HACK: the following lines are a hack for clients who use
|
||||||
multipart/mixed instead of multipart/alternative for
|
// multipart/mixed instead of multipart/alternative for
|
||||||
combined text/html messages (eg. Stock Android "Mail" does so).
|
// combined text/html messages (eg. Stock Android "Mail" does so).
|
||||||
So, if we detect such a message below, we skip the Html
|
// So, if we detect such a message below, we skip the Html
|
||||||
part. However, not sure, if there are useful situations to use
|
// part. However, not sure, if there are useful situations to use
|
||||||
plain+html in multipart/mixed - if so, we should disable the hack. */
|
// plain+html in multipart/mixed - if so, we should disable the hack.
|
||||||
let mut skip_part = -1;
|
let mut skip_part = -1;
|
||||||
let mut html_part = -1;
|
let mut html_part = -1;
|
||||||
let mut plain_cnt = 0;
|
let mut plain_cnt = 0;
|
||||||
let mut html_cnt = 0;
|
let mut html_cnt = 0;
|
||||||
|
|
||||||
for (i, cur_data) in mail.subparts.iter().enumerate() {
|
for (i, cur_data) in mail.subparts.iter().enumerate() {
|
||||||
match mailmime_get_mime_type(cur_data) {
|
match get_mime_type(cur_data)?.0.type_() {
|
||||||
(DcMimeType::TextPlain, _) => {
|
mime::TEXT => {
|
||||||
plain_cnt += 1;
|
plain_cnt += 1;
|
||||||
}
|
}
|
||||||
(DcMimeType::TextHtml, _) => {
|
mime::HTML => {
|
||||||
html_part = i as isize;
|
html_part = i as isize;
|
||||||
html_cnt += 1;
|
html_cnt += 1;
|
||||||
}
|
}
|
||||||
@@ -559,10 +536,8 @@ impl<'a> MimeParser<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (i, cur_data) in mail.subparts.iter().enumerate() {
|
for (i, cur_data) in mail.subparts.iter().enumerate() {
|
||||||
if i as isize != skip_part {
|
if i as isize != skip_part && self.parse_mime_recursive(cur_data)? {
|
||||||
if self.parse_mime_recursive(cur_data)? {
|
any_part_added = true;
|
||||||
any_part_added = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -573,7 +548,7 @@ impl<'a> MimeParser<'a> {
|
|||||||
|
|
||||||
fn add_single_part_if_known(&mut self, mail: &mailparse::ParsedMail<'_>) -> Result<bool> {
|
fn add_single_part_if_known(&mut self, mail: &mailparse::ParsedMail<'_>) -> Result<bool> {
|
||||||
// return true if a part was added
|
// return true if a part was added
|
||||||
let (mime_type, msg_type) = mailmime_get_mime_type(mail);
|
let (mime_type, msg_type) = get_mime_type(mail)?;
|
||||||
let raw_mime = mail.ctype.mimetype.to_lowercase();
|
let raw_mime = mail.ctype.mimetype.to_lowercase();
|
||||||
|
|
||||||
info!(
|
info!(
|
||||||
@@ -584,8 +559,8 @@ impl<'a> MimeParser<'a> {
|
|||||||
let old_part_count = self.parts.len();
|
let old_part_count = self.parts.len();
|
||||||
|
|
||||||
// regard `Content-Transfer-Encoding:`
|
// regard `Content-Transfer-Encoding:`
|
||||||
match mime_type {
|
match mime_type.type_() {
|
||||||
DcMimeType::TextPlain | DcMimeType::TextHtml => {
|
mime::TEXT | mime::HTML => {
|
||||||
let decoded_data = match mail.get_body() {
|
let decoded_data = match mail.get_body() {
|
||||||
Ok(decoded_data) => decoded_data,
|
Ok(decoded_data) => decoded_data,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
@@ -602,15 +577,15 @@ impl<'a> MimeParser<'a> {
|
|||||||
let simplified_txt = if decoded_data.is_empty() {
|
let simplified_txt = if decoded_data.is_empty() {
|
||||||
"".into()
|
"".into()
|
||||||
} else {
|
} else {
|
||||||
let is_html = mime_type == DcMimeType::TextHtml;
|
let is_html = mime_type == mime::TEXT_HTML;
|
||||||
simplifier.simplify(&decoded_data, is_html, is_msgrmsg)
|
simplifier.simplify(&decoded_data, is_html, is_msgrmsg)
|
||||||
};
|
};
|
||||||
|
|
||||||
if !simplified_txt.is_empty() {
|
if !simplified_txt.is_empty() {
|
||||||
let mut part = Part::default();
|
let mut part = Part::default();
|
||||||
part.typ = Viewtype::Text;
|
part.typ = Viewtype::Text;
|
||||||
part.mimetype = mime_type;
|
part.mimetype = Some(mime_type);
|
||||||
part.msg = Some(simplified_txt);
|
part.msg = simplified_txt;
|
||||||
part.msg_raw = Some(decoded_data);
|
part.msg_raw = Some(decoded_data);
|
||||||
self.do_add_single_part(part);
|
self.do_add_single_part(part);
|
||||||
}
|
}
|
||||||
@@ -619,11 +594,7 @@ impl<'a> MimeParser<'a> {
|
|||||||
self.is_forwarded = true;
|
self.is_forwarded = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DcMimeType::Image
|
mime::IMAGE | mime::AUDIO | mime::VIDEO | mime::APPLICATION => {
|
||||||
| DcMimeType::Audio
|
|
||||||
| DcMimeType::Video
|
|
||||||
| DcMimeType::File
|
|
||||||
| DcMimeType::AcSetupFile => {
|
|
||||||
// try to get file name from
|
// try to get file name from
|
||||||
// `Content-Disposition: ... filename*=...`
|
// `Content-Disposition: ... filename*=...`
|
||||||
// or `Content-Disposition: ... filename*0*=... filename*1*=... filename*2*=...`
|
// or `Content-Disposition: ... filename*0*=... filename*1*=... filename*2*=...`
|
||||||
@@ -672,7 +643,7 @@ impl<'a> MimeParser<'a> {
|
|||||||
fn do_add_single_file_part(
|
fn do_add_single_file_part(
|
||||||
&mut self,
|
&mut self,
|
||||||
msg_type: Viewtype,
|
msg_type: Viewtype,
|
||||||
mime_type: DcMimeType,
|
mime_type: Mime,
|
||||||
raw_mime: &String,
|
raw_mime: &String,
|
||||||
decoded_data: &[u8],
|
decoded_data: &[u8],
|
||||||
filename: &str,
|
filename: &str,
|
||||||
@@ -714,18 +685,19 @@ impl<'a> MimeParser<'a> {
|
|||||||
|
|
||||||
/* create and register Mime part referencing the new Blob object */
|
/* create and register Mime part referencing the new Blob object */
|
||||||
let mut part = Part::default();
|
let mut part = Part::default();
|
||||||
part.typ = msg_type;
|
if mime_type.type_() == mime::IMAGE {
|
||||||
part.mimetype = mime_type;
|
|
||||||
part.bytes = decoded_data.len() as libc::c_int;
|
|
||||||
part.param.set(Param::File, blob.as_name());
|
|
||||||
part.param.set(Param::MimeType, raw_mime);
|
|
||||||
|
|
||||||
if mime_type == DcMimeType::Image {
|
|
||||||
if let Ok((width, height)) = dc_get_filemeta(decoded_data) {
|
if let Ok((width, height)) = dc_get_filemeta(decoded_data) {
|
||||||
part.param.set_int(Param::Width, width as i32);
|
part.param.set_int(Param::Width, width as i32);
|
||||||
part.param.set_int(Param::Height, height as i32);
|
part.param.set_int(Param::Height, height as i32);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
part.typ = msg_type;
|
||||||
|
part.mimetype = Some(mime_type);
|
||||||
|
part.bytes = decoded_data.len();
|
||||||
|
part.param.set(Param::File, blob.as_name());
|
||||||
|
part.param.set(Param::MimeType, raw_mime);
|
||||||
|
|
||||||
self.do_add_single_part(part);
|
self.do_add_single_part(part);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -783,7 +755,7 @@ impl<'a> MimeParser<'a> {
|
|||||||
|
|
||||||
let part = &mut self.parts[0];
|
let part = &mut self.parts[0];
|
||||||
part.typ = Viewtype::Text;
|
part.typ = Viewtype::Text;
|
||||||
part.msg = Some(format!("[{}]", error_msg.as_ref()));
|
part.msg = format!("[{}]", error_msg.as_ref());
|
||||||
self.parts.truncate(1);
|
self.parts.truncate(1);
|
||||||
|
|
||||||
assert_eq!(self.parts.len(), 1);
|
assert_eq!(self.parts.len(), 1);
|
||||||
@@ -968,69 +940,40 @@ fn is_known(key: &str) -> bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug, Clone)]
|
#[derive(Debug, Default, Clone)]
|
||||||
pub struct Part {
|
pub struct Part {
|
||||||
pub typ: Viewtype,
|
pub typ: Viewtype,
|
||||||
pub is_meta: bool,
|
pub is_meta: bool,
|
||||||
pub mimetype: DcMimeType,
|
pub mimetype: Option<Mime>,
|
||||||
pub msg: Option<String>,
|
pub msg: String,
|
||||||
pub msg_raw: Option<String>,
|
pub msg_raw: Option<String>,
|
||||||
pub bytes: i32,
|
pub bytes: usize,
|
||||||
pub param: Params,
|
pub param: Params,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mailmime_get_mime_type(mail: &mailparse::ParsedMail<'_>) -> (DcMimeType, Viewtype) {
|
fn get_mime_type(mail: &mailparse::ParsedMail<'_>) -> Result<(Mime, Viewtype)> {
|
||||||
let unknown_type = (DcMimeType::Unknown, Viewtype::Unknown);
|
let mimetype = mail.ctype.mimetype.parse::<Mime>()?;
|
||||||
|
|
||||||
let mimetype = mail.ctype.mimetype.to_lowercase();
|
let viewtype = match mimetype.type_() {
|
||||||
let mut parts = mimetype.split('/');
|
mime::TEXT => {
|
||||||
let typ = parts.next().expect("invalid mimetype");
|
|
||||||
let subtype = parts.next().unwrap_or_default();
|
|
||||||
|
|
||||||
match typ {
|
|
||||||
"text" => {
|
|
||||||
if !mailmime_is_attachment_disposition(mail) {
|
if !mailmime_is_attachment_disposition(mail) {
|
||||||
if subtype == "plain" {
|
match mimetype.subtype() {
|
||||||
return (DcMimeType::TextPlain, Viewtype::Text);
|
mime::PLAIN | mime::HTML => Viewtype::Text,
|
||||||
}
|
_ => Viewtype::File,
|
||||||
if subtype == "html" {
|
|
||||||
return (DcMimeType::TextHtml, Viewtype::Text);
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
Viewtype::File
|
||||||
}
|
}
|
||||||
|
|
||||||
(DcMimeType::File, Viewtype::File)
|
|
||||||
}
|
}
|
||||||
"image" => {
|
mime::IMAGE => match mimetype.subtype() {
|
||||||
let msg_type = match subtype {
|
mime::GIF => Viewtype::Gif,
|
||||||
"gif" => Viewtype::Gif,
|
mime::SVG => Viewtype::File,
|
||||||
"svg+xml" => {
|
_ => Viewtype::Image,
|
||||||
return (DcMimeType::File, Viewtype::File);
|
},
|
||||||
}
|
mime::AUDIO => Viewtype::Audio,
|
||||||
_ => Viewtype::Image,
|
mime::VIDEO => Viewtype::Video,
|
||||||
};
|
mime::MULTIPART => Viewtype::Unknown,
|
||||||
|
mime::MESSAGE => {
|
||||||
(DcMimeType::Image, msg_type)
|
|
||||||
}
|
|
||||||
"audio" => (DcMimeType::Audio, Viewtype::Audio),
|
|
||||||
"video" => (DcMimeType::Video, Viewtype::Video),
|
|
||||||
"multipart" => {
|
|
||||||
let mime_type = match subtype {
|
|
||||||
"alternative" => DcMimeType::MpAlternative,
|
|
||||||
"related" => DcMimeType::MpRelated,
|
|
||||||
"encrypted" => {
|
|
||||||
// maybe try_decrypt failed to decrypt
|
|
||||||
// or it wasn't in proper Autocrypt format
|
|
||||||
DcMimeType::MpNotDecryptable
|
|
||||||
}
|
|
||||||
"signed" => DcMimeType::MpSigned,
|
|
||||||
"mixed" => DcMimeType::MpMixed,
|
|
||||||
"report" => DcMimeType::MpReport,
|
|
||||||
_ => DcMimeType::MpOther,
|
|
||||||
};
|
|
||||||
|
|
||||||
(mime_type, Viewtype::Unknown)
|
|
||||||
}
|
|
||||||
"message" => {
|
|
||||||
// Enacapsulated messages, see https://www.w3.org/Protocols/rfc1341/7_3_Message.html
|
// Enacapsulated messages, see https://www.w3.org/Protocols/rfc1341/7_3_Message.html
|
||||||
// Also used as part "message/disposition-notification" of "multipart/report", which, however, will
|
// Also used as part "message/disposition-notification" of "multipart/report", which, however, will
|
||||||
// be handled separatedly.
|
// be handled separatedly.
|
||||||
@@ -1038,17 +981,13 @@ fn mailmime_get_mime_type(mail: &mailparse::ParsedMail<'_>) -> (DcMimeType, View
|
|||||||
// which are unwanted at all).
|
// which are unwanted at all).
|
||||||
// For now, we skip these parts at all; if desired, we could return DcMimeType::File/DC_MSG_File
|
// For now, we skip these parts at all; if desired, we could return DcMimeType::File/DC_MSG_File
|
||||||
// for selected and known subparts.
|
// for selected and known subparts.
|
||||||
unknown_type
|
Viewtype::Unknown
|
||||||
}
|
}
|
||||||
"application" => {
|
mime::APPLICATION => Viewtype::File,
|
||||||
if subtype == "autocrypt-setup" {
|
_ => Viewtype::Unknown,
|
||||||
return (DcMimeType::AcSetupFile, Viewtype::File);
|
};
|
||||||
}
|
|
||||||
|
|
||||||
(DcMimeType::File, Viewtype::File)
|
Ok((mimetype, viewtype))
|
||||||
}
|
|
||||||
_ => unknown_type,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mailmime_is_attachment_disposition(mail: &mailparse::ParsedMail<'_>) -> bool {
|
fn mailmime_is_attachment_disposition(mail: &mailparse::ParsedMail<'_>) -> bool {
|
||||||
@@ -1127,10 +1066,9 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_dc_mailmime_parse_crash_fuzzy(data in "[!-~\t ]{2000,}") {
|
fn test_dc_mailmime_parse_crash_fuzzy(data in "[!-~\t ]{2000,}") {
|
||||||
let context = dummy_context();
|
let context = dummy_context();
|
||||||
// parsing should error out for all these random strings
|
|
||||||
assert!(
|
// just don't crash
|
||||||
MimeParser::from_bytes(&context.ctx, data.as_bytes()).is_err()
|
let _ = MimeParser::from_bytes(&context.ctx, data.as_bytes());
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user