carrier 0.12.2

carrier is a generic secure message system for IoT
use endpoint;
use headers;
use headers::Headers;
use identity;
use osaka::osaka;
use rand;
use sha2::{Digest, Sha256};
use std::fs::{rename, File};
use std::io::Write;
use std::path::Path;

pub fn main(
    poll: osaka::Poll,
    headers: headers::Headers,
    _: &identity::Identity,
    mut stream: endpoint::Stream,
) -> Option<osaka::Task<()>> {
    match headers.get(b":method") {
        Some(b"PUT") => match headers.get(b"file") {
            None => {
                let headers = Headers::with_error(400, "missing file header");
                stream.send(headers.encode());
                None
            }
            Some(path) => match headers.get(b"sha256") {
                None => {
                    let headers = Headers::with_error(400, "missing sha256 header");
                    stream.send(headers.encode());
                    None
                }
                Some(sha) => {
                    let path = String::from_utf8_lossy(path).into_owned();
                    Some(sft_(poll, stream, path, sha.to_vec()))
                }
            },
        },
        _ => {
            let headers = Headers::with_error(405, "method not supported");
            stream.send(headers.encode());
            None
        }
    }
}

#[osaka]
pub fn sft_(_poll: osaka::Poll, mut stream: endpoint::Stream, path: String, sha: Vec<u8>) {
    info!("file transfer started {}", path);

    let tmppath = loop {
        let tmppath = format!("{}.{}", path, rand::random::<u64>());
        if !Path::new(&tmppath).exists() {
            break tmppath;
        }
    };

    let mut file = match File::create(&tmppath) {
        Ok(file) => file,
        Err(e) => {
            let headers = Headers::with_error(404, format!("{}", e));
            stream.send(headers.encode());
            return;
        }
    };

    stream.send(Headers::with(":status", "100").encode());

    let mut hasher = Sha256::new();
    loop {
        let b = osaka::sync!(stream);
        if b.len() == 0 {
            break;
        }
        hasher.input(&b);
        file.write(&b).expect("file write");
    }
    drop(file);

    if hasher.result().to_vec() != sha {
        let headers = Headers::with_error(409, "sha mismatch");
        stream.send(headers.encode());
        return;
    }

    if let Err(e) = rename(&tmppath, &path) {
        let headers = Headers::with_error(409, format!("cannot move {} to {}: {}", tmppath, path, e));
        stream.send(headers.encode());
        return;
    }

    stream.send(Headers::ok().encode());
    info!("file transfer complete");
}