From: Ian Jackson Date: Sun, 8 Aug 2021 16:22:05 +0000 (+0100) Subject: server: wip X-Git-Tag: hippotat/1.0.0~210 X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ian/git?a=commitdiff_plain;h=53b875d35b3e24755902199532050b63b02a71c6;p=hippotat.git server: wip Signed-off-by: Ian Jackson --- diff --git a/Cargo.lock b/Cargo.lock index 0a7897e..ee063c2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -428,6 +428,7 @@ dependencies = [ "lazy-regex", "lazy_static", "log", + "memchr", "mime", "parking_lot", "regex", diff --git a/Cargo.toml b/Cargo.toml index 8d27fef..fb745c8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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"] } diff --git a/src/bin/server.rs b/src/bin/server.rs index 3724c45..be85bb5 100644 --- a/src/bin/server.rs +++ b/src/bin/server.rs @@ -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 { diff --git a/src/lib.rs b/src/lib.rs index b5323b7..443bd4b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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 index 0000000..877d83a --- /dev/null +++ b/src/multipart.rs @@ -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> { + 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 }) +} diff --git a/src/prelude.rs b/src/prelude.rs index 0d53dbe..f3e0de2 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -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}; diff --git a/src/reporter.rs b/src/reporter.rs index 0776dce..5b2093b 100644 --- a/src/reporter.rs +++ b/src/reporter.rs @@ -196,3 +196,19 @@ pub fn dedup_eyre_setup() { })) .context("set error handler")?; } + +const MAX_WARNINGS: usize = 15; + +pub struct Warnings { + pub warnings: Vec, +} + +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()); + } +}