#[derive(Debug)]
pub struct Component<'b> {
pub name: PartName,
- pub payload_start: &'b [u8],
+ pub payload: &'b [u8],
}
#[derive(Debug)]
+#[derive(Eq,PartialEq,Ord,PartialOrd,Hash)]
#[allow(non_camel_case_types)]
pub enum PartName { m, d, Other }
+pub type BoundaryFinder = memchr::memmem::Finder<'static>;
+
#[throws(AE)]
-pub fn process_component<'b>(warnings: &mut Warnings,
- after_leader: &'b [u8], expected: PartName)
- -> Option<Component<'b>> {
+/// Processes the start of a component (or terminating boundary).
+///
+/// Returned payload is only the start of the payload; the next
+/// boundary has not been identified.
+pub fn process_boundary<'b>(warnings: &mut Warnings,
+ after_leader: &'b [u8], expected: PartName)
+ -> Option<Component<'b>> {
let rhs = after_leader;
let mut rhs =
if let Some(rhs) = rhs.strip_prefix(b"\r\n") { rhs }
if disposition.len() >= 100 { throw!(anyhow!(
"Content-Disposition value implausibly long"
)) }
+
+ // todo: replace with mailparse?
+ // (not in side, dep on charset not in sid)
+ // also seems to box for all the bits
+
// This let's us pretend it's a mime type, so we can use mime::Mime
let disposition = format!("dummy/{}", disposition);
};
}
- Some(Component { name: part_name.unwrap_or(expected), payload_start: rhs })
+ //dbg!(DumpHex(rhs));
+
+ Some(Component { name: part_name.unwrap_or(expected), payload: rhs })
+}
+
+pub struct ComponentIterator<'b> {
+ at_boundary: &'b [u8],
+ boundary_finder: BoundaryFinder,
+}
+
+#[derive(Error,Debug)]
+#[error("missing mime multipart boundary")]
+pub struct MissingBoundary;
+
+impl<'b> ComponentIterator<'b> {
+ #[throws(MissingBoundary)]
+ pub fn resume_mid_component(buf: &'b [u8], boundary_finder: BoundaryFinder)
+ -> (&'b [u8], Self) {
+ let next_boundary = boundary_finder.find(buf).ok_or(MissingBoundary)?;
+ let part = &buf[0..next_boundary];
+ let part = Self::payload_trim(part);
+
+ //dbg!(DumpHex(part));
+
+ (part, ComponentIterator {
+ at_boundary: &buf[next_boundary..],
+ boundary_finder,
+ })
+ }
+
+ fn payload_trim(payload: &[u8]) -> &[u8] {
+ payload.strip_suffix(b"\r").unwrap_or(payload)
+ }
+
+ #[throws(AE)]
+ pub fn next(&mut self, warnings: &mut Warnings, expected: PartName)
+ -> Option<Component<'b>> {
+ if self.at_boundary.is_empty() { return None }
+
+ let mut comp = match {
+ //dbg!(DumpHex(self.boundary_finder.needle()));
+ let boundary_len = self.boundary_finder.needle().len();
+ //dbg!(boundary_len);
+ process_boundary(warnings,
+ &self.at_boundary[boundary_len..],
+ expected)?
+ } {
+ None => {
+ self.at_boundary = &self.at_boundary[0..0];
+ return None;
+ },
+ Some(c) => c,
+ };
+
+ let next_boundary = self.boundary_finder.find(&comp.payload)
+ .ok_or(MissingBoundary)?;
+
+ comp.payload = Self::payload_trim(&comp.payload[0..next_boundary]);
+ self.at_boundary = &self.at_boundary[next_boundary..];
+
+ //dbg!(DumpHex(comp.payload));
+ //dbg!(DumpHex(&self.at_boundary[0..5]));
+
+ Some(comp)
+ }
+}
+
+pub struct MetadataFieldIterator<'b> {
+ buf: &'b [u8],
+ last: Option<usize>,
+ iter: memchr::Memchr<'b>,
+}
+
+impl<'b> MetadataFieldIterator<'b> {
+ pub fn new(buf: &'b [u8]) -> Self { Self {
+ buf,
+ last: Some(0),
+ iter: memchr::Memchr::new(b'\n', buf),
+ } }
+
+ #[throws(AE)]
+ pub fn need_next(&mut self) -> &'b str
+ {
+ self.next().ok_or_else(|| anyhow!("missing"))??
+ }
+
+ #[throws(AE)]
+ pub fn need_parse<T>(&mut self) -> T
+ where T: FromStr,
+ AE: From<T::Err>,
+ {
+ self.parse()?.ok_or_else(|| anyhow!("missing"))?
+ }
+
+ #[throws(AE)]
+ pub fn parse<T>(&mut self) -> Option<T>
+ where T: FromStr,
+ AE: From<T::Err>,
+ {
+ let s = if let Some(r) = self.next() { r? } else { return None };
+ Some(s.parse()?)
+ }
+
+ pub fn remaining_bytes_len(&self) -> usize {
+ if let Some(last) = self.last {
+ self.buf.len() - last
+ } else {
+ 0
+ }
+ }
+}
+
+impl<'b> Iterator for MetadataFieldIterator<'b> {
+ type Item = Result<&'b str, std::str::Utf8Error>;
+ fn next(&mut self) -> Option<Result<&'b str, std::str::Utf8Error>> {
+ let last = self.last?;
+ let (s, last) = match self.iter.next() {
+ Some(nl) => (&self.buf[last..nl], Some(nl+1)),
+ None => (&self.buf[last..], None),
+ };
+ self.last = last;
+ let s = str::from_utf8(s).map(|s| s.trim());
+ Some(s)
+ }
}
+impl<'b> std::iter::FusedIterator for MetadataFieldIterator<'b> { }