Browse Source

melib/email/compose: set attachment status

Set Content-Disposition: attachment to, well, attachments.
tags/alpha-0.6.2
Manos Pitsidianakis 1 month ago
parent
commit
83bee279e6
WARNING! Although there is a key with this ID in the database it does not verify this commit! This commit is SUSPICIOUS. GPG Key ID: 73627C2F690DF710
9 changed files with 65 additions and 73 deletions
  1. +1
    -1
      Cargo.lock
  2. +0
    -1
      Cargo.toml
  3. +1
    -0
      melib/Cargo.toml
  4. +39
    -23
      melib/src/email/compose.rs
  5. +1
    -0
      melib/src/lib.rs
  6. +1
    -1
      src/bin.rs
  7. +2
    -19
      src/components/mail/compose.rs
  8. +18
    -17
      tests/generated_email.eml
  9. +2
    -11
      tests/generating_email.rs

+ 1
- 1
Cargo.lock View File

@ -972,7 +972,6 @@ dependencies = [
"unicode-segmentation",
"uuid",
"xdg",
"xdg-utils",
]
[[package]]
@ -1006,6 +1005,7 @@ dependencies = [
"unicode-segmentation",
"uuid",
"xdg",
"xdg-utils",
]
[[package]]

+ 0
- 1
Cargo.toml View File

@ -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"

+ 1
- 0
melib/Cargo.toml View File

@ -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"]

+ 39
- 23
melib/src/email/compose.rs View File

@ -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
let boundary = ContentType::make_boundary(&parts);
ret.extend(
format!(
"Content-Type: {}; charset=\"utf-8\"; boundary=\"{}\"\n",
"Content-Type: {}; charset=\"utf-8\"; boundary=\"{}\"\r\n",
kind, boundary
)
.chars(),
);
ret.push('\n';);
ret.push_str("\r\n";);
/* rfc1341 */
ret.push_str("This is a MIME formatted message with attachments. Use a MIME-compliant client to view it properly.\n");
ret.push_str("This is a MIME formatted message with attachments. Use a MIME-compliant client to view it properly.\r\n");
for sub in parts {
ret.push_str("--");
ret.push_str(&boundary);
ret.push('\n';);
ret.push_str("\r\n";);
print_attachment(ret, &kind, sub);
}
ret.push_str("--");
@ -310,13 +315,13 @@ fn print_attachment(ret: &mut String, kind: &MultipartType, a: AttachmentBuilder
charset: Charset::UTF8,
parameters: ref v,
} if v.is_empty() => {
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::<Vec<AttachmentBuilder>>(),
);
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)

+ 1
- 0
melib/src/lib.rs View File

@ -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 {

+ 1
- 1
src/bin.rs View File

@ -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;

+ 2
- 19
src/components/mail/compose.rs View File

@ -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;

tests/generated.mail → tests/generated_email.eml View File

@ -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__--

+ 2
- 11
tests/generating_email.rs View File

@ -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);
}

Loading…
Cancel
Save