From 83bee279e65f9997dd30a2a04130adc052a83005 Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Wed, 16 Sep 2020 19:57:06 +0300 Subject: [PATCH] melib/email/compose: set attachment status Set Content-Disposition: attachment to, well, attachments. --- Cargo.lock | 2 +- Cargo.toml | 1 - melib/Cargo.toml | 1 + melib/src/email/compose.rs | 62 ++++++++++++------- melib/src/lib.rs | 1 + src/bin.rs | 2 +- src/components/mail/compose.rs | 21 +------ tests/{generated.mail => generated_email.eml} | 35 ++++++----- tests/generating_email.rs | 13 +--- 9 files changed, 65 insertions(+), 73 deletions(-) rename tests/{generated.mail => generated_email.eml} (96%) diff --git a/Cargo.lock b/Cargo.lock index 421fcd30..1ee1122b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -972,7 +972,6 @@ dependencies = [ "unicode-segmentation", "uuid", "xdg", - "xdg-utils", ] [[package]] @@ -1006,6 +1005,7 @@ dependencies = [ "unicode-segmentation", "uuid", "xdg", + "xdg-utils", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 89ead05b..2b4e4e39 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,7 +39,6 @@ serde_json = "1.0" toml = { version = "0.5.6", features = ["preserve_order", ] } indexmap = { version = "^1.5", features = ["serde-1", ] } linkify = "0.4.0" -xdg-utils = "0.3.0" notify = "4.0.1" # >:c notify-rust = { version = "^4", optional = true } termion = "1.5.1" diff --git a/melib/Cargo.toml b/melib/Cargo.toml index f682e8c8..9ca54ff2 100644 --- a/melib/Cargo.toml +++ b/melib/Cargo.toml @@ -48,6 +48,7 @@ smol = "0.1.18" async-stream = "0.2.1" base64 = { version = "0.12.3", optional = true } flate2 = { version = "1.0.16", optional = true } +xdg-utils = "0.3.0" [features] default = ["unicode_algorithms", "imap_backend", "maildir_backend", "mbox_backend", "vcard", "sqlite3", "smtp", "deflate_compression"] diff --git a/melib/src/email/compose.rs b/melib/src/email/compose.rs index 921449f4..59c5eacd 100644 --- a/melib/src/email/compose.rs +++ b/melib/src/email/compose.rs @@ -30,6 +30,7 @@ use std::ffi::OsStr; use std::io::Read; use std::path::{Path, PathBuf}; use std::str; +use xdg_utils::query_mime_info; pub mod mime; pub mod random; @@ -244,21 +245,25 @@ impl Draft { } for (k, v) in self.headers.deref() { if v.is_ascii() { - ret.extend(format!("{}: {}\n", k, v).chars()); + ret.extend(format!("{}: {}\r\n", k, v).chars()); } else { - ret.extend(format!("{}: {}\n", k, mime::encode_header(v)).chars()); + ret.extend(format!("{}: {}\r\n", k, mime::encode_header(v)).chars()); } } - ret.push_str("MIME-Version: 1.0\n"); + ret.push_str("MIME-Version: 1.0\r\n"); if self.attachments.is_empty() { let content_type: ContentType = Default::default(); let content_transfer_encoding: ContentTransferEncoding = ContentTransferEncoding::_8Bit; - ret.extend(format!("Content-Type: {}; charset=\"utf-8\"\n", content_type).chars()); + ret.extend(format!("Content-Type: {}; charset=\"utf-8\"\r\n", content_type).chars()); ret.extend( - format!("Content-Transfer-Encoding: {}\n", content_transfer_encoding).chars(), + format!( + "Content-Transfer-Encoding: {}\r\n", + content_transfer_encoding + ) + .chars(), ); - ret.push('\n'); + ret.push_str("\r\n"); ret.push_str(&self.body); } else if self.body.is_empty() && self.attachments.len() == 1 { let attachment = std::mem::replace(&mut self.attachments, Vec::new()).remove(0); @@ -283,18 +288,18 @@ fn build_multipart(ret: &mut String, kind: MultipartType, parts: Vec { - ret.push('\n'); + ret.push_str("\r\n"); ret.push_str(&String::from_utf8_lossy(a.raw())); - ret.push('\n'); + ret.push_str("\r\n"); } Text { .. } => { ret.push_str(&a.build().into_raw()); - ret.push('\n'); + ret.push_str("\r\n"); } Multipart { boundary: _boundary, @@ -331,13 +336,14 @@ fn print_attachment(ret: &mut String, kind: &MultipartType, a: AttachmentBuilder .map(|s| s.into()) .collect::>(), ); - ret.push('\n'); + ret.push_str("\r\n"); } MessageRfc822 | PGPSignature => { - ret.push_str(&format!("Content-Type: {}; charset=\"utf-8\"\n", kind)); - ret.push('\n'); + ret.push_str(&format!("Content-Type: {}; charset=\"utf-8\"\r\n", kind)); + ret.push_str("Content-Disposition: attachment\r\n"); + ret.push_str("\r\n"); ret.push_str(&String::from_utf8_lossy(a.raw())); - ret.push('\n'); + ret.push_str("\r\n"); } _ => { let content_transfer_encoding: ContentTransferEncoding = @@ -345,7 +351,7 @@ fn print_attachment(ret: &mut String, kind: &MultipartType, a: AttachmentBuilder if let Some(name) = a.content_type().name() { ret.extend( format!( - "Content-Type: {}; name=\"{}\"; charset=\"utf-8\"\n", + "Content-Type: {}; name=\"{}\"; charset=\"utf-8\"\r\n", a.content_type(), name ) @@ -353,15 +359,20 @@ fn print_attachment(ret: &mut String, kind: &MultipartType, a: AttachmentBuilder ); } else { ret.extend( - format!("Content-Type: {}; charset=\"utf-8\"\n", a.content_type()).chars(), + format!("Content-Type: {}; charset=\"utf-8\"\r\n", a.content_type()).chars(), ); } + ret.push_str("Content-Disposition: attachment\r\n"); ret.extend( - format!("Content-Transfer-Encoding: {}\n", content_transfer_encoding).chars(), + format!( + "Content-Transfer-Encoding: {}\r\n", + content_transfer_encoding + ) + .chars(), ); - ret.push('\n'); + ret.push_str("\r\n"); ret.push_str(&BASE64_MIME.encode(a.raw()).trim()); - ret.push('\n'); + ret.push_str("\r\n"); } } } @@ -428,12 +439,17 @@ where let mut contents = Vec::new(); file.read_to_end(&mut contents)?; let mut attachment = AttachmentBuilder::default(); + attachment .set_raw(contents) .set_body_to_raw() .set_content_type(ContentType::Other { name: path.file_name().map(|s| s.to_string_lossy().into()), - tag: b"application/octet-stream".to_vec(), + tag: if let Ok(mime_type) = query_mime_info(&path) { + mime_type + } else { + b"application/octet-stream".to_vec() + }, }); Ok(attachment) diff --git a/melib/src/lib.rs b/melib/src/lib.rs index dafe33e9..2dfc66bf 100644 --- a/melib/src/lib.rs +++ b/melib/src/lib.rs @@ -138,6 +138,7 @@ pub extern crate indexmap; pub extern crate smallvec; pub extern crate smol; pub extern crate uuid; +pub extern crate xdg_utils; pub use shellexpand::ShellExpandTrait; pub mod shellexpand { diff --git a/src/bin.rs b/src/bin.rs index d6655d5f..c879094b 100644 --- a/src/bin.rs +++ b/src/bin.rs @@ -30,7 +30,7 @@ use std::alloc::System; use std::collections::VecDeque; use std::path::PathBuf; -extern crate xdg_utils; + #[macro_use] extern crate serde_derive; extern crate linkify; diff --git a/src/components/mail/compose.rs b/src/components/mail/compose.rs index 4ea4747e..f3a94e38 100644 --- a/src/components/mail/compose.rs +++ b/src/components/mail/compose.rs @@ -32,7 +32,6 @@ use nix::sys::wait::WaitStatus; use std::convert::TryInto; use std::str::FromStr; use std::sync::{Arc, Mutex}; -use xdg_utils::query_mime_info; #[derive(Debug, PartialEq)] enum Cursor { @@ -1243,7 +1242,7 @@ impl Component for Composer { .wait_with_output() .expect("failed to launch command") .stdout; - let mut attachment = + let attachment = match melib::email::compose::attachment_from_file(f.path()) { Ok(a) => a, Err(err) => { @@ -1258,14 +1257,6 @@ impl Component for Composer { return true; } }; - if let Ok(mime_type) = query_mime_info(f.path()) { - match attachment.content_type { - ContentType::Other { ref mut tag, .. } => { - *tag = mime_type; - } - _ => {} - } - } self.draft.attachments_mut().push(attachment); self.dirty = true; return true; @@ -1281,7 +1272,7 @@ impl Component for Composer { } } Action::Compose(ComposeAction::AddAttachment(ref path)) => { - let mut attachment = match melib::email::compose::attachment_from_file(path) { + let attachment = match melib::email::compose::attachment_from_file(path) { Ok(a) => a, Err(err) => { context.replies.push_back(UIEvent::Notification( @@ -1293,14 +1284,6 @@ impl Component for Composer { return true; } }; - if let Ok(mime_type) = query_mime_info(path) { - match attachment.content_type { - ContentType::Other { ref mut tag, .. } => { - *tag = mime_type; - } - _ => {} - } - } self.draft.attachments_mut().push(attachment); self.dirty = true; return true; diff --git a/tests/generated.mail b/tests/generated_email.eml similarity index 96% rename from tests/generated.mail rename to tests/generated_email.eml index 20f23ff0..357d754b 100644 --- a/tests/generated.mail +++ b/tests/generated_email.eml @@ -1,19 +1,20 @@ -Subject: -From: -To: -Cc: -Bcc: -MIME-Version: 1.0 -Content-Type: multipart/mixed; charset="utf-8"; boundary="bzz_bzz__bzz__" - -This is a MIME formatted message with attachments. Use a MIME-compliant client to view it properly. ---bzz_bzz__bzz__ - -hello world. ---bzz_bzz__bzz__ -Content-Type: image/gif; name="test_image.gif"; charset="utf-8" -Content-Transfer-Encoding: base64 - +Subject: +From: +To: +Cc: +Bcc: +MIME-Version: 1.0 +Content-Type: multipart/mixed; charset="utf-8"; boundary="bzz_bzz__bzz__" + +This is a MIME formatted message with attachments. Use a MIME-compliant client to view it properly. +--bzz_bzz__bzz__ + +hello world. +--bzz_bzz__bzz__ +Content-Type: image/gif; name="test_image.gif"; charset="utf-8" +Content-Disposition: attachment +Content-Transfer-Encoding: base64 + R0lGODdhKAAXAOfZAAABzAADzQAEzgQFtBEAxAAGxBcAxwALvRcFwAAPwBcLugATuQEUuxoNuxYQ sxwOvAYVvBsStSAVtx8YsRUcuhwhth4iuCQsyDAwuDc1vTc3uDg4uT85rkc9ukJBvENCvURGukdF wUVKt0hLuUxPvVZSvFlYu1hbt2BZuFxdul5joGhqlnNuf3FvlnBvwXJyt3Jxw3N0oXx1gH12gV99 @@ -36,5 +37,5 @@ Ob+jG0YVRBErUrOiiGJ8KxgtYsh27xWL/tswnTtEbsiRVYdJNMHk4yOGhswGjR88UKjQ9Ey+/8TL XKKGGn7Akph/8XX2WDTTcAYfguVt9hhrEPqmzIOJ3VUheb48WJiHG6amC4i+WVJKKCimqGIoYxyj WWK8kKjaJ9bA18sxvXjYhourmbbMMrjI+OIn1QymDCVXANGFK4S1gQw0PxozzC+33FLLKUJq9gk1 gyWDhyNwrMLkYGUEM4wvuLRiCiieXIJJJVlmJskcZ9TZRht1lnFGGmTMkMoonVQSSSOFAGJHHI0w -ouiijDaaCCGQRgrpH3q4QYYXWDihxBE+7KCDDjnUIEVAADs= +ouiijDaaCCGQRgrpH3q4QYYXWDihxBE+7KCDDjnUIEVAADs= --bzz_bzz__bzz__-- diff --git a/tests/generating_email.rs b/tests/generating_email.rs index e44a2d0c..4e211d93 100644 --- a/tests/generating_email.rs +++ b/tests/generating_email.rs @@ -1,20 +1,11 @@ use melib; use melib::email::Draft; -use xdg_utils::query_mime_info; #[test] fn build_draft() { let mut new_draft = Draft::default(); - let mut attachment = melib::email::attachment_from_file(&"./tests/test_image.gif") + let attachment = melib::email::attachment_from_file(&"./tests/test_image.gif") .expect("Could not open test_image.gif."); - if let Ok(mime_type) = query_mime_info("./tests/test_image.gif") { - match attachment.content_type { - melib::email::attachment_types::ContentType::Other { ref mut tag, .. } => { - *tag = mime_type; - } - _ => {} - } - } new_draft.headers_mut().remove("User-Agent"); new_draft.headers_mut().remove("Date"); @@ -27,5 +18,5 @@ fn build_draft() { let boundary_str = &boundary["bzz_bzz__bzz__".len()..]; let raw = raw.replace(boundary_str, ""); - assert_eq!(include_str!("generated.mail"), &raw); + assert_eq!(include_str!("generated_email.eml"), &raw); }