use mail_parser::{MessageParser, MessagePart};
use std::{
env, fs, io,
path::{Path, PathBuf},
};
const MESSAGE: &[u8] = br#"From: Art Vandelay <art@vandelay.com> (Vandelay Industries)
To: "Colleagues": "James Smythe" <james@vandelay.com>; Friends:
jane@example.com, =?UTF-8?Q?John_Sm=C3=AEth?= <john@example.com>;
Date: Sat, 20 Nov 2021 14:22:01 -0800
Subject: Why not both importing AND exporting? =?utf-8?b?4pi6?=
Content-Type: multipart/mixed; boundary="festivus";
--festivus
Content-Type: text/html; charset="us-ascii"
Content-Transfer-Encoding: base64
PGh0bWw+PHA+SSB3YXMgdGhpbmtpbmcgYWJvdXQgcXVpdHRpbmcgdGhlICZsZHF1bztle
HBvcnRpbmcmcmRxdW87IHRvIGZvY3VzIGp1c3Qgb24gdGhlICZsZHF1bztpbXBvcnRpbm
cmcmRxdW87LDwvcD48cD5idXQgdGhlbiBJIHRob3VnaHQsIHdoeSBub3QgZG8gYm90aD8
gJiN4MjYzQTs8L3A+PC9odG1sPg==
--festivus
Content-Type: message/rfc822; name="Exporting my book about coffee tables.eml"
Content-Disposition: inline; filename="Exporting my book about coffee tables.eml"
Content-Transfer-Encoding: 7bit
From: "Cosmo Kramer" <kramer@kramerica.com>
Subject: Exporting my book about coffee tables
Content-Type: multipart/mixed; boundary="giddyup";
--giddyup
Content-Type: text/plain; charset="utf-16"
Content-Transfer-Encoding: quoted-printable
=FF=FE=0C!5=D8"=DD5=D8)=DD5=D8-=DD =005=D8*=DD5=D8"=DD =005=D8"=
=DD5=D85=DD5=D8-=DD5=D8,=DD5=D8/=DD5=D81=DD =005=D8*=DD5=D86=DD =
=005=D8=1F=DD5=D8,=DD5=D8,=DD5=D8(=DD =005=D8-=DD5=D8)=DD5=D8"=
=DD5=D8=1E=DD5=D80=DD5=D8"=DD!=00
--giddyup
Content-Type: image/gif; name*1="about "; name*0="Book ";
name*2*=utf-8''%e2%98%95 tables.gif
Content-Transfer-Encoding: Base64
Content-Disposition: attachment
R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7
--giddyup--
--festivus--
"#;
fn main() -> io::Result<()> {
let directory = env::args_os().nth(1).map_or_else(
|| env::temp_dir().join("mail-parser-attachments"),
PathBuf::from,
);
fs::create_dir_all(&directory)?;
let message = MessageParser::new()
.parse(MESSAGE)
.expect("the message parses");
for part in message.parts().filter(|part| part.is_attachment()) {
let path = directory.join(file_name(part));
fs::write(&path, part.decoded())?;
println!(
"{} ({} bytes, attachment of message {})",
path.display(),
part.decoded_len(),
part.message().id()
);
}
Ok(())
}
fn file_name(part: MessagePart<'_>) -> PathBuf {
let name = part
.attachment_name()
.map(str::to_string)
.or_else(|| {
part.nested()
.and_then(|nested| nested.subject())
.map(|subject| format!("{subject}.eml"))
})
.unwrap_or_default();
Path::new(&name).file_name().map_or_else(
|| PathBuf::from(format!("part-{}", part.id())),
PathBuf::from,
)
}