Some more FETCH things work
This commit is contained in:
parent
64a322b4cb
commit
24c6607304
8 changed files with 436 additions and 62 deletions
46
Cargo.lock
generated
46
Cargo.lock
generated
|
@ -11,6 +11,15 @@ dependencies = [
|
|||
"nom 6.1.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "addr2line"
|
||||
version = "0.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b"
|
||||
dependencies = [
|
||||
"gimli",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "adler"
|
||||
version = "1.0.2"
|
||||
|
@ -24,6 +33,7 @@ dependencies = [
|
|||
"anyhow",
|
||||
"argon2",
|
||||
"async-trait",
|
||||
"backtrace",
|
||||
"base64",
|
||||
"boitalettres",
|
||||
"chrono",
|
||||
|
@ -343,6 +353,21 @@ version = "1.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
|
||||
[[package]]
|
||||
name = "backtrace"
|
||||
version = "0.3.66"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cab84319d616cfb654d03394f38ab7e6f0919e181b1b57e1fd15e7fb4077d9a7"
|
||||
dependencies = [
|
||||
"addr2line",
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"miniz_oxide",
|
||||
"object",
|
||||
"rustc-demangle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.13.0"
|
||||
|
@ -963,6 +988,12 @@ dependencies = [
|
|||
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gimli"
|
||||
version = "0.26.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4"
|
||||
|
||||
[[package]]
|
||||
name = "gloo-timers"
|
||||
version = "0.2.4"
|
||||
|
@ -1499,6 +1530,15 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.29.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.12.0"
|
||||
|
@ -1951,6 +1991,12 @@ dependencies = [
|
|||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342"
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.4.0"
|
||||
|
|
|
@ -10,6 +10,7 @@ description = "Encrypted mail storage over Garage"
|
|||
anyhow = "1.0.28"
|
||||
argon2 = "0.3"
|
||||
async-trait = "0.1"
|
||||
backtrace = "0.3"
|
||||
base64 = "0.13"
|
||||
clap = { version = "3.1.18", features = ["derive", "env"] }
|
||||
duplexify = "1.1.0"
|
||||
|
|
|
@ -274,30 +274,28 @@ impl MailboxView {
|
|||
FetchAttribute::Rfc822Size => {
|
||||
attributes.push(MessageAttribute::Rfc822Size(meta.rfc822_size as u32))
|
||||
}
|
||||
FetchAttribute::Rfc822Header => attributes.push(
|
||||
MessageAttribute::Rfc822Header(NString(Some(IString::Literal(
|
||||
meta.headers
|
||||
.clone()
|
||||
.try_into()
|
||||
.or(Err(Error::msg("IString conversion error")))?,
|
||||
)))),
|
||||
),
|
||||
FetchAttribute::Rfc822Header => {
|
||||
attributes.push(MessageAttribute::Rfc822Header(NString(
|
||||
meta.headers.to_vec().try_into().ok().map(IString::Literal),
|
||||
)))
|
||||
}
|
||||
FetchAttribute::Rfc822Text => {
|
||||
let r = parsed
|
||||
.raw_message.get(parsed.offset_body..parsed.offset_end)
|
||||
.ok_or(Error::msg("Unable to extract email body, cursors out of bound. This is a bug."))?
|
||||
.try_into()
|
||||
.or(Err(Error::msg("IString conversion error")))?;
|
||||
.ok_or(Error::msg("Unable to extract email body, cursors out of bound. This is a bug."))?;
|
||||
|
||||
attributes.push(MessageAttribute::Rfc822Text(NString(Some(
|
||||
IString::Literal(r),
|
||||
))))
|
||||
}
|
||||
FetchAttribute::Rfc822 => {
|
||||
attributes.push(MessageAttribute::Rfc822(NString(Some(IString::Literal(
|
||||
body.as_ref().unwrap().clone().try_into().unwrap(),
|
||||
)))))
|
||||
attributes.push(MessageAttribute::Rfc822Text(NString(
|
||||
r.try_into().ok().map(IString::Literal),
|
||||
)));
|
||||
}
|
||||
FetchAttribute::Rfc822 => attributes.push(MessageAttribute::Rfc822(NString(
|
||||
body.as_ref()
|
||||
.unwrap()
|
||||
.clone()
|
||||
.try_into()
|
||||
.ok()
|
||||
.map(IString::Literal),
|
||||
))),
|
||||
FetchAttribute::Envelope => {
|
||||
attributes.push(MessageAttribute::Envelope(message_envelope(&parsed)))
|
||||
}
|
||||
|
@ -313,21 +311,8 @@ impl MailboxView {
|
|||
peek,
|
||||
} => {
|
||||
// @TODO Add missing section specifiers
|
||||
let text = match section {
|
||||
Some(FetchSection::Text(None)) => {
|
||||
parsed
|
||||
.raw_message.get(parsed.offset_body..parsed.offset_end)
|
||||
.ok_or(Error::msg("Unable to extract email body, cursors out of bound. This is a bug."))?
|
||||
}
|
||||
Some(FetchSection::Header(None)) => {
|
||||
parsed
|
||||
.raw_message.get(..parsed.offset_body)
|
||||
.ok_or(Error::msg("Unable to extract email body, cursors out of bound. This is a bug."))?
|
||||
}
|
||||
None => &parsed.raw_message,
|
||||
_ => bail!("Unimplemented: section {:?}", section),
|
||||
};
|
||||
|
||||
match get_message_section(&parsed, section) {
|
||||
Ok(text) => {
|
||||
let seen_flag = Flag::Seen.to_string();
|
||||
if !peek && !flags.iter().any(|x| *x == seen_flag) {
|
||||
// Add \Seen flag
|
||||
|
@ -342,21 +327,33 @@ impl MailboxView {
|
|||
(&text[*begin as usize..], Some(*begin))
|
||||
} else {
|
||||
(
|
||||
&text[*begin as usize..(*begin + len.get()) as usize],
|
||||
&text[*begin as usize
|
||||
..(*begin + len.get()) as usize],
|
||||
Some(*begin),
|
||||
)
|
||||
}
|
||||
}
|
||||
None => (text, None),
|
||||
None => (&text[..], None),
|
||||
};
|
||||
|
||||
let is = IString::try_from(std::str::from_utf8(text)?).unwrap();
|
||||
let data =
|
||||
NString(text.to_vec().try_into().ok().map(IString::Literal));
|
||||
attributes.push(MessageAttribute::BodyExt {
|
||||
section: section.clone(),
|
||||
origin,
|
||||
data: NString(Some(is)),
|
||||
data,
|
||||
})
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::error!(
|
||||
"Could not get section {:?} of message {}: {}",
|
||||
section,
|
||||
uuid,
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
FetchAttribute::InternalDate => {
|
||||
attributes.push(MessageAttribute::InternalDate(MyDateTime(
|
||||
Utc.fix()
|
||||
|
@ -972,6 +969,129 @@ fn headers_to_basic_fields<'a, T>(bp: &'a Part<T>) -> Result<(SpecialAttrs<'a>,
|
|||
Ok((attrs, bf))
|
||||
}
|
||||
|
||||
fn get_message_section<'a>(
|
||||
parsed: &'a Message<'a>,
|
||||
section: &Option<FetchSection>,
|
||||
) -> Result<Cow<'a, [u8]>> {
|
||||
match section {
|
||||
Some(FetchSection::Text(None)) => Ok(parsed
|
||||
.raw_message
|
||||
.get(parsed.offset_body..parsed.offset_end)
|
||||
.ok_or(Error::msg(
|
||||
"Unable to extract email body, cursors out of bound. This is a bug.",
|
||||
))?
|
||||
.into()),
|
||||
Some(FetchSection::Text(Some(part))) => {
|
||||
subpart_msg_fn(parsed, part.0.as_slice(), |part_msg| {
|
||||
Ok(part_msg
|
||||
.raw_message
|
||||
.get(part_msg.offset_body..parsed.offset_end)
|
||||
.ok_or(Error::msg(
|
||||
"Unable to extract email body, cursors out of bound. This is a bug.",
|
||||
))?
|
||||
.to_vec()
|
||||
.into())
|
||||
})
|
||||
}
|
||||
Some(FetchSection::Header(part)) => subpart_msg_fn(
|
||||
parsed,
|
||||
part.as_ref().map(|p| p.0.as_slice()).unwrap_or(&[]),
|
||||
|part_msg| {
|
||||
Ok(part_msg
|
||||
.raw_message
|
||||
.get(..part_msg.offset_body)
|
||||
.ok_or(Error::msg(
|
||||
"Unable to extract email header, cursors out of bound. This is a bug.",
|
||||
))?
|
||||
.to_vec()
|
||||
.into())
|
||||
},
|
||||
),
|
||||
Some(FetchSection::Part(part)) => subpart_fn(parsed, part.0.as_slice(), |_msg, part| {
|
||||
let bytes = match part {
|
||||
MessagePart::Text(p) | MessagePart::Html(p) => p.body.as_bytes().to_vec(),
|
||||
MessagePart::Binary(p) | MessagePart::InlineBinary(p) => p.body.to_vec(),
|
||||
MessagePart::Message(Part {
|
||||
body: MessageAttachment::Raw(r),
|
||||
..
|
||||
}) => r.to_vec(),
|
||||
MessagePart::Message(Part {
|
||||
body: MessageAttachment::Parsed(p),
|
||||
..
|
||||
}) => p.raw_message.to_vec(),
|
||||
MessagePart::Multipart(_) => bail!("Multipart part has no body"),
|
||||
};
|
||||
Ok(bytes.into())
|
||||
}),
|
||||
Some(FetchSection::Mime(part)) => subpart_fn(parsed, part.0.as_slice(), |msg, part| {
|
||||
let raw_headers = match part {
|
||||
MessagePart::Text(p) | MessagePart::Html(p) => &p.headers_raw,
|
||||
MessagePart::Binary(p) | MessagePart::InlineBinary(p) => &p.headers_raw,
|
||||
MessagePart::Message(p) => &p.headers_raw,
|
||||
MessagePart::Multipart(m) => &m.headers_raw,
|
||||
};
|
||||
let mut ret = vec![];
|
||||
for (name, body) in raw_headers {
|
||||
ret.extend(name.as_str().as_bytes());
|
||||
ret.extend(b": ");
|
||||
ret.extend(&msg.raw_message[body.start..body.end]);
|
||||
}
|
||||
ret.extend(b"\r\n");
|
||||
Ok(ret.into())
|
||||
}),
|
||||
None => Ok(parsed.raw_message.clone()),
|
||||
_ => bail!("Unimplemented: section {:?}", section),
|
||||
}
|
||||
}
|
||||
|
||||
fn subpart_msg_fn<'a, F, R>(msg: &Message<'a>, path: &[NonZeroU32], f: F) -> Result<R>
|
||||
where
|
||||
F: FnOnce(&Message<'_>) -> Result<R>,
|
||||
{
|
||||
if path.is_empty() {
|
||||
f(msg)
|
||||
} else {
|
||||
let part = msg
|
||||
.parts
|
||||
.get(path[0].get() as usize - 1)
|
||||
.ok_or(anyhow!("No such subpart: {}", path[0]))?;
|
||||
if matches!(part, MessagePart::Message(_)) {
|
||||
let part_msg = part
|
||||
.parse_message()
|
||||
.ok_or(anyhow!("Cannot parse subpart: {}", path[0]))?;
|
||||
subpart_msg_fn(&part_msg, &path[1..], f)
|
||||
} else {
|
||||
bail!("Subpart is not a message: {}", path[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn subpart_fn<'a, F, R>(msg: &Message<'a>, path: &[NonZeroU32], f: F) -> Result<R>
|
||||
where
|
||||
F: FnOnce(&Message<'_>, &MessagePart<'_>) -> Result<R>,
|
||||
{
|
||||
if path.is_empty() {
|
||||
bail!("Unexpected empty path");
|
||||
} else {
|
||||
let part = msg
|
||||
.parts
|
||||
.get(path[0].get() as usize - 1)
|
||||
.ok_or(anyhow!("No such subpart: {}", path[0]))?;
|
||||
if path.len() == 1 {
|
||||
f(msg, part)
|
||||
} else {
|
||||
if matches!(part, MessagePart::Message(_)) {
|
||||
let part_msg = part
|
||||
.parse_message()
|
||||
.ok_or(anyhow!("Cannot parse subpart: {}", path[0]))?;
|
||||
subpart_fn(&part_msg, &path[1..], f)
|
||||
} else {
|
||||
bail!("Subpart is not a message: {}", path[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
|
|
@ -392,7 +392,7 @@ async fn k2v_lock_loop_internal(
|
|||
|
||||
let res = futures::try_join!(watch_lock_loop, lock_notify_loop, take_lock_loop);
|
||||
|
||||
info!("lock loop exited: {:?}, releasing", res);
|
||||
info!("lock loop exited, releasing");
|
||||
|
||||
if !held_tx.is_closed() {
|
||||
warn!("wierd...");
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use std::convert::TryFrom;
|
||||
use std::io::Write;
|
||||
|
||||
pub mod incoming;
|
||||
pub mod mailbox;
|
||||
|
@ -17,6 +18,9 @@ impl<'a> TryFrom<&'a [u8]> for IMF<'a> {
|
|||
type Error = ();
|
||||
|
||||
fn try_from(body: &'a [u8]) -> Result<IMF<'a>, ()> {
|
||||
eprintln!("---- BEGIN PARSED MESSAGE ----");
|
||||
let _ = std::io::stderr().write_all(body);
|
||||
eprintln!("---- END PARSED MESSAGE ----");
|
||||
let parsed = mail_parser::Message::parse(body).ok_or(())?;
|
||||
Ok(Self { raw: body, parsed })
|
||||
}
|
||||
|
|
181
src/mail_parser_tests.rs
Normal file
181
src/mail_parser_tests.rs
Normal file
|
@ -0,0 +1,181 @@
|
|||
use mail_parser::Message;
|
||||
|
||||
#[test]
|
||||
fn test1() {
|
||||
let input = br#"Content-Type: multipart/mixed; boundary="1234567890123456789012345678901234567890123456789012345678901234567890123456789012"
|
||||
|
||||
--1234567890123456789012345678901234567890123456789012345678901234567890123456789012
|
||||
Content-Type: multipart/mixed; boundary="123456789012345678901234567890123456789012345678901234567890123456789012345678901"
|
||||
|
||||
--123456789012345678901234567890123456789012345678901234567890123456789012345678901
|
||||
Content-Type: multipart/mixed; boundary="12345678901234567890123456789012345678901234567890123456789012345678901234567890"
|
||||
|
||||
--12345678901234567890123456789012345678901234567890123456789012345678901234567890
|
||||
Content-Type: text/plain
|
||||
|
||||
1
|
||||
--1234567890123456789012345678901234567890123456789012345678901234567890123456789012
|
||||
Content-Type: text/plain
|
||||
|
||||
22
|
||||
--123456789012345678901234567890123456789012345678901234567890123456789012345678901
|
||||
Content-Type: text/plain
|
||||
|
||||
333
|
||||
--12345678901234567890123456789012345678901234567890123456789012345678901234567890
|
||||
Content-Type: text/plain
|
||||
|
||||
4444
|
||||
"#;
|
||||
|
||||
let message = Message::parse(input);
|
||||
dbg!(message);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test2() {
|
||||
let input = br#"Message-ID: <39235E1C.1DC7EA90@example.com>
|
||||
Date: Wed, 17 May 2000 23:06:04 -0400
|
||||
From: Doug Sauder <dwsauder@example.com>
|
||||
X-Mailer: Mozilla 4.7 [en] (WinNT; I)
|
||||
X-Accept-Language: en
|
||||
MIME-Version: 1.0
|
||||
To: Joe Blow <blow@example.com>
|
||||
Subject: Test message from Netscape Communicator 4.7
|
||||
Content-Type: multipart/mixed;
|
||||
boundary="------------A1FCDEE154E03D875E5D6779"
|
||||
|
||||
This is a multi-part message in MIME format.
|
||||
--------------A1FCDEE154E03D875E5D6779
|
||||
Content-Type: text/plain; charset=iso-8859-1
|
||||
Content-Transfer-Encoding: quoted-printable
|
||||
|
||||
Die Hasen und die Fr=F6sche
|
||||
|
||||
Die Hasen klagten einst =FCber ihre mi=DFliche Lage; "wir leben", sprach =
|
||||
ein
|
||||
Redner, "in steter Furcht vor Menschen und Tieren, eine Beute der Hunde,
|
||||
der Adler, ja fast aller Raubtiere! Unsere stete Angst ist =E4rger als de=
|
||||
r
|
||||
Tod selbst. Auf, la=DFt uns ein f=FCr allemal sterben."
|
||||
|
||||
In einem nahen Teich wollten sie sich nun ers=E4ufen; sie eilten ihm zu;
|
||||
allein das au=DFerordentliche Get=F6se und ihre wunderbare Gestalt
|
||||
erschreckte eine Menge Fr=F6sche, die am Ufer sa=DFen, so sehr, da=DF sie=
|
||||
aufs
|
||||
schnellste untertauchten.
|
||||
|
||||
"Halt", rief nun eben dieser Sprecher, "wir wollen das Ers=E4ufen noch ei=
|
||||
n
|
||||
wenig aufschieben, denn auch uns f=FCrchten, wie ihr seht, einige Tiere,
|
||||
welche also wohl noch ungl=FCcklicher sein m=FCssen als wir."
|
||||
|
||||
|
||||
|
||||
--------------A1FCDEE154E03D875E5D6779
|
||||
Content-Type: image/png;
|
||||
name="redball.png"
|
||||
Content-Transfer-Encoding: base64
|
||||
Content-Disposition: inline;
|
||||
filename="redball.png"
|
||||
|
||||
iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAMAAAC6CgRnAAADAFBMVEX///8AAAABAAALAAAV
|
||||
AAAaAAAXAAARAAAKAAADAAAcAAAyAABEAABNAABIAAA9AAAjAAAWAAAmAABhAAB7AACGAACH
|
||||
AAB9AAB0AABgAAA5AAAUAAAGAAAnAABLAABvAACQAAClAAC7AAC/AACrAAChAACMAABzAABb
|
||||
AAAuAAAIAABMAAB3AACZAAC0GRnKODjVPT3bKSndBQW4AACoAAB5AAAxAAAYAAAEAABFAACa
|
||||
AAC7JCTRYWHfhITmf3/mVlbqHx/SAAC5AACjAABdAABCAAAoAAAJAABnAAC6Dw/QVFTek5Pl
|
||||
rKzpmZntZWXvJSXXAADBAACxAACcAABtAABTAAA2AAAbAAAFAABKAACBAADLICDdZ2fonJzr
|
||||
pqbtiorvUVHvFBTRAADDAAC2AAB4AABeAABAAAAiAABXAACSAADCAADaGxvoVVXseHjveHjv
|
||||
V1fvJibhAADOAAC3AACnAACVAABHAAArAAAPAACdAADFAADhBQXrKCjvPDzvNTXvGxvjAADQ
|
||||
AADJAAC1AACXAACEAABsAABPAAASAAACAABiAADpAADvAgLnAADYAADLAAC6AACwAABwAAAT
|
||||
AAAkAABYAADIAADTAADNAACzAACDAABuAAAeAAB+AADAAACkAACNAAB/AABpAABQAAAwAACR
|
||||
AACpAAC8AACqAACbAABlAABJAAAqAAAOAAA0AACsAACvAACtAACmAACJAAB6AABrAABaAAA+
|
||||
AAApAABqAACCAACfAACeAACWAACPAAB8AAAZAAAHAABVAACOAACKAAA4AAAQAAA/AAByAACA
|
||||
AABcAAA3AAAsAABmAABDAABWAAAgAAAzAAA8AAA6AAAfAAAMAAAdAAANAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD8
|
||||
LtlFAAAAAXRSTlMAQObYZgAAABZ0RVh0U29mdHdhcmUAZ2lmMnBuZyAyLjAuMT1evmgAAAII
|
||||
SURBVHicY2CAg/8QwIABmJhZWFnZ2Dk4MaU5uLh5eHn5+LkFBDlQJf8zC/EIi4iKiUtI8koJ
|
||||
Scsgyf5nlpWTV1BUUlZRVVPX4NFk1UJIyghp6+jq6RsYGhmbKJgK85mZW8Dk/rNaSlhZ29ja
|
||||
2Ts4Ojkr6Li4urFDNf53N/Ow8vTy9vH18w8IDAoWDQkNC4+ASP5ni4wKio6JjYtPSExKTnFW
|
||||
SE1LF4A69n9GZlZ2Tm5efkFhUXFySWlZlEd5RSVY7j+TkGRVdU1tXX1DY1Ozcktpa1t7h2Yn
|
||||
OAj+d7l1tyo79vT29SdNSJ44SbFVdHIo9xSIHNPUaWqTpifNSJrZnK00S0U1a/acUG5piNz/
|
||||
uXLzVJ2qm6dXz584S2WB1cJFi5cshZr539xVftnyFKUVTi2TVjqvyhJLXb1m7TqoHPt6F/HW
|
||||
0g0bN63crGqVtWXrtu07BJihcsw71+zanRW8Z89eq337RQ/Ip60xO3gIElX/LbikDm8T36Kw
|
||||
bNmRo7O3zpHkPSZwHBqL//8flz1x2OOkyKJTi7aqbzutfUZI2gIuF8F2lr/D5dw2+fZdwpl8
|
||||
YVOlI+CJ4/9/joOyYed5QzMvhGqnm2V0WiClm///D0lfXHtJ6vLlK9w7rx7vQk5SQJbFtSms
|
||||
1y9evXid7QZacgOxmSxktNzdtSwwU+J/VICaCPFIYU3XAJhIOtjf5sfyAAAAJXRFWHRDb21t
|
||||
ZW50AGNsaXAyZ2lmIHYuMC42IGJ5IFl2ZXMgUGlndWV0NnM7vAAAAABJRU5ErkJggg==
|
||||
--------------A1FCDEE154E03D875E5D6779
|
||||
Content-Type: image/png;
|
||||
name="greenball.png"
|
||||
Content-Transfer-Encoding: base64
|
||||
Content-Disposition: inline;
|
||||
filename="greenball.png"
|
||||
|
||||
iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAMAAAC6CgRnAAADAFBMVEX///8AAAAAEAAAGAAA
|
||||
IQAACAAAMQAAQgAAUgAAWgAASgAIYwAIcwAIewAQjAAIawAAOQAAYwAQlAAQnAAhpQAQpQAh
|
||||
rQBCvRhjxjFjxjlSxiEpzgAYvQAQrQAYrQAhvQCU1mOt1nuE1lJK3hgh1gAYxgAYtQAAKQBC
|
||||
zhDO55Te563G55SU52NS5yEh3gAYzgBS3iGc52vW75y974yE71JC7xCt73ul3nNa7ykh5wAY
|
||||
1gAx5wBS7yFr7zlK7xgp5wAp7wAx7wAIhAAQtQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAp
|
||||
1fnZAAAAAXRSTlMAQObYZgAAABZ0RVh0U29mdHdhcmUAZ2lmMnBuZyAyLjAuMT1evmgAAAFt
|
||||
SURBVHicddJtV8IgFAdwD2zIgMEE1+NcqdsoK+m5tCyz7/+ZiLmHsyzvq53zO/cy+N9ery1b
|
||||
Ve9PWQA9z4MQ+H8Yoj7GASZ95IHfaBGmLOSchyIgyOu22mgQSjUcDuNYcoGjLiLK1cHh0fHJ
|
||||
aTKKOcMItgYxT89OzsfjyTTLC8UF0c2ZNmKquJhczq6ub+YmSVUYRF59GeDastu7+9nD41Nm
|
||||
kiJ2jc2J3kAWZ9Pr55fH18XSmRuKUTXUaqHy7O19tfr4NFle/w3YDrWRUIlZrL/W86XJkyJV
|
||||
G9EaEjIx2XyZmZJGioeUaL+2AY8TY8omR6nkLKhu70zjUKVJXsp3quS2DVSJWNh3zzJKCyex
|
||||
I0ZxBP3afE0ElyqOlZJyw8r3BE2SFiJCyxA434SCkg65RhdeQBljQtCg39LWrA90RDDG1EWr
|
||||
YUO23hMANUKRRl61E529cR++D2G5LK002dr/qrcfu9u0V3bxn/XdhR/NYeeN0ggsLAAAACV0
|
||||
RVh0Q29tbWVudABjbGlwMmdpZiB2LjAuNiBieSBZdmVzIFBpZ3VldDZzO7wAAAAASUVORK5C
|
||||
YII=
|
||||
--------------A1FCDEE154E03D875E5D6779
|
||||
Content-Type: image/png;
|
||||
name="blueball.png"
|
||||
Content-Transfer-Encoding: base64
|
||||
Content-Disposition: inline;
|
||||
filename="blueball.png"
|
||||
|
||||
iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAMAAAC6CgRnAAADAFBMVEX///8AAAgAABAAABgA
|
||||
AAAACCkAEEIAEEoACDEAEFIIIXMIKXsIKYQIIWsAGFoACDkIIWMQOZwYQqUYQq0YQrUQOaUQ
|
||||
MZQAGFIQMYwpUrU5Y8Y5Y84pWs4YSs4YQs4YQr1Ca8Z7nNacvd6Mtd5jlOcxa94hUt4YStYY
|
||||
QsYQMaUAACHO5+/n7++cxu9ShO8pWucQOa1Ke86tzt6lzu9ajO8QMZxahNat1ufO7++Mve9K
|
||||
e+8YOaUYSsaMvee15++Uve8AAClajOdzpe9rnO8IKYwxY+8pWu8IIXsAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADB
|
||||
Mg1VAAAAAXRSTlMAQObYZgAAABZ0RVh0U29mdHdhcmUAZ2lmMnBuZyAyLjAuMT1evmgAAAGI
|
||||
SURBVHicddJtV5swGAbgEk6AJhBSk4bMCUynBSLaqovbrG/bfPn/vyh70lbsscebL5xznTsh
|
||||
5BmNhgQoRChwo50EOIohUYLDj4zHhKYQkrEoQdvock4ne0IKMVUpKZLQDeqSTIsv+18PyqqW
|
||||
Uw2IBsRM7307PPp+fDJrWtnpLDJvewYxnewfnvanZ+fzpmwXijC8KbqEa3Fx2ff91Y95U9XC
|
||||
UpaDeQwiMpHXP/v+1++bWVPWQoGFawtjury9vru/f/C1Vi7ezT0WWpQHf/7+u/G71aLThK/M
|
||||
jRxmT6KdzZ9fGk9yatMsTgZLl3XVgFRAC6spj/13enssqJVtWVa3NdBSacL8+VZmYqKmdd1C
|
||||
SYoOiMOSGwtzlqqlFFIuOqv0a1ZEZrUkWICLLFW266y1KvWE1zV/iDAH1EopnVLCiygZCIom
|
||||
H3NCKX0lnI+B1iuuzCGTxwXjnDO4d7NpbX42YJJHkBwmAm2TxwAZg40J3+Xtbv1rgOAZwG0N
|
||||
xW62p+lT+Yi747sD/wEUVMzYmWkOvwAAACV0RVh0Q29tbWVudABjbGlwMmdpZiB2LjAuNiBi
|
||||
eSBZdmVzIFBpZ3VldDZzO7wAAAAASUVORK5CYII=
|
||||
--------------A1FCDEE154E03D875E5D6779--
|
||||
"#;
|
||||
|
||||
let message = Message::parse(input).unwrap();
|
||||
//dbg!(&message);
|
||||
let part = message.parts.get(0).unwrap();
|
||||
//dbg!(&part);
|
||||
let part_msg = part.parse_message().unwrap();
|
||||
}
|
|
@ -9,6 +9,9 @@ mod mail;
|
|||
mod server;
|
||||
mod time;
|
||||
|
||||
#[cfg(test)]
|
||||
mod mail_parser_tests;
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
use anyhow::{bail, Result};
|
||||
|
@ -121,7 +124,8 @@ async fn main() -> Result<()> {
|
|||
|
||||
// Abort on panic (same behavior as in Go)
|
||||
std::panic::set_hook(Box::new(|panic_info| {
|
||||
tracing::error!("{}", panic_info.to_string());
|
||||
eprintln!("{}", panic_info.to_string());
|
||||
eprintln!("{:?}", backtrace::Backtrace::new());
|
||||
std::process::abort();
|
||||
}));
|
||||
|
||||
|
|
18
tests/inject_emails.sh
Executable file
18
tests/inject_emails.sh
Executable file
|
@ -0,0 +1,18 @@
|
|||
#!/bin/sh
|
||||
|
||||
cd $(dirname $0)
|
||||
|
||||
function mail_lmtp_session (
|
||||
echo -e "LHLO localhost\r"
|
||||
for mail in $(find emails -name '*.eml'); do
|
||||
echo -e "MAIL FROM: <alex@adnab.me>\r"
|
||||
echo -e "RCPT TO: <lx@staging.deuxfleurs.org>\r"
|
||||
echo -e "DATA\r"
|
||||
cat $mail
|
||||
echo -e "\r"
|
||||
echo -e ".\r"
|
||||
done
|
||||
echo -e "QUIT\r"
|
||||
)
|
||||
|
||||
mail_lmtp_session | tee >(nc localhost 12024)
|
Loading…
Reference in a new issue