mirror of
https://github.com/chatmail/core.git
synced 2026-05-08 01:16:31 +03:00
streamline mimetype guessing and build_body_file
This commit is contained in:
@@ -118,17 +118,14 @@ fn dc_poke_eml_file(context: &Context, filename: impl AsRef<Path>) -> Result<(),
|
|||||||
/// @param context The context as created by dc_context_new().
|
/// @param context The context as created by dc_context_new().
|
||||||
/// @param spec The file or directory to import. NULL for the last command.
|
/// @param spec The file or directory to import. NULL for the last command.
|
||||||
/// @return 1=success, 0=error.
|
/// @return 1=success, 0=error.
|
||||||
unsafe fn poke_spec(context: &Context, spec: *const libc::c_char) -> libc::c_int {
|
fn poke_spec(context: &Context, spec: *const libc::c_char) -> libc::c_int {
|
||||||
if !context.sql.is_open() {
|
if !context.sql.is_open() {
|
||||||
error!(context, "Import: Database not opened.");
|
error!(context, "Import: Database not opened.");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
let ok_to_continue;
|
|
||||||
let mut success: libc::c_int = 0;
|
|
||||||
let real_spec: String;
|
let real_spec: String;
|
||||||
let mut suffix: *mut libc::c_char = ptr::null_mut();
|
let mut read_cnt = 0;
|
||||||
let mut read_cnt: libc::c_int = 0;
|
|
||||||
|
|
||||||
/* if `spec` is given, remember it for later usage; if it is not given, try to use the last one */
|
/* if `spec` is given, remember it for later usage; if it is not given, try to use the last one */
|
||||||
if !spec.is_null() {
|
if !spec.is_null() {
|
||||||
@@ -137,34 +134,27 @@ unsafe fn poke_spec(context: &Context, spec: *const libc::c_char) -> libc::c_int
|
|||||||
.sql
|
.sql
|
||||||
.set_config(context, "import_spec", Some(&real_spec))
|
.set_config(context, "import_spec", Some(&real_spec))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
ok_to_continue = true;
|
|
||||||
} else {
|
} else {
|
||||||
let rs = context.sql.get_config(context, "import_spec");
|
let rs = context.sql.get_config(context, "import_spec");
|
||||||
if rs.is_none() {
|
if rs.is_none() {
|
||||||
error!(context, "Import: No file or folder given.");
|
error!(context, "Import: No file or folder given.");
|
||||||
ok_to_continue = false;
|
return 0;
|
||||||
} else {
|
|
||||||
ok_to_continue = true;
|
|
||||||
}
|
}
|
||||||
real_spec = rs.unwrap_or_default();
|
real_spec = rs.unwrap();
|
||||||
}
|
}
|
||||||
if ok_to_continue {
|
if let Some(suffix) = dc_get_filesuffix_lc(&real_spec) {
|
||||||
let ok_to_continue2;
|
if suffix == "eml" {
|
||||||
suffix = dc_get_filesuffix_lc(&real_spec);
|
|
||||||
if !suffix.is_null()
|
|
||||||
&& libc::strcmp(suffix, b"eml\x00" as *const u8 as *const libc::c_char) == 0
|
|
||||||
{
|
|
||||||
if dc_poke_eml_file(context, &real_spec).is_ok() {
|
if dc_poke_eml_file(context, &real_spec).is_ok() {
|
||||||
read_cnt += 1
|
read_cnt += 1
|
||||||
}
|
}
|
||||||
ok_to_continue2 = true;
|
}
|
||||||
} else {
|
} else {
|
||||||
/* import a directory */
|
/* import a directory */
|
||||||
let dir_name = std::path::Path::new(&real_spec);
|
let dir_name = std::path::Path::new(&real_spec);
|
||||||
let dir = std::fs::read_dir(dir_name);
|
let dir = std::fs::read_dir(dir_name);
|
||||||
if dir.is_err() {
|
if dir.is_err() {
|
||||||
error!(context, "Import: Cannot open directory \"{}\".", &real_spec,);
|
error!(context, "Import: Cannot open directory \"{}\".", &real_spec,);
|
||||||
ok_to_continue2 = false;
|
return 0;
|
||||||
} else {
|
} else {
|
||||||
let dir = dir.unwrap();
|
let dir = dir.unwrap();
|
||||||
for entry in dir {
|
for entry in dir {
|
||||||
@@ -182,10 +172,8 @@ unsafe fn poke_spec(context: &Context, spec: *const libc::c_char) -> libc::c_int
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ok_to_continue2 = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ok_to_continue2 {
|
|
||||||
info!(
|
info!(
|
||||||
context,
|
context,
|
||||||
"Import: {} items read from \"{}\".", read_cnt, &real_spec
|
"Import: {} items read from \"{}\".", read_cnt, &real_spec
|
||||||
@@ -196,12 +184,7 @@ unsafe fn poke_spec(context: &Context, spec: *const libc::c_char) -> libc::c_int
|
|||||||
msg_id: 0,
|
msg_id: 0,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
success = 1
|
1
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
free(suffix as *mut libc::c_void);
|
|
||||||
success
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn log_msg(context: &Context, prefix: impl AsRef<str>, msg: &Message) {
|
unsafe fn log_msg(context: &Context, prefix: impl AsRef<str>, msg: &Message) {
|
||||||
|
|||||||
@@ -843,7 +843,6 @@ unsafe fn import_self_keys(context: &Context, dir_name: *const libc::c_char) ->
|
|||||||
Maybe we should make the "default" key handlong also a little bit smarter
|
Maybe we should make the "default" key handlong also a little bit smarter
|
||||||
(currently, the last imported key is the standard key unless it contains the string "legacy" in its name) */
|
(currently, the last imported key is the standard key unless it contains the string "legacy" in its name) */
|
||||||
let mut imported_cnt: libc::c_int = 0;
|
let mut imported_cnt: libc::c_int = 0;
|
||||||
let mut suffix: *mut libc::c_char = ptr::null_mut();
|
|
||||||
let mut set_default: libc::c_int;
|
let mut set_default: libc::c_int;
|
||||||
let mut buf: *mut libc::c_char = ptr::null_mut();
|
let mut buf: *mut libc::c_char = ptr::null_mut();
|
||||||
// a pointer inside buf, MUST NOT be free()'d
|
// a pointer inside buf, MUST NOT be free()'d
|
||||||
@@ -859,15 +858,19 @@ unsafe fn import_self_keys(context: &Context, dir_name: *const libc::c_char) ->
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
let entry = entry.unwrap();
|
let entry = entry.unwrap();
|
||||||
free(suffix as *mut libc::c_void);
|
|
||||||
let name_f = entry.file_name();
|
let name_f = entry.file_name();
|
||||||
let name_c = name_f.to_c_string().unwrap();
|
let name_c = name_f.to_c_string().unwrap();
|
||||||
suffix = dc_get_filesuffix_lc(name_f.to_string_lossy());
|
|
||||||
if suffix.is_null()
|
match dc_get_filesuffix_lc(name_f.to_string_lossy()) {
|
||||||
|| strcmp(suffix, b"asc\x00" as *const u8 as *const libc::c_char) != 0
|
Some(suffix) => {
|
||||||
{
|
if suffix != ".asc" {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
let path_plus_name = dir.join(entry.file_name());
|
let path_plus_name = dir.join(entry.file_name());
|
||||||
info!(context, "Checking: {}", path_plus_name.display());
|
info!(context, "Checking: {}", path_plus_name.display());
|
||||||
|
|
||||||
@@ -940,7 +943,6 @@ unsafe fn import_self_keys(context: &Context, dir_name: *const libc::c_char) ->
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
free(suffix as *mut libc::c_void);
|
|
||||||
free(buf as *mut libc::c_void);
|
free(buf as *mut libc::c_void);
|
||||||
free(buf2 as *mut libc::c_void);
|
free(buf2 as *mut libc::c_void);
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use std::path::Path;
|
|||||||
use std::ptr;
|
use std::ptr;
|
||||||
|
|
||||||
use chrono::TimeZone;
|
use chrono::TimeZone;
|
||||||
use libc::{free, strcmp};
|
use libc::free;
|
||||||
use mmime::clist::*;
|
use mmime::clist::*;
|
||||||
use mmime::mailimf_types::*;
|
use mmime::mailimf_types::*;
|
||||||
use mmime::mailimf_types_helper::*;
|
use mmime::mailimf_types_helper::*;
|
||||||
@@ -904,96 +904,58 @@ unsafe fn set_body_text(part: *mut mailmime, text: &str) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
unsafe fn build_body_file(
|
fn build_body_file(context: &Context, msg: &Message, base_name: &str) -> (*mut mailmime, String) {
|
||||||
context: &Context,
|
let path_filename = match msg.param.get(Param::File) {
|
||||||
msg: &Message,
|
None => {
|
||||||
base_name: &str,
|
return (ptr::null_mut(), "".to_string());
|
||||||
) -> (*mut mailmime, String) {
|
}
|
||||||
let needs_ext: bool;
|
Some(path) => path,
|
||||||
let mime_fields: *mut mailmime_fields;
|
|
||||||
let mut mime_sub: *mut mailmime = ptr::null_mut();
|
|
||||||
let content: *mut mailmime_content;
|
|
||||||
let path_filename = msg.param.get(Param::File);
|
|
||||||
let mut filename_to_send = "".to_string();
|
|
||||||
|
|
||||||
let mut mimetype = msg
|
|
||||||
.param
|
|
||||||
.get(Param::MimeType)
|
|
||||||
.map(|s| s.strdup())
|
|
||||||
.unwrap_or_else(|| std::ptr::null_mut());
|
|
||||||
|
|
||||||
let mut filename_encoded = ptr::null_mut();
|
|
||||||
|
|
||||||
if let Some(ref path_filename) = path_filename {
|
|
||||||
let suffix = dc_get_filesuffix_lc(path_filename);
|
|
||||||
|
|
||||||
filename_to_send = if msg.type_0 == Viewtype::Voice {
|
|
||||||
let ts = chrono::Utc.timestamp(msg.timestamp_sort as i64, 0);
|
|
||||||
|
|
||||||
let suffix = if !suffix.is_null() {
|
|
||||||
to_string(suffix)
|
|
||||||
} else {
|
|
||||||
"dat".into()
|
|
||||||
};
|
};
|
||||||
ts.format(&format!("voice-message_%Y-%m-%d_%H-%M-%S.{}", suffix))
|
let suffix = dc_get_filesuffix_lc(path_filename).unwrap_or_else(|| "dat".into());
|
||||||
.to_string()
|
let filename_to_send = match msg.type_0 {
|
||||||
} else if msg.type_0 == Viewtype::Audio {
|
Viewtype::Voice => chrono::Utc
|
||||||
Path::new(path_filename)
|
.timestamp(msg.timestamp_sort as i64, 0)
|
||||||
|
.format(&format!("voice-message_%Y-%m-%d_%H-%M-%S.{}", suffix))
|
||||||
|
.to_string(),
|
||||||
|
Viewtype::Audio => Path::new(path_filename)
|
||||||
.file_name()
|
.file_name()
|
||||||
.map(|c| c.to_string_lossy().to_string())
|
.map(|c| c.to_string_lossy().to_string())
|
||||||
.unwrap_or_default()
|
.unwrap_or_default(),
|
||||||
} else if msg.type_0 == Viewtype::Image || msg.type_0 == Viewtype::Gif {
|
Viewtype::Image | Viewtype::Gif => format!(
|
||||||
format!(
|
|
||||||
"{}.{}",
|
"{}.{}",
|
||||||
if base_name.is_empty() {
|
if base_name.is_empty() {
|
||||||
"image"
|
"image"
|
||||||
} else {
|
} else {
|
||||||
base_name
|
base_name
|
||||||
},
|
},
|
||||||
if !suffix.is_null() {
|
&suffix,
|
||||||
as_str(suffix)
|
),
|
||||||
} else {
|
Viewtype::Video => format!("video.{}", &suffix),
|
||||||
"dat"
|
_ => Path::new(path_filename)
|
||||||
},
|
|
||||||
)
|
|
||||||
} else if msg.type_0 == Viewtype::Video {
|
|
||||||
format!(
|
|
||||||
"video.{}",
|
|
||||||
if !suffix.is_null() {
|
|
||||||
as_str(suffix)
|
|
||||||
} else {
|
|
||||||
"dat"
|
|
||||||
},
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
Path::new(path_filename)
|
|
||||||
.file_name()
|
.file_name()
|
||||||
.map(|c| c.to_string_lossy().to_string())
|
.map(|c| c.to_string_lossy().to_string())
|
||||||
.unwrap_or_default()
|
.unwrap_or_default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
if mimetype.is_null() {
|
let mimetype = match msg.param.get(Param::MimeType) {
|
||||||
if suffix.is_null() {
|
Some(mtype) => mtype,
|
||||||
mimetype = "application/octet-stream".strdup();
|
None => {
|
||||||
} else if strcmp(suffix, b"png\x00" as *const u8 as *const libc::c_char) == 0 {
|
let path = Path::new(path_filename);
|
||||||
mimetype = "image/png".strdup();
|
if let Some(res) = message::guess_msgtype_from_suffix(&path) {
|
||||||
} else if strcmp(suffix, b"jpg\x00" as *const u8 as *const libc::c_char) == 0
|
res.1
|
||||||
|| strcmp(suffix, b"jpeg\x00" as *const u8 as *const libc::c_char) == 0
|
|
||||||
|| strcmp(suffix, b"jpe\x00" as *const u8 as *const libc::c_char) == 0
|
|
||||||
{
|
|
||||||
mimetype = "image/jpeg".strdup();
|
|
||||||
} else if strcmp(suffix, b"gif\x00" as *const u8 as *const libc::c_char) == 0 {
|
|
||||||
mimetype = "image/gif".strdup();
|
|
||||||
} else {
|
} else {
|
||||||
mimetype = "application/octet-stream".strdup();
|
"application/octet-stream"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !mimetype.is_null() {
|
};
|
||||||
|
|
||||||
|
let needs_ext = dc_needs_ext_header(&filename_to_send);
|
||||||
|
|
||||||
|
unsafe {
|
||||||
/* create mime part, for Content-Disposition, see RFC 2183.
|
/* create mime part, for Content-Disposition, see RFC 2183.
|
||||||
`Content-Disposition: attachment` seems not to make a difference to `Content-Disposition: inline` at least on tested Thunderbird and Gma'l in 2017.
|
`Content-Disposition: attachment` seems not to make a difference to `Content-Disposition: inline` at least on tested Thunderbird and Gma'l in 2017.
|
||||||
But I've heard about problems with inline and outl'k, so we just use the attachment-type until we run into other problems ... */
|
But I've heard about problems with inline and outl'k, so we just use the attachment-type until we run into other problems ... */
|
||||||
needs_ext = dc_needs_ext_header(&filename_to_send);
|
let mime_fields = mailmime_fields_new_filename(
|
||||||
mime_fields = mailmime_fields_new_filename(
|
|
||||||
MAILMIME_DISPOSITION_TYPE_ATTACHMENT as libc::c_int,
|
MAILMIME_DISPOSITION_TYPE_ATTACHMENT as libc::c_int,
|
||||||
if needs_ext {
|
if needs_ext {
|
||||||
ptr::null_mut()
|
ptr::null_mut()
|
||||||
@@ -1034,8 +996,8 @@ unsafe fn build_body_file(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
content = mailmime_content_new_with_str(mimetype);
|
let content = mailmime_content_new_with_str(mimetype.strdup());
|
||||||
filename_encoded = dc_encode_header_words(&filename_to_send);
|
let filename_encoded = dc_encode_header_words(&filename_to_send);
|
||||||
clist_insert_after(
|
clist_insert_after(
|
||||||
(*content).ct_parameters,
|
(*content).ct_parameters,
|
||||||
(*(*content).ct_parameters).last,
|
(*(*content).ct_parameters).last,
|
||||||
@@ -1044,24 +1006,20 @@ unsafe fn build_body_file(
|
|||||||
filename_encoded,
|
filename_encoded,
|
||||||
) as *mut libc::c_void,
|
) as *mut libc::c_void,
|
||||||
);
|
);
|
||||||
mime_sub = mailmime_new_empty(content, mime_fields);
|
free(filename_encoded as *mut libc::c_void);
|
||||||
|
let mime_sub = mailmime_new_empty(content, mime_fields);
|
||||||
let abs_path = dc_get_abs_path(context, path_filename)
|
let abs_path = dc_get_abs_path(context, path_filename)
|
||||||
.to_c_string()
|
.to_c_string()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
mailmime_set_body_file(mime_sub, dc_strdup(abs_path.as_ptr()));
|
mailmime_set_body_file(mime_sub, dc_strdup(abs_path.as_ptr()));
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
free(mimetype as *mut libc::c_void);
|
|
||||||
free(filename_encoded as *mut libc::c_void);
|
|
||||||
|
|
||||||
(mime_sub, filename_to_send)
|
(mime_sub, filename_to_send)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* Render
|
* Render
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
unsafe fn is_file_size_okay(context: &Context, msg: &Message) -> bool {
|
fn is_file_size_okay(context: &Context, msg: &Message) -> bool {
|
||||||
let mut file_size_okay = true;
|
let mut file_size_okay = true;
|
||||||
let path = msg.param.get(Param::File).unwrap_or_default();
|
let path = msg.param.get(Param::File).unwrap_or_default();
|
||||||
let bytes = dc_get_filebytes(context, &path);
|
let bytes = dc_get_filebytes(context, &path);
|
||||||
|
|||||||
@@ -548,11 +548,11 @@ fn validate_filename(filename: &str) -> String {
|
|||||||
|
|
||||||
// the returned suffix is lower-case
|
// the returned suffix is lower-case
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
pub unsafe fn dc_get_filesuffix_lc(path_filename: impl AsRef<str>) -> *mut libc::c_char {
|
pub fn dc_get_filesuffix_lc(path_filename: impl AsRef<str>) -> Option<String> {
|
||||||
if let Some(p) = Path::new(path_filename.as_ref()).extension() {
|
if let Some(p) = Path::new(path_filename.as_ref()).extension() {
|
||||||
p.to_string_lossy().to_lowercase().strdup()
|
Some(p.to_string_lossy().to_lowercase())
|
||||||
} else {
|
} else {
|
||||||
ptr::null_mut()
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -680,6 +680,7 @@ pub fn guess_msgtype_from_suffix(path: &Path) -> Option<(Viewtype, &str)> {
|
|||||||
"mp4" => (Viewtype::Video, "video/mp4"),
|
"mp4" => (Viewtype::Video, "video/mp4"),
|
||||||
"jpg" => (Viewtype::Image, "image/jpeg"),
|
"jpg" => (Viewtype::Image, "image/jpeg"),
|
||||||
"jpeg" => (Viewtype::Image, "image/jpeg"),
|
"jpeg" => (Viewtype::Image, "image/jpeg"),
|
||||||
|
"jpe" => (Viewtype::Image, "image/jpeg"),
|
||||||
"png" => (Viewtype::Image, "image/png"),
|
"png" => (Viewtype::Image, "image/png"),
|
||||||
"webp" => (Viewtype::Image, "image/webp"),
|
"webp" => (Viewtype::Image, "image/webp"),
|
||||||
"gif" => (Viewtype::Gif, "image/gif"),
|
"gif" => (Viewtype::Gif, "image/gif"),
|
||||||
@@ -687,7 +688,6 @@ pub fn guess_msgtype_from_suffix(path: &Path) -> Option<(Viewtype, &str)> {
|
|||||||
"vcard" => (Viewtype::File, "text/vcard"),
|
"vcard" => (Viewtype::File, "text/vcard"),
|
||||||
};
|
};
|
||||||
let extension: &str = &path.extension()?.to_str()?.to_lowercase();
|
let extension: &str = &path.extension()?.to_str()?.to_lowercase();
|
||||||
|
|
||||||
KNOWN.get(extension).map(|x| *x)
|
KNOWN.get(extension).map(|x| *x)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user