use std::borrow::Cow;
use std::cell::Cell;
use std::fmt;
use std::io::{BufRead, Read};
use std::mem;
use std::rc::Rc;
use std::str;
use chrono::prelude::*;
use super::header::{self, ContentType};
use crate::account::model::*;
use crate::support::error::Error;
#[allow(unused_variables)]
pub trait Visitor: fmt::Debug {
type Output;
fn uid(&mut self, uid: Uid) -> Result<(), Self::Output> {
self.visit_default()
}
fn email_id(&mut self, id: &str) -> Result<(), Self::Output> {
self.visit_default()
}
fn last_modified(&mut self, modseq: Modseq) -> Result<(), Self::Output> {
self.visit_default()
}
fn savedate(
&mut self,
savedate: DateTime<Utc>,
) -> Result<(), Self::Output> {
self.visit_default()
}
fn want_flags(&self) -> bool {
false
}
fn flags(&mut self, flags: &[Flag]) -> Result<(), Self::Output> {
self.visit_default()
}
fn recent(&mut self) -> Result<(), Self::Output> {
self.visit_default()
}
fn end_flags(&mut self) -> Result<(), Self::Output> {
self.visit_default()
}
fn rfc822_size(&mut self, size: u32) -> Result<(), Self::Output> {
self.visit_default()
}
fn metadata(
&mut self,
metadata: &MessageMetadata,
) -> Result<(), Self::Output> {
self.visit_default()
}
fn raw_line(&mut self, line: &[u8]) -> Result<(), Self::Output> {
self.visit_default()
}
fn header(
&mut self,
raw: &[u8],
name: &str,
value: &[u8],
) -> Result<(), Self::Output> {
self.visit_default()
}
fn content_type(
&mut self,
ct: &ContentType<'_>,
) -> Result<(), Self::Output> {
self.visit_default()
}
fn leaf_section(
&mut self,
) -> Option<Box<dyn Visitor<Output = Self::Output>>> {
None
}
fn start_content(&mut self) -> Result<(), Self::Output> {
self.visit_default()
}
fn content(&mut self, data: &[u8]) -> Result<(), Self::Output> {
self.visit_default()
}
fn start_part(
&mut self,
) -> Option<Box<dyn Visitor<Output = Self::Output>>> {
None
}
fn child_result(
&mut self,
child_result: Self::Output,
) -> Result<(), Self::Output> {
self.visit_default()
}
fn end(&mut self) -> Self::Output;
fn visit_default(&mut self) -> Result<(), Self::Output>;
}
pub trait MessageAccessor {
type Reader: BufRead;
fn uid(&mut self) -> Uid;
fn email_id(&mut self) -> Option<String>;
fn last_modified(&mut self) -> Modseq;
fn savedate(&mut self) -> Option<DateTime<Utc>>;
fn is_recent(&mut self) -> bool;
fn flags(&mut self) -> Vec<Flag>;
fn rfc822_size(&mut self) -> Option<u32>;
fn open(&mut self) -> Result<(MessageMetadata, Self::Reader), Error>;
}
#[cfg(test)]
pub struct SimpleAccessor {
pub uid: Uid,
pub email_id: Option<String>,
pub last_modified: Modseq,
pub recent: bool,
pub flags: Vec<Flag>,
pub savedate: Option<DateTime<Utc>>,
pub rfc822_size: Option<u32>,
pub metadata: MessageMetadata,
pub data: Vec<u8>,
}
#[cfg(test)]
impl Default for SimpleAccessor {
fn default() -> Self {
SimpleAccessor {
uid: Uid::MIN,
email_id: None,
last_modified: Modseq::MIN,
recent: false,
flags: vec![],
savedate: None,
rfc822_size: None,
metadata: MessageMetadata::default(),
data: vec![],
}
}
}
#[cfg(test)]
impl MessageAccessor for SimpleAccessor {
type Reader = std::io::BufReader<std::io::Cursor<Vec<u8>>>;
fn uid(&mut self) -> Uid {
self.uid
}
fn email_id(&mut self) -> Option<String> {
self.email_id.clone()
}
fn last_modified(&mut self) -> Modseq {
self.last_modified
}
fn savedate(&mut self) -> Option<DateTime<Utc>> {
self.savedate
}
fn is_recent(&mut self) -> bool {
self.recent
}
fn flags(&mut self) -> Vec<Flag> {
self.flags.clone()
}
fn rfc822_size(&mut self) -> Option<u32> {
self.rfc822_size
}
fn open(&mut self) -> Result<(MessageMetadata, Self::Reader), Error> {
Ok((
self.metadata.clone(),
std::io::BufReader::with_capacity(
80,
std::io::Cursor::new(self.data.clone()),
),
))
}
}
pub fn grovel<V>(
accessor: &mut impl MessageAccessor,
visitor: impl IntoBoxedVisitor<V>,
) -> Result<V, Error> {
Groveller::new(visitor.into_boxed_visitor()).grovel(accessor)
}
#[derive(Debug)]
struct Groveller<V> {
visitor: Box<dyn Visitor<Output = V>>,
in_headers: bool,
in_content: bool,
first_line_of_content: bool,
seen_content_type: bool,
seen_multipart_delim: bool,
last_line_ending_is_content: bool,
buffered_header: Vec<u8>,
buffered_line_ending: &'static [u8],
default_content_type: ContentType<'static>,
child_default_content_type: ContentType<'static>,
child: Option<Box<Self>>,
boundary: Option<Vec<u8>>,
is_message_rfc822: bool,
recursion_depth: u32,
total_part_count: Rc<Cell<u32>>,
}
const CT_TEXT_PLAIN: ContentType<'static> = ContentType {
typ: Cow::Borrowed(b"text"),
subtype: Cow::Borrowed(b"plain"),
parms: vec![],
};
const CT_MESSAGE_RFC822: ContentType<'static> = ContentType {
typ: Cow::Borrowed(b"message"),
subtype: Cow::Borrowed(b"rfc822"),
parms: vec![],
};
#[cfg(not(test))]
const MAX_BUFFER: usize = 65536;
#[cfg(test)]
pub(super) const MAX_BUFFER: usize = 256;
const MAX_RECURSION: u32 = 20;
const MAX_PARTS: u32 = 1000;
pub trait IntoBoxedVisitor<V> {
fn into_boxed_visitor(self) -> Box<dyn Visitor<Output = V>>;
}
impl<V: Visitor + 'static> IntoBoxedVisitor<V::Output> for V {
fn into_boxed_visitor(self) -> Box<dyn Visitor<Output = V::Output>> {
Box::new(self)
}
}
impl<V> IntoBoxedVisitor<V> for Box<dyn Visitor<Output = V>> {
fn into_boxed_visitor(self) -> Self {
self
}
}
impl<V> Groveller<V> {
fn new(visitor: Box<dyn Visitor<Output = V>>) -> Self {
Groveller::new_with_part_count(visitor, Rc::new(Cell::new(0)))
}
fn new_with_part_count(
visitor: Box<dyn Visitor<Output = V>>,
part_count: Rc<Cell<u32>>,
) -> Self {
Groveller {
visitor,
in_headers: true,
in_content: false,
first_line_of_content: true,
seen_content_type: false,
seen_multipart_delim: false,
last_line_ending_is_content: true,
buffered_header: vec![],
buffered_line_ending: b"",
default_content_type: CT_TEXT_PLAIN,
child_default_content_type: CT_TEXT_PLAIN,
child: None,
boundary: None,
is_message_rfc822: false,
recursion_depth: 0,
total_part_count: part_count,
}
}
fn grovel(
mut self,
accessor: &mut impl MessageAccessor,
) -> Result<V, Error> {
if let Err(result) = self.check_info(accessor) {
return Ok(result);
}
let (metadata, reader) = accessor.open()?;
if let Err(result) = self.visitor.metadata(&metadata) {
return Ok(result);
}
self.read_through(reader)
}
fn check_info(
&mut self,
accessor: &mut impl MessageAccessor,
) -> Result<(), V> {
self.visitor.uid(accessor.uid())?;
if let Some(email_id) = accessor.email_id() {
self.visitor.email_id(&email_id)?;
}
self.visitor.last_modified(accessor.last_modified())?;
if let Some(savedate) = accessor.savedate() {
self.visitor.savedate(savedate)?;
}
if self.visitor.want_flags() {
if accessor.is_recent() {
self.visitor.recent()?;
}
let flags = accessor.flags();
self.visitor.flags(&flags)?;
self.visitor.end_flags()?;
}
if let Some(rfc822_size) = accessor.rfc822_size() {
self.visitor.rfc822_size(rfc822_size)?;
}
Ok(())
}
fn read_through(mut self, mut r: impl BufRead) -> Result<V, Error> {
let mut buf = Vec::new();
let mut wrapped_cr = false;
loop {
let direct_consumed = if wrapped_cr {
None
} else {
let r_buf = r.fill_buf()?;
if r_buf.is_empty() {
break;
}
let lf = memchr::memchr(b'\n', r_buf);
if let Some(lf) = lf {
let could_be_continuation =
could_be_continuation(&r_buf[lf + 1..]);
if let Err(output) = self.push_line_and_content(
&r_buf[..=lf],
could_be_continuation,
) {
return Ok(output);
}
}
lf
};
if let Some(direct_consumed) = direct_consumed {
r.consume(direct_consumed + 1);
continue;
}
buf.clear();
if wrapped_cr {
buf.push(b'\r');
wrapped_cr = false;
}
r.by_ref()
.take(MAX_BUFFER as u64)
.read_until(b'\n', &mut buf)?;
if buf.is_empty() {
break;
}
if MAX_BUFFER == buf.len() && Some(&b'\r') == buf.last() {
wrapped_cr = true;
buf.pop();
}
let next_buf = r.fill_buf()?;
let could_be_continuation =
!next_buf.is_empty() && could_be_continuation(next_buf);
if let Err(output) =
self.push_line_and_content(&buf, could_be_continuation)
{
return Ok(output);
}
}
Ok(self.end())
}
fn push_line_and_content(
&mut self,
line: &[u8],
next_could_be_continuation: bool,
) -> Result<(), V> {
self.push(line, next_could_be_continuation)?;
self.push_content(line)
}
fn push(
&mut self,
line: &[u8],
next_could_be_continuation: bool,
) -> Result<(), V> {
self.visitor.raw_line(line)?;
if self.in_headers {
let is_continuation =
line.starts_with(b" ") || line.starts_with(b"\t");
if !is_continuation && !self.buffered_header.is_empty() {
self.process_buffered_header()?;
}
if b"\n" == line || b"\r\n" == line {
self.end_headers()?;
} else if is_continuation {
if !self.buffered_header.is_empty() {
self.buffered_header.extend_from_slice(line);
if self.buffered_header.len() > MAX_BUFFER {
self.process_buffered_header()?;
}
}
} else {
assert!(self.buffered_header.is_empty());
if next_could_be_continuation {
self.buffered_header.extend_from_slice(line);
} else {
self.process_header(line)?;
}
}
} else {
let is_first = self.first_line_of_content;
self.first_line_of_content = false;
if is_first || !self.buffered_line_ending.is_empty() {
let (at_boundary, is_final) = self
.boundary
.as_ref()
.map(|boundary| {
if line.starts_with(boundary) {
(true, line[boundary.len()..].starts_with(b"--"))
} else {
(false, false)
}
})
.unwrap_or((false, false));
if at_boundary {
self.buffered_line_ending = b"";
if self.seen_multipart_delim {
self.end_multipart_part()?;
}
self.seen_multipart_delim = true;
if !is_final {
self.start_multipart_part()?;
}
return Ok(());
}
let ble = self.buffered_line_ending;
self.on_child(|child| child.push_content(ble))?;
}
let content = if line.ends_with(b"\r\n") {
self.buffered_line_ending = b"\r\n";
&line[..line.len() - 2]
} else if line.ends_with(b"\n") {
self.buffered_line_ending = b"\n";
&line[..line.len() - 1]
} else {
self.buffered_line_ending = b"";
line
};
self.on_child(|child| {
child.push(line, next_could_be_continuation)
})?;
self.on_child(|child| child.push_content(content))?;
}
Ok(())
}
fn push_content(&mut self, content: &[u8]) -> Result<(), V> {
if self.in_headers {
Ok(())
} else if self.in_content {
self.visitor.content(content)
} else {
self.in_content |= b"\n" == content || b"\r\n" == content;
Ok(())
}
}
fn process_buffered_header(&mut self) -> Result<(), V> {
let mut bh = mem::take(&mut self.buffered_header);
let ret = self.process_header(&bh);
bh.clear();
self.buffered_header = bh;
ret
}
fn process_header(&mut self, header: &[u8]) -> Result<(), V> {
let mut split = header.splitn(2, |&b| b':' == b);
let (name, value) = match (split.next(), split.next()) {
(Some(name), Some(value)) => (name, value),
_ => return Ok(()),
};
let name = match str::from_utf8(name) {
Err(_) => return Ok(()),
Ok(name) => name.trim(),
};
self.visitor.header(header, name, value)?;
if "Content-Type".eq_ignore_ascii_case(name) {
if let Some(ct) = header::parse_content_type(value) {
self.content_type(&ct)?;
}
}
Ok(())
}
fn content_type(&mut self, ct: &ContentType<'_>) -> Result<(), V> {
if self.seen_content_type {
return Ok(());
}
self.seen_content_type = true;
self.visitor.content_type(ct)?;
if ct.is_type("multipart") {
if let Some(bound) = ct.parm("boundary") {
let mut boundary = Vec::with_capacity(bound.len() + 2);
boundary.extend_from_slice(b"--");
boundary.extend_from_slice(bound);
self.boundary = Some(boundary);
}
if ct.is_subtype("digest") {
self.child_default_content_type = CT_MESSAGE_RFC822;
}
} else if ct.is_type("message") && ct.is_subtype("rfc822") {
self.is_message_rfc822 = true;
}
Ok(())
}
fn end_headers(&mut self) -> Result<(), V> {
assert!(self.buffered_header.is_empty());
if !self.seen_content_type {
let dct = self.default_content_type.clone();
self.content_type(&dct)?;
}
self.in_headers = false;
if !self.is_message_rfc822 && self.boundary.is_none() {
if let Some(new_visitor) = self.visitor.leaf_section() {
self.visitor = new_visitor;
}
}
self.visitor.start_content()?;
if self.is_message_rfc822 {
self.start_multipart_part()?;
}
Ok(())
}
fn do_multipart_bookkeeping(&self) -> bool {
self.recursion_depth < MAX_RECURSION
&& (self.boundary.is_some() || self.is_message_rfc822)
&& self.total_part_count.get() < MAX_PARTS
}
fn start_multipart_part(&mut self) -> Result<(), V> {
if !self.do_multipart_bookkeeping() {
return Ok(());
}
assert!(self.child.is_none());
if let Some(child_visitor) = self.visitor.start_part() {
let mut child = Self::new_with_part_count(
child_visitor,
Rc::clone(&self.total_part_count),
);
child.default_content_type =
self.child_default_content_type.clone();
child.recursion_depth = self.recursion_depth + 1;
child.last_line_ending_is_content =
self.last_line_ending_is_content && self.is_message_rfc822;
self.child = Some(Box::new(child));
self.total_part_count.set(self.total_part_count.get() + 1);
}
Ok(())
}
fn on_child(
&mut self,
f: impl FnOnce(&mut Self) -> Result<(), V>,
) -> Result<(), V> {
let child_result = self.child.as_mut().and_then(|c| f(c).err());
if let Some(child_result) = child_result {
self.child = None;
self.visitor.child_result(child_result)
} else {
Ok(())
}
}
fn end_multipart_part(&mut self) -> Result<(), V> {
if !self.do_multipart_bookkeeping() && self.child.is_none() {
return Ok(());
}
if self.boundary.is_none()
&& !self.buffered_line_ending.is_empty()
&& self.last_line_ending_is_content
{
let ble = self.buffered_line_ending;
self.on_child(|child| child.push_content(ble))?;
}
if let Some(child) = self.child.take() {
self.visitor.child_result(child.end())?;
}
Ok(())
}
fn end(mut self) -> V {
if let Err(output) = self.end_multipart_part() {
return output;
}
self.visitor.end()
}
}
fn could_be_continuation(tail: &[u8]) -> bool {
tail.is_empty() || tail.starts_with(b" ") || tail.starts_with(b"\t")
}
pub struct VisitorMap<V, FTO, FFROM> {
delegate: Box<dyn Visitor<Output = V>>,
map_to: FTO,
map_from: FFROM,
}
impl<V, FTO, FFROM> fmt::Debug for VisitorMap<V, FTO, FFROM> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("VisitorMap")
.field("delegate", &self.delegate)
.field("map_to", &"<function>")
.field("map_from", &"<function>")
.finish()
}
}
impl<
V: 'static,
R,
FTO: Clone + FnMut(V) -> R + 'static,
FFROM: Clone + FnMut(R) -> Option<V> + 'static,
> VisitorMap<V, FTO, FFROM>
{
pub fn new(
delegate: Box<dyn Visitor<Output = V>>,
map_to: FTO,
map_from: FFROM,
) -> Self {
VisitorMap {
delegate,
map_to,
map_from,
}
}
}
impl<
V: 'static,
R,
FTO: Clone + FnMut(V) -> R + 'static,
FFROM: Clone + FnMut(R) -> Option<V> + 'static,
> Visitor for VisitorMap<V, FTO, FFROM>
{
type Output = R;
fn uid(&mut self, uid: Uid) -> Result<(), Self::Output> {
self.delegate.uid(uid).map_err(&mut self.map_to)
}
fn email_id(&mut self, id: &str) -> Result<(), Self::Output> {
self.delegate.email_id(id).map_err(&mut self.map_to)
}
fn last_modified(&mut self, modseq: Modseq) -> Result<(), Self::Output> {
self.delegate
.last_modified(modseq)
.map_err(&mut self.map_to)
}
fn savedate(
&mut self,
savedate: DateTime<Utc>,
) -> Result<(), Self::Output> {
self.delegate.savedate(savedate).map_err(&mut self.map_to)
}
fn want_flags(&self) -> bool {
self.delegate.want_flags()
}
fn flags(&mut self, flags: &[Flag]) -> Result<(), Self::Output> {
self.delegate.flags(flags).map_err(&mut self.map_to)
}
fn recent(&mut self) -> Result<(), Self::Output> {
self.delegate.recent().map_err(&mut self.map_to)
}
fn end_flags(&mut self) -> Result<(), Self::Output> {
self.delegate.end_flags().map_err(&mut self.map_to)
}
fn rfc822_size(&mut self, size: u32) -> Result<(), Self::Output> {
self.delegate.rfc822_size(size).map_err(&mut self.map_to)
}
fn metadata(
&mut self,
metadata: &MessageMetadata,
) -> Result<(), Self::Output> {
self.delegate.metadata(metadata).map_err(&mut self.map_to)
}
fn raw_line(&mut self, line: &[u8]) -> Result<(), Self::Output> {
self.delegate.raw_line(line).map_err(&mut self.map_to)
}
fn header(
&mut self,
raw: &[u8],
name: &str,
value: &[u8],
) -> Result<(), Self::Output> {
self.delegate
.header(raw, name, value)
.map_err(&mut self.map_to)
}
fn content_type(
&mut self,
ct: &ContentType<'_>,
) -> Result<(), Self::Output> {
self.delegate.content_type(ct).map_err(&mut self.map_to)
}
fn leaf_section(
&mut self,
) -> Option<Box<dyn Visitor<Output = Self::Output>>> {
self.delegate.leaf_section().map(|delegate| {
Box::new(VisitorMap {
delegate,
map_to: self.map_to.clone(),
map_from: self.map_from.clone(),
}) as Box<dyn Visitor<Output = Self::Output>>
})
}
fn start_content(&mut self) -> Result<(), Self::Output> {
self.delegate.start_content().map_err(&mut self.map_to)
}
fn content(&mut self, data: &[u8]) -> Result<(), Self::Output> {
self.delegate.content(data).map_err(&mut self.map_to)
}
fn start_part(
&mut self,
) -> Option<Box<dyn Visitor<Output = Self::Output>>> {
self.delegate.start_part().map(|delegate| {
Box::new(VisitorMap {
delegate,
map_to: self.map_to.clone(),
map_from: self.map_from.clone(),
}) as Box<dyn Visitor<Output = Self::Output>>
})
}
fn child_result(
&mut self,
child_result: Self::Output,
) -> Result<(), Self::Output> {
if let Some(child_result) = (self.map_from)(child_result) {
self.delegate
.child_result(child_result)
.map_err(&mut self.map_to)
} else {
Ok(())
}
}
fn end(&mut self) -> Self::Output {
(self.map_to)(self.delegate.end())
}
fn visit_default(&mut self) -> Result<(), Self::Output> {
panic!("missing method on VisitorMap")
}
}