chiark / gitweb /
server: get token
[hippotat.git] / src / multipart.rs
index 877d83af8869451da0d867df140f1e12aa0a2dc9..1c0be403351b4d91fe691afc6d7d39e10b79d8fe 100644 (file)
@@ -4,12 +4,14 @@
 
 use crate::prelude::*;
 
+#[derive(Debug)]
 pub struct Component<'b> {
   pub name: PartName,
   pub payload_start: &'b [u8],
 }
 
 #[derive(Debug)]
+#[derive(Eq,PartialEq,Ord,PartialOrd,Hash)]
 #[allow(non_camel_case_types)]
 pub enum PartName { m, d, Other }
 
@@ -41,6 +43,13 @@ pub fn process_component<'b>(warnings: &mut Warnings,
         regex_captures!(r#"^Content-Disposition[ \t]*:[ \t]*(.*)$"#i, l) { y }
         else { return Ok(()) };
 
+      let disposition = disposition.trim_end();
+      if disposition.len() >= 100 { throw!(anyhow!(
+        "Content-Disposition value implausibly long"
+      )) }
+      // This let's us pretend it's a mime type, so we can use mime::Mime
+      let disposition = format!("dummy/{}", disposition);
+
       let disposition: mime::Mime = disposition.parse()
         .context("parse Content-Disposition")?;
       let name = disposition.get_param("name")
@@ -64,3 +73,55 @@ pub fn process_component<'b>(warnings: &mut Warnings,
 
   Some(Component { name: part_name.unwrap_or(expected), payload_start: rhs })
 }
+
+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()?)
+  }
+}
+                                      
+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> { }