chiark / gitweb /
server: wip
authorIan Jackson <ijackson@chiark.greenend.org.uk>
Sun, 8 Aug 2021 16:22:05 +0000 (17:22 +0100)
committerIan Jackson <ijackson@chiark.greenend.org.uk>
Sun, 8 Aug 2021 16:22:05 +0000 (17:22 +0100)
Signed-off-by: Ian Jackson <ijackson@chiark.greenend.org.uk>
Cargo.lock
Cargo.toml
src/bin/server.rs
src/lib.rs
src/multipart.rs [new file with mode: 0644]
src/prelude.rs
src/reporter.rs

index 0a7897ec95fe2e7e560e6d296ed1ca8df8b17e03..ee063c25309113e533a81a1ebbf0215ed536b696 100644 (file)
@@ -428,6 +428,7 @@ dependencies = [
  "lazy-regex",
  "lazy_static",
  "log",
+ "memchr",
  "mime",
  "parking_lot",
  "regex",
index 8d27fef9a2fc7007cd110df9dbc4596f483db8e5..fb745c8a2daa5c30375421c76b7f015e3827be0c 100644 (file)
@@ -40,6 +40,7 @@ parking_lot = "0.11"
 regex = "1.5"
 lazy_static = "1.4"
 log = "0.4"
+memchr = "2"
 sha2 = "0.9"
 structopt = "0.3"
 tokio = { version = "1", features = ["full"] }
index 3724c453c48fc76fd96078e5cdf390cc28bf6ef9..be85bb5acabb28b63901f7986a444179cf06a634 100644 (file)
@@ -51,7 +51,22 @@ async fn handle(
       Err(ReadLimitedError::Hyper(e)) => throw!(e),
     };
 
-    eprintln!("boundary={:?} initial={:?}", boundary, initial);
+    let finder = memmem::Finder::new(&boundary);
+    let mut find_iter = finder.find_iter(&initial);
+
+    let start = if initial.starts_with(&boundary[1..]) { boundary.len()-1 }
+    else if let Some(start) = find_iter.next() { start + boundary.len() }
+    else { throw!(anyhow!("initial boundary not found")) };
+
+/*
+
+    if { 
+
+initial[start..].strip_prefixstarts_with(b"\n")
+    || initial[start..].starts_with(b"\r\n")*/
+
+    eprintln!("boundary={:?} initial={:?} start={}",
+              boundary, initial, start);
 
     Ok::<_,AE>(())
   }.await {
index b5323b7904accef01a88ad3fd9579a68b0574c49..443bd4b43341e2abf06669ba7e2ef764c74f06f6 100644 (file)
@@ -8,6 +8,7 @@ pub mod prelude;
 
 pub mod config;
 pub mod ipif;
+pub mod multipart;
 pub mod slip;
 pub mod reporter;
 pub mod queue;
diff --git a/src/multipart.rs b/src/multipart.rs
new file mode 100644 (file)
index 0000000..877d83a
--- /dev/null
@@ -0,0 +1,66 @@
+// Copyright 2021 Ian Jackson and contributors to Hippotat
+// SPDX-License-Identifier: GPL-3.0-or-later
+// There is NO WARRANTY.
+
+use crate::prelude::*;
+
+pub struct Component<'b> {
+  pub name: PartName,
+  pub payload_start: &'b [u8],
+}
+
+#[derive(Debug)]
+#[allow(non_camel_case_types)]
+pub enum PartName { m, d, Other }
+
+#[throws(AE)]
+pub fn process_component<'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         }
+  else if let Some(_  ) = rhs.strip_prefix(b"--"  ) { return None }
+  else if let Some(rhs) = rhs.strip_prefix(b"\n"  ) { rhs         }
+  else { throw!(anyhow!("invalid multipart delimiter")) };
+
+  let mut part_name = None;
+
+  loop {
+    // RHS points to the start of a header line
+    let nl = memchr::memchr(b'\n', rhs)
+      .ok_or_else(|| anyhow!("part headers truncated"))?;
+    let l = &rhs[0..nl]; rhs = &rhs[nl+1..];
+    if l == b"\r" || l == b"" { break } // end of headers
+    if l.starts_with(b"--") { throw!(anyhow!("boundary in part headers")) }
+
+    match (||{
+      let l = str::from_utf8(l).context("interpret part headers as utf-8")?;
+
+      let (_, disposition) = if let Some(y) =
+        regex_captures!(r#"^Content-Disposition[ \t]*:[ \t]*(.*)$"#i, l) { y }
+        else { return Ok(()) };
+
+      let disposition: mime::Mime = disposition.parse()
+        .context("parse Content-Disposition")?;
+      let name = disposition.get_param("name")
+        .ok_or_else(|| anyhow!(r#"find "name" in Content-Disposition"#))?;
+
+      let name = match name.as_ref() {
+        "m" => PartName::m,
+        "d" => PartName::d,
+        _   => PartName::Other,
+      };
+
+      if let Some(_) = mem::replace(&mut part_name, Some(name)) {
+        throw!(anyhow!(r#"multiple "name"s in Content-Disposition(s)"#))
+      }
+      Ok::<_,AE>(())
+    })() {
+      Err(e) => warnings.add(&e)?,
+      Ok(()) => { },
+    };
+  }
+
+  Some(Component { name: part_name.unwrap_or(expected), payload_start: rhs })
+}
index 0d53dbebb9600ab0fbb57e6632683102a4c391f3..f3e0de220c6b14dd48446522a3a98708abc1a483 100644 (file)
@@ -37,6 +37,7 @@ pub use itertools::{iproduct, Itertools};
 pub use lazy_regex::{regex_captures, regex_is_match, regex_replace_all};
 pub use lazy_static::lazy_static;
 pub use log::{trace, debug, info, warn, error};
+pub use memchr::memmem;
 pub use structopt::StructOpt;
 pub use thiserror::Error;
 pub use tokio::io::{AsyncBufReadExt, AsyncWriteExt};
index 0776dcee7d27f51bc3423d31465c9f406b36b54f..5b2093ba913979cc98cc924641a0f392e4c7da03 100644 (file)
@@ -196,3 +196,19 @@ pub fn dedup_eyre_setup() {
   }))
     .context("set error handler")?;
 }
+
+const MAX_WARNINGS: usize = 15;
+
+pub struct Warnings {
+  pub warnings: Vec<String>,
+}
+
+impl Warnings {
+  #[throws(AE)]
+  pub fn add(&mut self, e: &dyn Display) {
+    if self.warnings.len() >= MAX_WARNINGS {
+      throw!(anyhow!("too many warnings"))
+    }
+    self.warnings.push(e.to_string());
+  }
+}