<commit_before>// Copyright 2015-2016 ZFilexfer Developers. See the COPYRIGHT file at the
// top-level directory of this distribution and at
// https://intecture.io/COPYRIGHT.
//
// Licensed under the Mozilla Public License 2.0 <LICENSE or
// https://www.tldrlegal.com/l/mpl-2.0>. This file may not be copied,
// modified, or distributed except according to those terms.

use chunk::Chunk;
use czmq::{ZMsg, ZSock, ZSys};
use error::{Error, Result};
use std::rc::Rc;
use std::sync::{Arc, RwLock};
use std::thread::{JoinHandle, spawn};
use std::time::Instant;

#[cfg(not(test))]
const CHUNK_TIMEOUT: u64 = 60;
#[cfg(test)]
const CHUNK_TIMEOUT: u64 = 1;

pub struct Arbitrator {
    router: Rc<ZSock>,
    queue: Arc<RwLock<Vec<TimedChunk>>>,
    timer_handle: Option<JoinHandle<()>>,
    timer_comm: ZSock,
    slots: u32,
}

impl Drop for Arbitrator {
    fn drop(&mut self) {
        // Ignore failure as it means the thread has already
        // terminated.
        let _ = self.timer_comm.signal(0);
        if self.timer_handle.is_some() {
            self.timer_handle.take().unwrap().join().unwrap();
        }
    }
}

impl Arbitrator {
    pub fn new(router: Rc<ZSock>, upload_slots: u32) -> Result<Arbitrator> {
        let (comm_front, comm_back) = try!(ZSys::create_pipe());
        comm_front.set_sndtimeo(Some(1000));
        comm_back.set_rcvtimeo(Some(1000)); // Remember that this timeout controls the Timer loop speed!

        let lock = Arc::new(RwLock::new(Vec::new()));
        let timer = try!(Timer::new(comm_back, lock.clone()));

        Ok(Arbitrator {
            router: router,
            queue: lock,
            timer_handle: Some(spawn(move|| timer.run())),
            timer_comm: comm_front,
            slots: upload_slots,
        })
    }

    pub fn queue(&mut self, chunk: &Chunk, router_id: &[u8]) -> Result<()> {
        let timed_chunk = TimedChunk::new(router_id, chunk.get_index());
        {
            let mut writer = self.queue.write().unwrap();
            writer.push(timed_chunk);
        }

        try!(self.request());
        Ok(())
    }

    pub fn release(&mut self, chunk: &Chunk, router_id: &[u8]) -> Result<()> {
        let router_id = router_id.to_vec();
        {
            let mut queue = self.queue.write().unwrap();
            let mut index: Option<usize> = None;
            let mut x = 0;
            for c in queue.iter_mut() {
                if c.router_id == router_id && c.index == chunk.get_index() {
                    index = Some(x);
                    break;
                }

                x += 1;
            }

            match index {
                Some(i) => {
                    queue.remove(i);
                    self.slots += 1;
                },
                None => return Err(Error::ChunkIndex),
            }
        }

        try!(self.request());
        Ok(())
    }

    fn request(&mut self) -> Result<()> {
        for chunk in self.queue.write().unwrap().iter_mut() {
            if self.slots == 0 {
                break;
            }

            if !chunk.is_started() {
                self.slots -= 1;

                let msg = ZMsg::new();
                try!(msg.addbytes(&chunk.router_id));
                try!(msg.addstr("CHUNK"));
                try!(msg.addstr(&chunk.index.to_string()));
                try!(msg.send(&*self.router));

                chunk.start();
            }
        }

        Ok(())
    }
}

struct Timer {
    chunks: Arc<RwLock<Vec<TimedChunk>>>,
    sink: ZSock,
    comm: ZSock,
}

impl Timer {
    fn new(comm: ZSock, chunks: Arc<RwLock<Vec<TimedChunk>>>) -> Result<Timer> {
        Ok(Timer {
            chunks: chunks,
            sink: try!(ZSock::new_push(">inproc://zfilexfer_sink")),
            comm: comm,
        })
    }

    fn run(self) {
        loop {
            // Terminate on ZSock signal or system signal (SIGTERM)
            if self.comm.wait().is_ok() || ZSys::is_interrupted() {
                break;
            }

            for chunk in self.chunks.read().unwrap().iter() {
                if chunk.is_expired() {
                    let msg = ZMsg::new();
                    msg.addbytes(&chunk.router_id).unwrap();
                    msg.addstr(&chunk.index.to_string()).unwrap();
                    msg.addstr("0").unwrap();
                    msg.send(&self.sink).unwrap();
                }
            }
        }
    }
}

struct TimedChunk {
    router_id: Vec<u8>,
    index: u64,
    timestamp: Option<Instant>,
}

impl TimedChunk {
    fn new(router_id: &[u8], index: u64) -> TimedChunk {
        TimedChunk {
            router_id: router_id.to_vec(),
            index: index,
            timestamp: None,
        }
    }

    fn start(&mut self) {
        self.timestamp = Some(Instant::now());
    }

    fn is_started(&self) -> bool {
        self.timestamp.is_some()
    }

    fn is_expired(&self) -> bool {
        if self.timestamp.is_some() {
            self.timestamp.as_ref().unwrap().elapsed().as_secs() >= CHUNK_TIMEOUT
        } else {
            false
        }
    }
}

#[cfg(test)]
mod tests {
    use chunk::Chunk;
    use czmq::{ZMsg, ZSock, ZSockType, ZSys};
    use std::rc::Rc;
    use std::sync::{Arc, RwLock};
    use std::thread::{sleep, spawn};
    use std::time::{Duration, Instant};
    use super::*;
    use super::{TimedChunk, Timer};

    #[test]
    fn test_arbitrator_new() {
        ZSys::init();

        assert!(Arbitrator::new(Rc::new(ZSock::new(ZSockType::ROUTER)), 0).is_ok());
    }

    #[test]
    fn test_arbitrator_queue_release() {
        ZSys::init();

        let chunk = Chunk::test_new("/path/to/file", 0);

        let mut arbitrator = Arbitrator::new(Rc::new(ZSock::new(ZSockType::ROUTER)), 1).unwrap();
        assert!(arbitrator.queue(&chunk, "abc".as_bytes()).is_ok());
        assert_eq!(arbitrator.queue.read().unwrap().len(), 1);
        assert_eq!(arbitrator.slots, 0);
        assert!(arbitrator.release(&chunk, "abc".as_bytes()).is_ok());
        assert_eq!(arbitrator.queue.read().unwrap().len(), 0);
        assert_eq!(arbitrator.slots, 1);
    }

    #[test]
    fn test_arbitrator_request() {
        ZSys::init();

        let (client, router) = ZSys::create_pipe().unwrap();
        client.set_rcvtimeo(Some(500));

        let (comm, thread) = ZSys::create_pipe().unwrap();

        let chunks = vec![
            TimedChunk::new("abc".as_bytes(), 0),
            TimedChunk::new("abc".as_bytes(), 1),
            TimedChunk::new("abc".as_bytes(), 2),
            TimedChunk::new("def".as_bytes(), 0),
            TimedChunk::new("def".as_bytes(), 1),
            TimedChunk::new("def".as_bytes(), 2),
        ];

        {
            let mut arbitrator = Arbitrator {
                router: Rc::new(router),
                queue: Arc::new(RwLock::new(chunks)),
                timer_handle: None,
                timer_comm: comm,
                slots: 3,
            };

            arbitrator.request().unwrap();

            for x in 0..3 {
                let msg = ZMsg::recv(&client).unwrap();
                assert_eq!(&msg.popstr().unwrap().unwrap(), "abc");
                assert_eq!(&msg.popstr().unwrap().unwrap(), "CHUNK");
                assert_eq!(msg.popstr().unwrap().unwrap(), x.to_string());
            }

            assert!(client.recv_str().is_err());

            let chunk = Chunk::test_new("/path/to/file", 0);
            arbitrator.release(&chunk, "abc".as_bytes()).unwrap();
            arbitrator.request().unwrap();

            let msg = ZMsg::recv(&client).unwrap();
            assert_eq!(&msg.popstr().unwrap().unwrap(), "def");
            assert_eq!(&msg.popstr().unwrap().unwrap(), "CHUNK");
            assert_eq!(msg.popstr().unwrap().unwrap(), "0");
        }

        thread.wait().unwrap();
    }

    #[test]
    fn test_timer_new() {
        ZSys::init();

        assert!(Timer::new(ZSock::new(ZSockType::REQ), Arc::new(RwLock::new(Vec::new()))).is_ok());
    }

    #[test]
    fn test_timer_run() {
        ZSys::init();

        let (client, server) = ZSys::create_pipe().unwrap();
        let (comm, thread) = ZSys::create_pipe().unwrap();
        client.set_rcvtimeo(Some(1500));
        thread.set_rcvtimeo(Some(1000));

        let mut c = TimedChunk::new("abc".as_bytes(), 0);
        c.start();

        let timer = Timer {
            chunks: Arc::new(RwLock::new(vec![
                c,
            ])),
            sink: server,
            comm: thread,
        };
        let handle = spawn(|| timer.run());

        let msg = ZMsg::recv(&client).unwrap();
        assert_eq!(msg.popstr().unwrap().unwrap(), "abc");
        assert_eq!(msg.popstr().unwrap().unwrap(), "0");
        assert_eq!(msg.popstr().unwrap().unwrap(), "0");

        comm.signal(0).unwrap();
        handle.join().unwrap();
    }

    #[test]
    fn test_chunk_is_expired() {
        let timed = TimedChunk {
            router_id: vec![97, 98, 99],
            index: 0,
            timestamp: Some(Instant::now()),
        };

        sleep(Duration::from_secs(1));

        assert!(timed.is_expired());
    }
}
<commit_msg>Add sockopt linger<commit_after>// Copyright 2015-2016 ZFilexfer Developers. See the COPYRIGHT file at the
// top-level directory of this distribution and at
// https://intecture.io/COPYRIGHT.
//
// Licensed under the Mozilla Public License 2.0 <LICENSE or
// https://www.tldrlegal.com/l/mpl-2.0>. This file may not be copied,
// modified, or distributed except according to those terms.

use chunk::Chunk;
use czmq::{ZMsg, ZSock, ZSys};
use error::{Error, Result};
use std::rc::Rc;
use std::sync::{Arc, RwLock};
use std::thread::{JoinHandle, spawn};
use std::time::Instant;

#[cfg(not(test))]
const CHUNK_TIMEOUT: u64 = 60;
#[cfg(test)]
const CHUNK_TIMEOUT: u64 = 1;

pub struct Arbitrator {
    router: Rc<ZSock>,
    queue: Arc<RwLock<Vec<TimedChunk>>>,
    timer_handle: Option<JoinHandle<()>>,
    timer_comm: ZSock,
    slots: u32,
}

impl Drop for Arbitrator {
    fn drop(&mut self) {
        // Ignore failure as it means the thread has already
        // terminated.
        let _ = self.timer_comm.signal(0);
        if self.timer_handle.is_some() {
            self.timer_handle.take().unwrap().join().unwrap();
        }
    }
}

impl Arbitrator {
    pub fn new(router: Rc<ZSock>, upload_slots: u32) -> Result<Arbitrator> {
        let (comm_front, comm_back) = try!(ZSys::create_pipe());
        comm_front.set_sndtimeo(Some(1000));
        comm_front.set_linger(0);
        comm_back.set_rcvtimeo(Some(1000)); // Remember that this timeout controls the Timer loop speed!
        comm_back.set_linger(0);

        let lock = Arc::new(RwLock::new(Vec::new()));
        let timer = try!(Timer::new(comm_back, lock.clone()));

        Ok(Arbitrator {
            router: router,
            queue: lock,
            timer_handle: Some(spawn(move|| timer.run())),
            timer_comm: comm_front,
            slots: upload_slots,
        })
    }

    pub fn queue(&mut self, chunk: &Chunk, router_id: &[u8]) -> Result<()> {
        let timed_chunk = TimedChunk::new(router_id, chunk.get_index());
        {
            let mut writer = self.queue.write().unwrap();
            writer.push(timed_chunk);
        }

        try!(self.request());
        Ok(())
    }

    pub fn release(&mut self, chunk: &Chunk, router_id: &[u8]) -> Result<()> {
        let router_id = router_id.to_vec();
        {
            let mut queue = self.queue.write().unwrap();
            let mut index: Option<usize> = None;
            let mut x = 0;
            for c in queue.iter_mut() {
                if c.router_id == router_id && c.index == chunk.get_index() {
                    index = Some(x);
                    break;
                }

                x += 1;
            }

            match index {
                Some(i) => {
                    queue.remove(i);
                    self.slots += 1;
                },
                None => return Err(Error::ChunkIndex),
            }
        }

        try!(self.request());
        Ok(())
    }

    fn request(&mut self) -> Result<()> {
        for chunk in self.queue.write().unwrap().iter_mut() {
            if self.slots == 0 {
                break;
            }

            if !chunk.is_started() {
                self.slots -= 1;

                let msg = ZMsg::new();
                try!(msg.addbytes(&chunk.router_id));
                try!(msg.addstr("CHUNK"));
                try!(msg.addstr(&chunk.index.to_string()));
                try!(msg.send(&*self.router));

                chunk.start();
            }
        }

        Ok(())
    }
}

struct Timer {
    chunks: Arc<RwLock<Vec<TimedChunk>>>,
    sink: ZSock,
    comm: ZSock,
}

impl Timer {
    fn new(comm: ZSock, chunks: Arc<RwLock<Vec<TimedChunk>>>) -> Result<Timer> {
        Ok(Timer {
            chunks: chunks,
            sink: try!(ZSock::new_push(">inproc://zfilexfer_sink")),
            comm: comm,
        })
    }

    fn run(self) {
        loop {
            // Terminate on ZSock signal or system signal (SIGTERM)
            if self.comm.wait().is_ok() || ZSys::is_interrupted() {
                break;
            }

            for chunk in self.chunks.read().unwrap().iter() {
                if chunk.is_expired() {
                    let msg = ZMsg::new();
                    msg.addbytes(&chunk.router_id).unwrap();
                    msg.addstr(&chunk.index.to_string()).unwrap();
                    msg.addstr("0").unwrap();
                    msg.send(&self.sink).unwrap();
                }
            }
        }
    }
}

struct TimedChunk {
    router_id: Vec<u8>,
    index: u64,
    timestamp: Option<Instant>,
}

impl TimedChunk {
    fn new(router_id: &[u8], index: u64) -> TimedChunk {
        TimedChunk {
            router_id: router_id.to_vec(),
            index: index,
            timestamp: None,
        }
    }

    fn start(&mut self) {
        self.timestamp = Some(Instant::now());
    }

    fn is_started(&self) -> bool {
        self.timestamp.is_some()
    }

    fn is_expired(&self) -> bool {
        if self.timestamp.is_some() {
            self.timestamp.as_ref().unwrap().elapsed().as_secs() >= CHUNK_TIMEOUT
        } else {
            false
        }
    }
}

#[cfg(test)]
mod tests {
    use chunk::Chunk;
    use czmq::{ZMsg, ZSock, ZSockType, ZSys};
    use std::rc::Rc;
    use std::sync::{Arc, RwLock};
    use std::thread::{sleep, spawn};
    use std::time::{Duration, Instant};
    use super::*;
    use super::{TimedChunk, Timer};

    #[test]
    fn test_arbitrator_new() {
        ZSys::init();

        assert!(Arbitrator::new(Rc::new(ZSock::new(ZSockType::ROUTER)), 0).is_ok());
    }

    #[test]
    fn test_arbitrator_queue_release() {
        ZSys::init();

        let chunk = Chunk::test_new("/path/to/file", 0);

        let mut arbitrator = Arbitrator::new(Rc::new(ZSock::new(ZSockType::ROUTER)), 1).unwrap();
        assert!(arbitrator.queue(&chunk, "abc".as_bytes()).is_ok());
        assert_eq!(arbitrator.queue.read().unwrap().len(), 1);
        assert_eq!(arbitrator.slots, 0);
        assert!(arbitrator.release(&chunk, "abc".as_bytes()).is_ok());
        assert_eq!(arbitrator.queue.read().unwrap().len(), 0);
        assert_eq!(arbitrator.slots, 1);
    }

    #[test]
    fn test_arbitrator_request() {
        ZSys::init();

        let (client, router) = ZSys::create_pipe().unwrap();
        client.set_rcvtimeo(Some(500));

        let (comm, thread) = ZSys::create_pipe().unwrap();

        let chunks = vec![
            TimedChunk::new("abc".as_bytes(), 0),
            TimedChunk::new("abc".as_bytes(), 1),
            TimedChunk::new("abc".as_bytes(), 2),
            TimedChunk::new("def".as_bytes(), 0),
            TimedChunk::new("def".as_bytes(), 1),
            TimedChunk::new("def".as_bytes(), 2),
        ];

        {
            let mut arbitrator = Arbitrator {
                router: Rc::new(router),
                queue: Arc::new(RwLock::new(chunks)),
                timer_handle: None,
                timer_comm: comm,
                slots: 3,
            };

            arbitrator.request().unwrap();

            for x in 0..3 {
                let msg = ZMsg::recv(&client).unwrap();
                assert_eq!(&msg.popstr().unwrap().unwrap(), "abc");
                assert_eq!(&msg.popstr().unwrap().unwrap(), "CHUNK");
                assert_eq!(msg.popstr().unwrap().unwrap(), x.to_string());
            }

            assert!(client.recv_str().is_err());

            let chunk = Chunk::test_new("/path/to/file", 0);
            arbitrator.release(&chunk, "abc".as_bytes()).unwrap();
            arbitrator.request().unwrap();

            let msg = ZMsg::recv(&client).unwrap();
            assert_eq!(&msg.popstr().unwrap().unwrap(), "def");
            assert_eq!(&msg.popstr().unwrap().unwrap(), "CHUNK");
            assert_eq!(msg.popstr().unwrap().unwrap(), "0");
        }

        thread.wait().unwrap();
    }

    #[test]
    fn test_timer_new() {
        ZSys::init();

        assert!(Timer::new(ZSock::new(ZSockType::REQ), Arc::new(RwLock::new(Vec::new()))).is_ok());
    }

    #[test]
    fn test_timer_run() {
        ZSys::init();

        let (client, server) = ZSys::create_pipe().unwrap();
        let (comm, thread) = ZSys::create_pipe().unwrap();
        client.set_rcvtimeo(Some(1500));
        thread.set_rcvtimeo(Some(1000));

        let mut c = TimedChunk::new("abc".as_bytes(), 0);
        c.start();

        let timer = Timer {
            chunks: Arc::new(RwLock::new(vec![
                c,
            ])),
            sink: server,
            comm: thread,
        };
        let handle = spawn(|| timer.run());

        let msg = ZMsg::recv(&client).unwrap();
        assert_eq!(msg.popstr().unwrap().unwrap(), "abc");
        assert_eq!(msg.popstr().unwrap().unwrap(), "0");
        assert_eq!(msg.popstr().unwrap().unwrap(), "0");

        comm.signal(0).unwrap();
        handle.join().unwrap();
    }

    #[test]
    fn test_chunk_is_expired() {
        let timed = TimedChunk {
            router_id: vec![97, 98, 99],
            index: 0,
            timestamp: Some(Instant::now()),
        };

        sleep(Duration::from_secs(1));

        assert!(timed.is_expired());
    }
}
<|endoftext|>


<commit_before>#![deny(warnings)]

// TODO support reading from standard input

extern crate extra;
extern crate termion;

use std::cmp::max;
use std::env::args;
use std::fs::File;
use std::io::{self, Write, Read, StdoutLock, Stderr};
use std::path::Path;

use extra::option::OptionalExt;

use termion::{terminal_size, TermRead, TermWrite, IntoRawMode, Color, Key, RawTerminal, Style};

static LONG_HELP: &'static str = /* @MANSTART{mdless} */ r#"
NAME
    mdless- view a markdown file.

SYNOPSIS
    mdless [-h | --help] [input]

DESCRIPTION
    This utility views md files. If no input file is specified as an argument, standard input is
    used.

OPTIONS
    --help, -h
        Print this manual page.

AUTHOR
    This program was written by MovingtoMars and Jeremy Soller for Redox OS. Bugs, issues, or feature requests should
    be reported in the Github repository, 'redox-os/extrautils'.

COPYRIGHT
    Copyright (c) 2016 MovingtoMars, Jeremy Soller

    Permission is hereby granted, free of charge, to any person obtaining a copy of this software
    and associated documentation files (the "Software"), to deal in the Software without
    restriction, including without limitation the rights to use, copy, modify, merge, publish,
    distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
    Software is furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included in all copies or
    substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
    BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
    DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
"#; /* @MANEND */

fn main() {
    let mut args = args().skip(1);
    let stdout = io::stdout();
    let mut stdout = stdout.lock();
    let stdin = io::stdin();
    let mut stdin = stdin.lock();
    let mut stderr = io::stderr();

    if let Some(x) = args.next() {
        match x.as_str() {
            "--help" | "-h" => {
                // Print help.
                stdout.write(LONG_HELP.as_bytes()).try(&mut stderr);
                return;
            },
            filename => {
                let mut file = File::open(Path::new(filename)).try(&mut stderr);
                run(filename, &mut file, &mut stdin, &mut stdout, &mut stderr);
            }
        }

        if let Some(x) = args.next() {
            let mut file = File::open(Path::new(x.as_str())).try(&mut stderr);
            run(x.as_str(), &mut file, &mut stdin, &mut stdout, &mut stderr);
        }
    } else {
        writeln!(stderr, "Readin from stdin is not yet supported").try(&mut stderr);
        //run(&mut stdin, &mut stdin, &mut stdout, &mut stderr);
    };
}

struct Block {
    c: char,
    bold: bool,
    italic: bool,
    code: bool,
    link: bool
}

impl Block {
    fn parse(s: &str) -> Vec<Block> {
        let mut blocks = Vec::new();

        let mut bold = 0;
        let mut bolding = 0;
        let mut code = 0;
        let mut coding = 0;
        let mut link = 0;

        for (_i, c) in s.char_indices() {
            if c == '*' {
                if bolding == 0 {
                    if bold > 0 {
                        bolding = -1;
                    }else {
                        bolding = 1;
                    }
                }
            } else {
                bolding = 0;
            }

            if bolding > 0 {
                bold += bolding;
            }

            if c == '`' {
                if coding == 0 {
                    if code > 0 {
                        coding = -1;
                    }else {
                        coding = 1;
                    }
                }
            } else {
                coding = 0;
            }

            if coding > 0 {
                code += coding;
            }

            if c == '[' {
                link += 1;
            }

            blocks.push(Block {
                c: c,
                bold: bold >= 2,
                italic: bold == 1 || bold >= 3,
                code: code > 0,
                link: link > 0
            });

            if bolding < 0 {
                bold = max(0, bold + bolding);
            }

            if coding < 0 {
                code = max(0, code + coding);
            }

            if c == ']' {
                link = max(0, link - 1);
            }
        }

        blocks
    }

    //TODO: UTF8
    fn draw(&self, to: &mut RawTerminal<&mut StdoutLock>) -> std::io::Result<usize> {
        if self.bold {
            try!(to.style(Style::Bold));
        }

        if self.italic {
            try!(to.style(Style::Italic));
        }

        if self.code {
            try!(to.style(Style::Invert));
        }

        if self.link {
            try!(to.style(Style::Underline));
        }

        try!(to.write(&[self.c as u8]));

        if self.bold {
            try!(to.style(Style::NoBold));
        }

        if self.italic {
            try!(to.style(Style::NoItalic));
        }

        if self.code {
            try!(to.style(Style::NoInvert));
        }

        if self.link {
            try!(to.style(Style::NoUnderline));
        }

        Ok(1)
    }
}

struct Buffer {
    lines: Vec<Vec<Block>>,
    y_off: u16,
}

impl Buffer {
    fn new() -> Buffer {
        Buffer{
            lines: Vec::new(),
            y_off: 0,
        }
    }

    fn read(&mut self, from: &mut Read) -> std::io::Result<usize> {
        let mut tmp = String::new();
        let res = from.read_to_string(&mut tmp);
        try!(res);

        self.lines.append(&mut tmp
            .as_str()
            .split("\n")
            .map(|x| { Block::parse(x) })
            .collect());

        return res;
    }

    fn draw(&self, to: &mut RawTerminal<&mut StdoutLock>, w: u16, h: u16) -> std::io::Result<usize> {
        try!(to.goto(0, 0));

        let mut y = 0;
        let mut bytes_written = 0;

        let lines = if self.lines.len() <= h as usize {
            &self.lines
        } else if self.y_off as usize >= self.lines.len() {
            &self.lines[0..0]
        } else {
            &self.lines[self.y_off as usize..]
        };

        for line in lines {
            if line.len() > w as usize {
                for block in line[.. w as usize].iter() {
                    bytes_written += try!(block.draw(to));
                }
            } else {
                for block in line.iter() {
                    bytes_written += try!(block.draw(to));
                }
            }

            y += 1;
            try!(to.goto(0, y));
            if y >= h {
                break;
            }
        }

        Ok(bytes_written)
    }

    fn scroll_up(&mut self) {
        if self.y_off > 0 {
            self.y_off -= 1;
        }
    }

    fn scroll_down(&mut self, height: u16) {
        if ((self.y_off + height) as usize) <= self.lines.len() {
            self.y_off += 1;
        }
    }
}

fn run(path: &str, file: &mut Read, controls: &mut Read, stdout: &mut StdoutLock, stderr: &mut Stderr) {
    let mut stdout = stdout.into_raw_mode().try(stderr);

    let (w, h) = {
        let (w, h) = terminal_size().try(stderr);
        (w as u16, h as u16)
    };

    stdout.clear().try(stderr);
    stdout.reset().try(stderr);

    let mut buffer = Buffer::new();
    buffer.read(file).try(stderr);

    buffer.draw(&mut stdout, w, h - 1).try(stderr);
    stdout.goto(0, h - 1).try(stderr);
    stdout.bg_color(Color::White).try(stderr);
    stdout.color(Color::Black).try(stderr);
    stdout.write(path.as_bytes()).try(stderr);
    stdout.write(b" Press q to exit.").try(stderr);
    stdout.reset().try(stderr);
    stdout.flush().try(stderr);

    for c in controls.keys() {
        match c.unwrap() {
            Key::Char('q') => {
                stdout.clear().try(stderr);
                stdout.reset().try(stderr);
                stdout.goto(0, 0).try(stderr);
                return
            },
            Key::Char('b') => for _i in 1..h {
                buffer.scroll_up()
            },
            Key::Char(' ') => for _i in 1..h {
                buffer.scroll_down(h)
            },
            Key::Up | Key::Char('k') => buffer.scroll_up(),
            Key::Down | Key::Char('j') => buffer.scroll_down(h),
            _ => {},
        }

        stdout.clear().try(stderr);
        stdout.reset().try(stderr);
        buffer.draw(&mut stdout, w, h - 1).try(stderr);
        stdout.goto(0, h - 1).try(stderr);
        stdout.bg_color(Color::White).try(stderr);
        stdout.color(Color::Black).try(stderr);
        stdout.write(path.as_bytes()).try(stderr);
        stdout.write(b" Press q to exit.").try(stderr);
        stdout.reset().try(stderr);
        stdout.flush().try(stderr);
    }
}
<commit_msg>Switch method of parsing in mdless<commit_after>#![deny(warnings)]

// TODO support reading from standard input

extern crate extra;
extern crate termion;

use std::env::args;
use std::fs::File;
use std::io::{self, Write, Read, StdoutLock, Stderr};
use std::path::{Path, PathBuf};
use std::str::Chars;

use extra::option::OptionalExt;

use termion::{terminal_size, TermRead, TermWrite, IntoRawMode, Color, Key, RawTerminal, Style};

static LONG_HELP: &'static str = /* @MANSTART{mdless} */ r#"
NAME
    mdless- view a markdown file.

SYNOPSIS
    mdless [-h | --help] [input]

DESCRIPTION
    This utility views md files. If no input file is specified as an argument, standard input is
    used.

OPTIONS
    --help, -h
        Print this manual page.

AUTHOR
    This program was written by MovingtoMars and Jeremy Soller for Redox OS. Bugs, issues, or feature requests should
    be reported in the Github repository, 'redox-os/extrautils'.

COPYRIGHT
    Copyright (c) 2016 MovingtoMars, Jeremy Soller

    Permission is hereby granted, free of charge, to any person obtaining a copy of this software
    and associated documentation files (the "Software"), to deal in the Software without
    restriction, including without limitation the rights to use, copy, modify, merge, publish,
    distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
    Software is furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included in all copies or
    substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
    BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
    DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
"#; /* @MANEND */

fn main() {
    let mut args = args().skip(1);
    let stdout = io::stdout();
    let mut stdout = stdout.lock();
    let stdin = io::stdin();
    let mut stdin = stdin.lock();
    let mut stderr = io::stderr();

    if let Some(x) = args.next() {
        match x.as_str() {
            "--help" | "-h" => {
                // Print help.
                stdout.write(LONG_HELP.as_bytes()).try(&mut stderr);
                return;
            },
            filename => {
                let mut file = File::open(Path::new(filename)).try(&mut stderr);
                run(filename, &mut file, &mut stdin, &mut stdout, &mut stderr);
            }
        }

        if let Some(x) = args.next() {
            let mut file = File::open(Path::new(x.as_str())).try(&mut stderr);
            run(x.as_str(), &mut file, &mut stdin, &mut stdout, &mut stderr);
        }
    } else {
        writeln!(stderr, "Readin from stdin is not yet supported").try(&mut stderr);
        //run(&mut stdin, &mut stdin, &mut stdout, &mut stderr);
    };
}

//TODO: String in Text
enum Block {
    Text(char),
    Bold(Vec<Block>),
    Italic(Vec<Block>),
    Code(Vec<Block>),
    Link(Vec<Block>, String),
}

impl Block {
    fn parse_bold(s: &mut Chars) -> Block {
        let mut blocks = Vec::new();

        while let Some(c) = s.next() {
            if c == '*' && s.as_str().chars().next() == Some('*') {
                let _ = s.next();
                break;
            } else {
                blocks.push(Block::Text(c))
            }
        }

        Block::Bold(blocks)
    }

    fn parse_italic(s: &mut Chars) -> Block {
        let mut blocks = Vec::new();

        while let Some(c) = s.next() {
            if c == '*' {
                break;
            } else {
                blocks.push(Block::Text(c))
            }
        }

        Block::Italic(blocks)
    }

    fn parse_code(s: &mut Chars) -> Block {
        let mut blocks = Vec::new();

        while let Some(c) = s.next() {
            if c == '`' {
                break;
            } else {
                blocks.push(Block::Text(c))
            }
        }

        Block::Code(blocks)
    }

    fn parse_link(s: &mut Chars) -> Block {
        let mut blocks = Vec::new();
        let mut link = String::new();

        while let Some(c) = s.next() {
            match c {
                ']' => break,
                _ => blocks.push(Block::Text(c))
            }
        }

        if s.as_str().chars().next() == Some('(') {
            while let Some(c) = s.next() {
                match c {
                    '(' => (),
                    ')' => break,
                    _ => link.push(c)
                }
            }
        }

        Block::Link(blocks, link)
    }

    fn parse(s: &mut Chars) -> Vec<Block> {
        let mut blocks = Vec::new();

        while let Some(c) = s.next() {
            match c {
                '*' => if s.as_str().chars().next() == Some('*') {
                    let _ = s.next();
                    blocks.push(Block::parse_bold(s));
                } else {
                    blocks.push(Block::parse_italic(s));
                },
                '`' => blocks.push(Block::parse_code(s)),
                '[' => blocks.push(Block::parse_link(s)),
                _ => blocks.push(Block::Text(c))
            }
        }

        blocks
    }

    fn draw(&self, to: &mut RawTerminal<&mut StdoutLock>) -> std::io::Result<usize> {
        let mut res = 0;

        match *self {
            Block::Text(c) => {
                res += try!(to.write(&[c as u8]));
            },
            Block::Bold(ref blocks) => {
                try!(to.style(Style::Bold));
                for block in blocks.iter() {
                    res += try!(block.draw(to));
                }
                try!(to.style(Style::NoBold));
            },
            Block::Italic(ref blocks) => {
                try!(to.style(Style::Italic));
                for block in blocks.iter() {
                    res += try!(block.draw(to));
                }
                try!(to.style(Style::NoItalic));
            },
            Block::Code(ref blocks) => {
                try!(to.style(Style::Invert));
                for block in blocks.iter() {
                    res += try!(block.draw(to));
                }
                try!(to.style(Style::NoInvert));
            },
            Block::Link(ref blocks, ref _link) => {
                try!(to.style(Style::Underline));
                for block in blocks.iter() {
                    res += try!(block.draw(to));
                }
                try!(to.style(Style::NoUnderline));
            },
        }

        Ok(res)
    }

    fn enter(&self, path: &str) -> Option<PathBuf> {
        match *self {
            Block::Text(_c) => {},
            Block::Bold(ref blocks) => {
                for block in blocks.iter() {
                    if let Some(ret) = block.enter(path) {
                        return Some(ret);
                    }
                }
            },
            Block::Italic(ref blocks) => {
                for block in blocks.iter() {
                    if let Some(ret) = block.enter(path) {
                        return Some(ret);
                    }
                }
            },
            Block::Code(ref blocks) => {
                for block in blocks.iter() {
                    if let Some(ret) = block.enter(path) {
                        return Some(ret);
                    }
                }
            },
            Block::Link(ref _blocks, ref link) => {
                let mut ret = PathBuf::from(path);
                ret.pop();
                ret.push(link);
                return Some(ret);
            },
        }

        None
    }
}

struct Buffer {
    lines: Vec<Vec<Block>>,
    y_off: u16,
}

impl Buffer {
    fn new() -> Buffer {
        Buffer{
            lines: Vec::new(),
            y_off: 0,
        }
    }

    fn read(&mut self, from: &mut Read) -> std::io::Result<usize> {
        let mut tmp = String::new();
        let res = from.read_to_string(&mut tmp);
        try!(res);

        self.lines.append(&mut tmp
            .as_str()
            .split("\n")
            .map(|x| { Block::parse(&mut x.chars()) })
            .collect());

        return res;
    }

    fn draw(&self, to: &mut RawTerminal<&mut StdoutLock>, w: u16, h: u16) -> std::io::Result<usize> {
        try!(to.goto(0, 0));

        let mut y = 0;
        let mut bytes_written = 0;

        let lines = if self.lines.len() <= h as usize {
            &self.lines
        } else if self.y_off as usize >= self.lines.len() {
            &self.lines[0..0]
        } else {
            &self.lines[self.y_off as usize..]
        };

        for line in lines {
            if line.len() > w as usize {
                for block in line[.. w as usize].iter() {
                    bytes_written += try!(block.draw(to));
                }
            } else {
                for block in line.iter() {
                    bytes_written += try!(block.draw(to));
                }
            }

            y += 1;
            try!(to.goto(0, y));
            if y >= h {
                break;
            }
        }

        Ok(bytes_written)
    }

    fn enter(&mut self, path: &str, w: u16, h: u16) -> Option<PathBuf> {
        let mut y = 0;

        let lines = if self.lines.len() <= h as usize {
            &self.lines
        } else if self.y_off as usize >= self.lines.len() {
            &self.lines[0..0]
        } else {
            &self.lines[self.y_off as usize..]
        };

        for line in lines {
            if line.len() > w as usize {
                for block in line[.. w as usize].iter() {
                    if let Some(ret) = block.enter(path) {
                        return Some(ret);
                    }
                }
            } else {
                for block in line.iter() {
                    if let Some(ret) = block.enter(path) {
                        return Some(ret);
                    }
                }
            }

            y += 1;
            if y >= h {
                break;
            }
        }

        None
    }

    fn scroll_up(&mut self) {
        if self.y_off > 0 {
            self.y_off -= 1;
        }
    }

    fn scroll_down(&mut self, height: u16) {
        if ((self.y_off + height) as usize) <= self.lines.len() {
            self.y_off += 1;
        }
    }
}

fn run(path: &str, file: &mut Read, controls: &mut Read, stdout: &mut StdoutLock, stderr: &mut Stderr) {
    let mut stdout = stdout.into_raw_mode().try(stderr);

    let (w, h) = {
        let (w, h) = terminal_size().try(stderr);
        (w as u16, h as u16)
    };

    stdout.clear().try(stderr);
    stdout.reset().try(stderr);

    let mut buffer = Buffer::new();
    buffer.read(file).try(stderr);

    buffer.draw(&mut stdout, w, h - 1).try(stderr);
    stdout.goto(0, h - 1).try(stderr);
    stdout.bg_color(Color::White).try(stderr);
    stdout.color(Color::Black).try(stderr);
    stdout.write(path.as_bytes()).try(stderr);
    stdout.write(b" Press q to exit.").try(stderr);
    stdout.reset().try(stderr);
    stdout.flush().try(stderr);

    for c in controls.keys() {
        match c.unwrap() {
            Key::Char('q') => {
                stdout.clear().try(stderr);
                stdout.reset().try(stderr);
                stdout.goto(0, 0).try(stderr);
                return
            },
            Key::Char('b') => for _i in 1..h {
                buffer.scroll_up()
            },
            Key::Char(' ') => for _i in 1..h {
                buffer.scroll_down(h)
            },
            Key::Up | Key::Char('k') => buffer.scroll_up(),
            Key::Down | Key::Char('j') => buffer.scroll_down(h),
            Key::Char('\r') | Key::Char('\n') => {
                if let Some(link_path) = buffer.enter(path, w, h - 1) {
                    let mut new_file = File::open(link_path).try(stderr);
                    buffer = Buffer::new();
                    buffer.read(&mut new_file).try(stderr);
                }
            }
            _ => {},
        }

        stdout.clear().try(stderr);
        stdout.reset().try(stderr);
        buffer.draw(&mut stdout, w, h - 1).try(stderr);
        stdout.goto(0, h - 1).try(stderr);
        stdout.bg_color(Color::White).try(stderr);
        stdout.color(Color::Black).try(stderr);
        stdout.write(path.as_bytes()).try(stderr);
        stdout.write(b" Press q to exit.").try(stderr);
        stdout.reset().try(stderr);
        stdout.flush().try(stderr);
    }
}
<|endoftext|>


<commit_before>#![feature(exit_status)]

extern crate rustic;

use std::env;

fn main() {
    match rustic::run() {
        Err(e) => {
            println!("{}", e);
            env::set_exit_status(1);
        },
        Ok(Some(code)) => {
            env::set_exit_status(code)
        },
        _ => {},
    }
}
<commit_msg>use `std::process::exit()`<commit_after>#![feature(exit_status)]

extern crate rustic;

use std::env;
use std::process::exit;

fn main() {
    match rustic::run() {
        Err(e) => {
            println!("{}", e);
            exit(1);
        },
        Ok(Some(code)) => {
            exit(code)
        },
        _ => {},
    }
}
<|endoftext|>


<commit_before>// Copyright 2017 int08h LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//!
//! Roughtime server
//!
//! # Configuration
//! The `roughenough` server is configured via a config file:
//!
//! ```yaml
//! interface: 127.0.0.1
//! port: 8686
//! seed: f61075c988feb9cb700a4a6a3291bfbc9cab11b9c9eca8c802468eb38a43d7d3
//! ```
//!
//! Where:
//!
//!   * **interface** - IP address or interface name for listening to client requests
//!   * **port** - UDP port to listen to requests
//!   * **seed** - A 32-byte hexadecimal value used as the seed to generate the
//!                server's long-term key pair. **This is a secret value**, treat it
//!                with care.
//!
//! # Running the Server
//!
//! ```bash
//! $ cargo run --release --bin server /path/to/config.file
//! ```

extern crate byteorder;
extern crate core;
extern crate ring;
extern crate roughenough;
extern crate time;
extern crate untrusted;
extern crate fern;
extern crate ctrlc;
extern crate yaml_rust;
#[macro_use]
extern crate log;

use std::env;
use std::io;
use std::process;
use std::fs::File;
use std::io::Read;
use std::net::UdpSocket;
use std::time::Duration;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};

use byteorder::{LittleEndian, WriteBytesExt};

use roughenough::{RtMessage, Tag, Error};
use roughenough::{CERTIFICATE_CONTEXT, MIN_REQUEST_LENGTH, SIGNED_RESPONSE_CONTEXT, TREE_LEAF_TWEAK};
use roughenough::hex::*;
use roughenough::sign::Signer;

use ring::{digest, rand};
use ring::rand::SecureRandom;

use yaml_rust::YamlLoader;

const SERVER_VERSION: &'static str = "0.1.1";

fn create_ephemeral_key() -> Signer {
    let rng = rand::SystemRandom::new();
    let mut seed = [0u8; 32];
    rng.fill(&mut seed).unwrap();

    Signer::new(&seed)
}

fn make_dele_bytes(ephemeral_key: &Signer) -> Result<Vec<u8>, Error> {
    let zeros = [0u8; 8];
    let max = [0xff; 8];

    let mut dele_msg = RtMessage::new(3);
    dele_msg.add_field(Tag::PUBK, ephemeral_key.public_key_bytes())?;
    dele_msg.add_field(Tag::MINT, &zeros)?;
    dele_msg.add_field(Tag::MAXT, &max)?;

    dele_msg.encode()
}

fn make_cert(long_term_key: &mut Signer, ephemeral_key: &Signer) -> RtMessage {
    // Make DELE and sign it with long-term key
    let dele_bytes = make_dele_bytes(&ephemeral_key).unwrap();
    let dele_signature = {
        long_term_key.update(CERTIFICATE_CONTEXT.as_bytes());
        long_term_key.update(&dele_bytes);
        long_term_key.sign()
    };

    // Create CERT
    let mut cert_msg = RtMessage::new(2);
    cert_msg.add_field(Tag::SIG, &dele_signature).unwrap();
    cert_msg.add_field(Tag::DELE, &dele_bytes).unwrap();

    cert_msg
}

fn make_response(ephemeral_key: &mut Signer, cert_bytes: &[u8], nonce: &[u8]) -> RtMessage {
    //   create SREP
    //   sign SREP
    //   create response:
    //    - SIG
    //    - PATH (always 0)
    //    - SREP
    //    - CERT (pre-created)
    //    - INDX (always 0)

    let path = [0u8; 0];
    let zeros = [0u8; 4];

    let mut radi: Vec<u8> = Vec::with_capacity(4);
    let mut midp: Vec<u8> = Vec::with_capacity(8);

    // one second (in microseconds)
    radi.write_u32::<LittleEndian>(1000000).unwrap();

    // current epoch time in microseconds
    let now = {
        let tv = time::get_time();
        let secs = (tv.sec as u64) * 1000000;
        let nsecs = (tv.nsec as u64) / 1000;

        secs + nsecs
    };
    midp.write_u64::<LittleEndian>(now).unwrap();

    // Signed response SREP
    let srep_bytes = {
        // hash request nonce
        let mut ctx = digest::Context::new(&digest::SHA512);
        ctx.update(&TREE_LEAF_TWEAK);
        ctx.update(&nonce);
        let digest = ctx.finish();

        let mut srep_msg = RtMessage::new(3);
        srep_msg.add_field(Tag::RADI, &radi).unwrap();
        srep_msg.add_field(Tag::MIDP, &midp).unwrap();
        srep_msg.add_field(Tag::ROOT, digest.as_ref()).unwrap();

        srep_msg.encode().unwrap()
    };

    // signature on SREP
    let srep_signature = {
        ephemeral_key.update(SIGNED_RESPONSE_CONTEXT.as_bytes());
        ephemeral_key.update(&srep_bytes);
        ephemeral_key.sign()
    };

    let mut response = RtMessage::new(5);
    response.add_field(Tag::SIG, &srep_signature).unwrap();
    response.add_field(Tag::PATH, &path).unwrap();
    response.add_field(Tag::SREP, &srep_bytes).unwrap();
    response.add_field(Tag::CERT, cert_bytes).unwrap();
    response.add_field(Tag::INDX, &zeros).unwrap();

    response
}

// extract the client's nonce from its request
fn nonce_from_request(buf: &[u8], num_bytes: usize) -> Result<&[u8], Error> {
    if num_bytes < MIN_REQUEST_LENGTH as usize {
        return Err(Error::RequestTooShort);
    }

    let tag_count = &buf[..4];
    let expected_nonc = &buf[8..12];
    let expected_pad = &buf[12..16];

    let tag_count_is_2 = tag_count == [0x02, 0x00, 0x00, 0x00];
    let tag1_is_nonc = expected_nonc == Tag::NONC.wire_value();
    let tag2_is_pad = expected_pad == Tag::PAD.wire_value();

    if tag_count_is_2 && tag1_is_nonc && tag2_is_pad {
        Ok(&buf[0x10..0x50])
    } else {
        Err(Error::InvalidRequest)
    }
}

fn init_logging() {
    fern::Dispatch::new()
        .format(|out, message, record| {
            out.finish(
                format_args!("{} [{}] {}", time::now().rfc3339(), record.level(), message)
            )
        })
        .level(log::LogLevelFilter::Info)
        .chain(std::io::stdout())
        .apply()
        .unwrap();
}

fn load_config(config_file: &str) -> (String, u16, Vec<u8>) {
    let mut infile = File::open(config_file)
        .expect("failed to open config file");

    let mut contents = String::new();
    infile.read_to_string(&mut contents)
        .expect("could not read config file");

    let cfg = YamlLoader::load_from_str(&contents)
        .expect("could not parse config file");

    if cfg.len() != 1 {
        panic!("empty or malformed config file");
    }

    let mut port: u16 = 0;
    let mut iface: String = "unknown".to_string();
    let mut seed: String = "".to_string();

    for (key, value) in cfg[0].as_hash().unwrap() {
        match key.as_str().unwrap() {
            "port" => port = value.as_i64().unwrap() as u16,
            "interface" => iface = value.as_str().unwrap().to_string(),
            "seed" => seed = value.as_str().unwrap().to_string(),
            _ => warn!("ignoring unknown config key '{}'", key.as_str().unwrap())
        }
    }

    let binseed = seed.from_hex()
        .expect("seed value invalid; 'seed' should be 32 byte hex value");

    (iface, port, binseed)
}

fn main() {
    init_logging();

    info!("Roughenough server v{} starting", SERVER_VERSION);

    let mut args = env::args();
    if args.len() != 2 {
        error!("Usage: server /path/to/config.file");
        process::exit(1);
    }

    let (iface, port, seed) = load_config(&args.nth(1).unwrap());

    let mut lt_key = Signer::new(&seed);
    let mut ephemeral_key = create_ephemeral_key();
    let cert_bytes = make_cert(&mut lt_key, &ephemeral_key).encode().unwrap();

    info!("Long-term public key: {}", lt_key.public_key_bytes().to_hex());
    info!("Ephemeral public key: {}", ephemeral_key.public_key_bytes().to_hex());
    info!("Server listening on {}:{}", iface, port);

    let socket = UdpSocket::bind(format!("{}:{}", iface, port)).expect("failed to bind to socket");
    socket
        .set_read_timeout(Some(Duration::from_millis(100)))
        .expect("could not set read timeout");

    let mut buf = [0u8; 65536];
    let mut loop_count = 0u64;
    let mut responses = 0u64;
    let mut bad_requests = 0u64;

    let keep_running = Arc::new(AtomicBool::new(true));
    let kr = keep_running.clone();

    ctrlc::set_handler(move || { kr.store(false, Ordering::Release); })
        .expect("failed setting Ctrl-C handler");

    loop {
        if !keep_running.load(Ordering::Acquire) {
            info!("Ctrl-C caught, exiting...");
            break;
        }

        match socket.recv_from(&mut buf) {
            Ok((num_bytes, src_addr)) => {
                if let Ok(nonce) = nonce_from_request(&buf, num_bytes) {
                    let resp = make_response(&mut ephemeral_key, &cert_bytes, nonce);
                    let resp_bytes = resp.encode().unwrap();

                    socket
                        .send_to(&resp_bytes, src_addr)
                        .expect("could not send response");

                    info!("Responded to {}", src_addr);
                    responses += 1;
                } else {
                    info!("invalid request ({} bytes) from {}", num_bytes, src_addr);
                    bad_requests += 1;
                }
            }
            Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
                loop_count += 1;
                if loop_count % 600 == 0 {
                    info!("responses {}, invalid requests {}", responses, bad_requests);
                }
            }
            Err(ref e) => error!("Error {:?}: {:?}", e.kind(), e),
        }
    }

    info!("Done.");
}
<commit_msg>Convert to mio instead of manual poll() loop<commit_after>// Copyright 2017 int08h LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//!
//! Roughtime server
//!
//! # Configuration
//! The `roughenough` server is configured via a config file:
//!
//! ```yaml
//! interface: 127.0.0.1
//! port: 8686
//! seed: f61075c988feb9cb700a4a6a3291bfbc9cab11b9c9eca8c802468eb38a43d7d3
//! ```
//!
//! Where:
//!
//!   * **interface** - IP address or interface name for listening to client requests
//!   * **port** - UDP port to listen to requests
//!   * **seed** - A 32-byte hexadecimal value used as the seed to generate the
//!                server's long-term key pair. **This is a secret value**, treat it
//!                with care.
//!
//! # Running the Server
//!
//! ```bash
//! $ cargo run --release --bin server /path/to/config.file
//! ```

#![allow(deprecated)] // for mio::Timer

extern crate byteorder;
extern crate core;
extern crate ring;
extern crate roughenough;
extern crate time;
extern crate untrusted;
extern crate fern;
extern crate ctrlc;
extern crate yaml_rust;
#[macro_use]
extern crate log;
extern crate mio;

use std::env;
use std::process;
use std::fs::File;
use std::io::Read;
use std::time::Duration;
use std::net::SocketAddr;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};

use mio::{Poll, Token, Ready, PollOpt, Events};
use mio::net::UdpSocket;
use mio::timer::Timer;

use byteorder::{LittleEndian, WriteBytesExt};

use roughenough::{RtMessage, Tag, Error};
use roughenough::{CERTIFICATE_CONTEXT, MIN_REQUEST_LENGTH, SIGNED_RESPONSE_CONTEXT, TREE_LEAF_TWEAK};
use roughenough::hex::*;
use roughenough::sign::Signer;

use ring::{digest, rand};
use ring::rand::SecureRandom;

use yaml_rust::YamlLoader;

const SERVER_VERSION: &'static str = "0.2.0";

const MESSAGE: Token = Token(0);
const STATUS: Token = Token(1);

fn create_ephemeral_key() -> Signer {
    let rng = rand::SystemRandom::new();
    let mut seed = [0u8; 32];
    rng.fill(&mut seed).unwrap();

    Signer::new(&seed)
}

fn make_dele_bytes(ephemeral_key: &Signer) -> Result<Vec<u8>, Error> {
    let zeros = [0u8; 8];
    let max = [0xff; 8];

    let mut dele_msg = RtMessage::new(3);
    dele_msg.add_field(Tag::PUBK, ephemeral_key.public_key_bytes())?;
    dele_msg.add_field(Tag::MINT, &zeros)?;
    dele_msg.add_field(Tag::MAXT, &max)?;

    dele_msg.encode()
}

fn make_key_and_cert(seed: &[u8]) -> (Signer, Vec<u8>) {
    let mut long_term_key = Signer::new(seed);
    let ephemeral_key = create_ephemeral_key();

    info!("Long-term public key: {}", long_term_key.public_key_bytes().to_hex());
    info!("Ephemeral public key: {}", ephemeral_key.public_key_bytes().to_hex());

    // Make DELE and sign it with long-term key
    let dele_bytes = make_dele_bytes(&ephemeral_key).unwrap();
    let dele_signature = {
        long_term_key.update(CERTIFICATE_CONTEXT.as_bytes());
        long_term_key.update(&dele_bytes);
        long_term_key.sign()
    };

    // Create CERT
    let cert_bytes = {
        let mut cert_msg = RtMessage::new(2);
        cert_msg.add_field(Tag::SIG, &dele_signature).unwrap();
        cert_msg.add_field(Tag::DELE, &dele_bytes).unwrap();

        cert_msg.encode().unwrap()
    };

    return (ephemeral_key, cert_bytes);
}

fn make_response(ephemeral_key: &mut Signer, cert_bytes: &[u8], nonce: &[u8]) -> RtMessage {
    //   create SREP
    //   sign SREP
    //   create response:
    //    - SIG
    //    - PATH (always 0)
    //    - SREP
    //    - CERT (pre-created)
    //    - INDX (always 0)

    let path = [0u8; 0];
    let zeros = [0u8; 4];

    let mut radi: Vec<u8> = Vec::with_capacity(4);
    let mut midp: Vec<u8> = Vec::with_capacity(8);

    // one second (in microseconds)
    radi.write_u32::<LittleEndian>(1000000).unwrap();

    // current epoch time in microseconds
    let now = {
        let tv = time::get_time();
        let secs = (tv.sec as u64) * 1000000;
        let nsecs = (tv.nsec as u64) / 1000;

        secs + nsecs
    };
    midp.write_u64::<LittleEndian>(now).unwrap();

    // Signed response SREP
    let srep_bytes = {
        // hash request nonce
        let mut ctx = digest::Context::new(&digest::SHA512);
        ctx.update(&TREE_LEAF_TWEAK);
        ctx.update(&nonce);
        let digest = ctx.finish();

        let mut srep_msg = RtMessage::new(3);
        srep_msg.add_field(Tag::RADI, &radi).unwrap();
        srep_msg.add_field(Tag::MIDP, &midp).unwrap();
        srep_msg.add_field(Tag::ROOT, digest.as_ref()).unwrap();

        srep_msg.encode().unwrap()
    };

    // signature on SREP
    let srep_signature = {
        ephemeral_key.update(SIGNED_RESPONSE_CONTEXT.as_bytes());
        ephemeral_key.update(&srep_bytes);
        ephemeral_key.sign()
    };

    let mut response = RtMessage::new(5);
    response.add_field(Tag::SIG, &srep_signature).unwrap();
    response.add_field(Tag::PATH, &path).unwrap();
    response.add_field(Tag::SREP, &srep_bytes).unwrap();
    response.add_field(Tag::CERT, cert_bytes).unwrap();
    response.add_field(Tag::INDX, &zeros).unwrap();

    response
}

// extract the client's nonce from its request
fn nonce_from_request(buf: &[u8], num_bytes: usize) -> Result<&[u8], Error> {
    if num_bytes < MIN_REQUEST_LENGTH as usize {
        return Err(Error::RequestTooShort);
    }

    let tag_count = &buf[..4];
    let expected_nonc = &buf[8..12];
    let expected_pad = &buf[12..16];

    let tag_count_is_2 = tag_count == [0x02, 0x00, 0x00, 0x00];
    let tag1_is_nonc = expected_nonc == Tag::NONC.wire_value();
    let tag2_is_pad = expected_pad == Tag::PAD.wire_value();

    if tag_count_is_2 && tag1_is_nonc && tag2_is_pad {
        Ok(&buf[0x10..0x50])
    } else {
        Err(Error::InvalidRequest)
    }
}

fn init_logging() {
    fern::Dispatch::new()
        .format(|out, message, record| {
            out.finish(
                format_args!("{} [{}] {}", time::now().rfc3339(), record.level(), message)
            )
        })
        .level(log::LogLevelFilter::Info)
        .chain(std::io::stdout())
        .apply()
        .unwrap();
}

fn load_config(config_file: &str) -> (SocketAddr, Vec<u8>) {
    let mut infile = File::open(config_file)
        .expect("failed to open config file");

    let mut contents = String::new();
    infile.read_to_string(&mut contents)
        .expect("could not read config file");

    let cfg = YamlLoader::load_from_str(&contents)
        .expect("could not parse config file");

    if cfg.len() != 1 {
        panic!("empty or malformed config file");
    }

    let mut port: u16 = 0;
    let mut iface: String = "unknown".to_string();
    let mut seed: String = "".to_string();

    for (key, value) in cfg[0].as_hash().unwrap() {
        match key.as_str().unwrap() {
            "port" => port = value.as_i64().unwrap() as u16,
            "interface" => iface = value.as_str().unwrap().to_string(),
            "seed" => seed = value.as_str().unwrap().to_string(),
            _ => warn!("ignoring unknown config key '{}'", key.as_str().unwrap())
        }
    }

    let addr = format!("{}:{}", iface, port);
    let sock_addr: SocketAddr = addr.parse()
        .expect(&format!("could not create socket address from {}", addr));

    let binseed = seed.from_hex()
        .expect("seed value invalid; 'seed' should be 32 byte hex value");

    (sock_addr, binseed)
}

fn polling_loop(addr: &SocketAddr, mut ephemeral_key: &mut Signer, cert_bytes: &[u8]) {
    let keep_running = Arc::new(AtomicBool::new(true));
    let kr = keep_running.clone();

    ctrlc::set_handler(move || { kr.store(false, Ordering::Release); })
        .expect("failed setting Ctrl-C handler");

    let socket = UdpSocket::bind(addr).expect("failed to bind to socket");
    let status_duration = Duration::from_secs(6000);
    let poll_duration = Some(Duration::from_millis(100));

    let mut timer: Timer<()> = Timer::default();
    timer.set_timeout(status_duration, ()).expect("unable to set_timeout");

    let mut buf = [0u8; 65536];
    let mut events = Events::with_capacity(32);
    let mut num_responses = 0u64;
    let mut num_bad_requests = 0u64;

    let poll = Poll::new().unwrap();
    poll.register(&socket, MESSAGE, Ready::readable(), PollOpt::edge()).unwrap();
    poll.register(&timer, STATUS, Ready::readable(), PollOpt::edge()).unwrap();

    loop {
        if !keep_running.load(Ordering::Acquire) {
            info!("Ctrl-C caught, exiting...");
            break;
        }

        poll.poll(&mut events, poll_duration).expect("poll failed");

        for event in events.iter() {
            match event.token() {
                MESSAGE => {
                    let (num_bytes, src_addr) = socket.recv_from(&mut buf).expect("recv_from failed");

                    if let Ok(nonce) = nonce_from_request(&buf, num_bytes) {
                        let resp = make_response(&mut ephemeral_key, &cert_bytes, nonce);
                        let resp_bytes = resp.encode().unwrap();

                        socket.send_to(&resp_bytes, &src_addr).expect("send_to failed");

                        info!("Responded to {}", src_addr);
                        num_responses += 1;
                    } else {
                        info!("invalid request ({} bytes) from {}", num_bytes, src_addr);
                        num_bad_requests += 1;
                    }
                }

                STATUS => {
                    info!("responses {}, invalid requests {}", num_responses, num_bad_requests);
                    timer.set_timeout(status_duration, ()).expect("unable to set_timeout");
                }

                _ => unreachable!()
            }
        }
    }
}

fn main() {
    init_logging();

    info!("Roughenough server v{} starting", SERVER_VERSION);

    let mut args = env::args();
    if args.len() != 2 {
        error!("Usage: server /path/to/config.file");
        process::exit(1);
    }

    let (addr, key_seed) = load_config(&args.nth(1).unwrap());
    let (mut ephemeral_key, cert_bytes) = make_key_and_cert(&key_seed);

    info!("Server listening on {}", addr);

    polling_loop(&addr, &mut ephemeral_key, &cert_bytes);

    info!("Done.");
    process::exit(0);
}
<|endoftext|>


<commit_before>use std::fmt;

use bytes::{Buf, Bytes};

/// A piece of a message body.
///
/// These are returned by [`Body`](::Body). It is an efficient buffer type.
///
/// A `Chunk` can be easily created by many of Rust's standard types that
/// represent a collection of bytes, using `Chunk::from`.
pub struct Chunk {
    /// The buffer of bytes making up this body.
    bytes: Bytes,
}

// An unexported type to prevent locking `Chunk::into_iter()` to `Bytes::into_iter()`.
#[derive(Debug)]
pub struct IntoIter {
    inner: <Bytes as IntoIterator>::IntoIter,
}

impl Chunk {
    /// Converts this `Chunk` directly into the `Bytes` type without copies.
    ///
    /// This is simply an inherent alias for `Bytes::from(chunk)`, which exists,
    /// but doesn't appear in rustdocs.
    #[inline]
    pub fn into_bytes(self) -> Bytes {
        self.into()
    }
}

impl Buf for Chunk {
    #[inline]
    fn remaining(&self) -> usize {
        //perf: Bytes::len() isn't inline yet,
        //so it's slightly slower than checking
        //the length of the slice.
        self.bytes().len()
    }

    #[inline]
    fn bytes(&self) -> &[u8] {
        &self.bytes
    }

    #[inline]
    fn advance(&mut self, cnt: usize) {
        self.bytes.advance(cnt);
    }
}

impl From<Vec<u8>> for Chunk {
    #[inline]
    fn from(v: Vec<u8>) -> Chunk {
        Chunk::from(Bytes::from(v))
    }
}

impl From<&'static [u8]> for Chunk {
    #[inline]
    fn from(slice: &'static [u8]) -> Chunk {
        Chunk::from(Bytes::from_static(slice))
    }
}

impl From<String> for Chunk {
    #[inline]
    fn from(s: String) -> Chunk {
        s.into_bytes().into()
    }
}

impl From<&'static str> for Chunk {
    #[inline]
    fn from(slice: &'static str) -> Chunk {
        slice.as_bytes().into()
    }
}

impl From<Bytes> for Chunk {
    #[inline]
    fn from(bytes: Bytes) -> Chunk {
        Chunk {
            bytes: bytes,
        }
    }
}

impl From<Chunk> for Bytes {
    #[inline]
    fn from(chunk: Chunk) -> Bytes {
        chunk.bytes
    }
}

impl ::std::ops::Deref for Chunk {
    type Target = [u8];

    #[inline]
    fn deref(&self) -> &Self::Target {
        self.as_ref()
    }
}

impl AsRef<[u8]> for Chunk {
    #[inline]
    fn as_ref(&self) -> &[u8] {
        &self.bytes
    }
}

impl fmt::Debug for Chunk {
    #[inline]
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Debug::fmt(self.as_ref(), f)
    }
}

impl Default for Chunk {
    #[inline]
    fn default() -> Chunk {
        Chunk::from(Bytes::new())
    }
}

impl IntoIterator for Chunk {
    type Item = u8;
    type IntoIter = IntoIter;

    #[inline]
    fn into_iter(self) -> Self::IntoIter {
        IntoIter {
            inner: self.bytes.into_iter(),
        }
    }
}

impl Extend<u8> for Chunk {
    #[inline]
    fn extend<T>(&mut self, iter: T) where T: IntoIterator<Item=u8> {
        self.bytes.extend(iter)
    }
}

impl Iterator for IntoIter {
    type Item = u8;

    #[inline]
    fn next(&mut self) -> Option<Self::Item> {
        self.inner.next()
    }

    #[inline]
    fn size_hint(&self) -> (usize, Option<usize>) {
        self.inner.size_hint()
    }
}

impl ExactSizeIterator for IntoIter {}

#[cfg(test)]
mod tests {
    #[cfg(feature = "nightly")]
    use test::Bencher;

    #[cfg(feature = "nightly")]
    #[bench]
    fn bench_chunk_static_buf(b: &mut Bencher) {
        use bytes::BufMut;

        let s = "Hello, World!";
        b.bytes = s.len() as u64;

        let mut dst = Vec::with_capacity(128);

        b.iter(|| {
            let chunk = ::Chunk::from(s);
            dst.put(chunk);
            ::test::black_box(&dst);
            dst.clear();
        })
    }
}

<commit_msg>refactor(body): use fmt::Debug of Bytes for Chunk<commit_after>use std::fmt;

use bytes::{Buf, Bytes};

/// A piece of a message body.
///
/// These are returned by [`Body`](::Body). It is an efficient buffer type.
///
/// A `Chunk` can be easily created by many of Rust's standard types that
/// represent a collection of bytes, using `Chunk::from`.
pub struct Chunk {
    /// The buffer of bytes making up this body.
    bytes: Bytes,
}

// An unexported type to prevent locking `Chunk::into_iter()` to `Bytes::into_iter()`.
#[derive(Debug)]
pub struct IntoIter {
    inner: <Bytes as IntoIterator>::IntoIter,
}

impl Chunk {
    /// Converts this `Chunk` directly into the `Bytes` type without copies.
    ///
    /// This is simply an inherent alias for `Bytes::from(chunk)`, which exists,
    /// but doesn't appear in rustdocs.
    #[inline]
    pub fn into_bytes(self) -> Bytes {
        self.into()
    }
}

impl Buf for Chunk {
    #[inline]
    fn remaining(&self) -> usize {
        //perf: Bytes::len() isn't inline yet,
        //so it's slightly slower than checking
        //the length of the slice.
        self.bytes().len()
    }

    #[inline]
    fn bytes(&self) -> &[u8] {
        &self.bytes
    }

    #[inline]
    fn advance(&mut self, cnt: usize) {
        self.bytes.advance(cnt);
    }
}

impl From<Vec<u8>> for Chunk {
    #[inline]
    fn from(v: Vec<u8>) -> Chunk {
        Chunk::from(Bytes::from(v))
    }
}

impl From<&'static [u8]> for Chunk {
    #[inline]
    fn from(slice: &'static [u8]) -> Chunk {
        Chunk::from(Bytes::from_static(slice))
    }
}

impl From<String> for Chunk {
    #[inline]
    fn from(s: String) -> Chunk {
        s.into_bytes().into()
    }
}

impl From<&'static str> for Chunk {
    #[inline]
    fn from(slice: &'static str) -> Chunk {
        slice.as_bytes().into()
    }
}

impl From<Bytes> for Chunk {
    #[inline]
    fn from(bytes: Bytes) -> Chunk {
        Chunk {
            bytes: bytes,
        }
    }
}

impl From<Chunk> for Bytes {
    #[inline]
    fn from(chunk: Chunk) -> Bytes {
        chunk.bytes
    }
}

impl ::std::ops::Deref for Chunk {
    type Target = [u8];

    #[inline]
    fn deref(&self) -> &Self::Target {
        self.as_ref()
    }
}

impl AsRef<[u8]> for Chunk {
    #[inline]
    fn as_ref(&self) -> &[u8] {
        &self.bytes
    }
}

impl fmt::Debug for Chunk {
    #[inline]
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Debug::fmt(&self.bytes, f)
    }
}

impl Default for Chunk {
    #[inline]
    fn default() -> Chunk {
        Chunk::from(Bytes::new())
    }
}

impl IntoIterator for Chunk {
    type Item = u8;
    type IntoIter = IntoIter;

    #[inline]
    fn into_iter(self) -> Self::IntoIter {
        IntoIter {
            inner: self.bytes.into_iter(),
        }
    }
}

impl Extend<u8> for Chunk {
    #[inline]
    fn extend<T>(&mut self, iter: T) where T: IntoIterator<Item=u8> {
        self.bytes.extend(iter)
    }
}

impl Iterator for IntoIter {
    type Item = u8;

    #[inline]
    fn next(&mut self) -> Option<Self::Item> {
        self.inner.next()
    }

    #[inline]
    fn size_hint(&self) -> (usize, Option<usize>) {
        self.inner.size_hint()
    }
}

impl ExactSizeIterator for IntoIter {}

#[cfg(test)]
mod tests {
    #[cfg(feature = "nightly")]
    use test::Bencher;

    #[cfg(feature = "nightly")]
    #[bench]
    fn bench_chunk_static_buf(b: &mut Bencher) {
        use bytes::BufMut;

        let s = "Hello, World!";
        b.bytes = s.len() as u64;

        let mut dst = Vec::with_capacity(128);

        b.iter(|| {
            let chunk = ::Chunk::from(s);
            dst.put(chunk);
            ::test::black_box(&dst);
            dst.clear();
        })
    }
}

<|endoftext|>


<commit_before>// Copyright 2015 Nathan Sizemore <nathanrsizemore@gmail.com>
//
// This Source Code Form is subject to the
// terms of the Mozilla Public License, v.
// 2.0. If a copy of the MPL was not
// distributed with this file, You can
// obtain one at
// http://mozilla.org/MPL/2.0/.
//
// This Source Code Form is "Incompatible
// With Secondary Licenses", as defined by
// the Mozilla Public License, v. 2.0.

use std::{str, ptr};
use std::ffi::{CStr, CString};
use std::thread;
use std::net::TcpStream;
use std::sync::mpsc::{channel, Sender, Receiver};

use super::libc::{c_int, c_char};
use super::simple_stream::bstream::Bstream;

extern "C" {
    fn register_writer_tx(tx: *mut Sender<Vec<u8>>);
    fn register_stop_tx(tx: *mut Sender<()>);
}

/// Connects to the provided address, (eg "123.123.123.123:3000") and registers
/// the on data received handler
#[no_mangle]
pub extern "C" fn connect(address: *const c_char,
    handler: extern fn(*const c_char, c_int),
    on_connect_handler: extern fn(),
    on_disconnect_handler: extern fn()) -> c_int {

    println!("Rust - connect");
    println!("Rust - connect");
    println!("Rust - connect");
    println!("Rust - connect");
    println!("Rust - connect");
    println!("Rust - connect");
    println!("Rust - connect");
    println!("Rust - connect");
    println!("Rust - connect");
    println!("Rust - connect");
    println!("Rust - connect");
    println!("Rust - connect");
    println!("Rust - connect");
    println!("Rust - connect");
    println!("Rust - connect");
    println!("Rust - connect");
    println!("Rust - connect");
    println!("Rust - connect");
    println!("Rust - connect");
    println!("Rust - connect");

    let mut r_address;
    unsafe {
        r_address = CStr::from_ptr(address);
    }
    let s_address = r_address.to_bytes();
    let host_address = match str::from_utf8(s_address) {
        Ok(safe_str) => safe_str,
        Err(_) => {
            println!("Invalid host address");
            return -1 as c_int;
        }
    };

    // Create and register a way to kill this client
    let (k_tx, kill_rx): (Sender<()>, Receiver<()>) = channel();
    let kill_tx = k_tx.clone();
    let mut k_tx_ptr = Box::new(k_tx);
    unsafe {
        register_stop_tx(&mut *k_tx_ptr);
    }

    // Writer thread's channel
    let (w_tx, w_rx): (Sender<Vec<u8>>, Receiver<Vec<u8>>) = channel();
    let mut w_tx_ptr = Box::new(w_tx);
    unsafe {
        register_writer_tx(&mut *w_tx_ptr);
    }

    println!("Attempting connect to: {}", host_address);

    let result = TcpStream::connect(host_address);
    if result.is_err() {
        println!("Error connecting to {} - {}", host_address, result.unwrap_err());
        return -1 as c_int;
    }
    println!("Connected");
    on_connect_handler();

    let stream = result.unwrap();
    let client = Bstream::new(stream);

    let r_client = client.clone();
    let r_kill_tx = kill_tx.clone();

    let w_client = client.clone();
    let w_kill_tx = kill_tx.clone();

    // Start the reader thread
    thread::Builder::new()
        .name("ReaderThread".to_string())
        .spawn(move||{
            reader_thread(r_client, handler, r_kill_tx)
        }).unwrap();

    // Start the writer thread
    thread::Builder::new()
        .name("WriterThread".to_string())
        .spawn(move||{
            writer_thread(w_rx, w_client, w_kill_tx)
        }).unwrap();

    // Wait for the kill signal
    match kill_rx.recv() {
        Ok(_) => { }
        Err(e) => {
            println!("Error on kill channel: {}", e);
            return -1 as c_int;
        }
    };
    on_disconnect_handler();

    // Exit out in standard C fashion
    0 as c_int
}

/// Writes the complete contents of buffer to the server
/// Returns -1 on error
#[no_mangle]
pub extern "C" fn send_to_writer(w_tx: *mut Sender<Vec<u8>>,
                                 buffer: *const c_char,
                                 count: c_int,
                                 k_tx: *mut Sender<()>) -> c_int {
    if count < 1 {
        println!("Error - count must be greater than zero");
        return -1 as c_int;
    }

    let num_elts = count as usize;
    let mut n_buffer = Vec::<u8>::with_capacity(num_elts);
    for x in 0..num_elts as isize {
        unsafe {
            n_buffer.push(ptr::read(buffer.offset(x)) as u8);
        }
    }

    unsafe {
        match (*w_tx).send(n_buffer) {
            Ok(_) => { }
            Err(e) => {
                println!("Error sending buffer: {}", e);
                let _ = (*k_tx).send(());
                return -1 as c_int;
            }
        };
    }

    0 as c_int
}

/// Forever listens to incoming data and when a complete message is received,
/// the passed callback is hit
fn reader_thread(client: Bstream,
                 event_handler: extern fn(*const c_char, c_int),
                 kill_tx: Sender<()>) {

    let mut reader = client.clone();
    loop {
        match reader.read() {
            Ok(buffer) => {
                // Launch the handler in a new thread
                thread::Builder::new()
                    .name("Reader-Worker".to_string())
                    .spawn(move||{
                        let slice = &buffer[..];
                        let c_buffer = CString::new(slice).unwrap();
                        event_handler(c_buffer.as_ptr(), buffer.len() as c_int);
                    }).unwrap();
            }
            Err(e) => {
                println!("Error: {}", e);
                break;
            }
        };
    }
    println!("Reader thread finished");
    let _ = kill_tx.send(());
}

/// Forever listens to Receiver<Vec<u8>> waiting on messages to come in
/// Once available, blocks until the entire message has been written
fn writer_thread(rx: Receiver<Vec<u8>>, client: Bstream, kill_tx: Sender<()>) {
    let mut writer = client.clone();
    loop {
        match rx.recv() {
            Ok(ref mut buffer) => {
                match writer.write(buffer) {
                    Ok(_) => { }
                    Err(e) => {
                        println!("Error: {}", e);
                        break;
                    }
                };
            }
            Err(e) => {
                println!("Error: {}", e);
                break;
            }
        };
    }
    println!("Writer thread finished");
    let _ = kill_tx.send(());
}
<commit_msg>Debugging<commit_after>// Copyright 2015 Nathan Sizemore <nathanrsizemore@gmail.com>
//
// This Source Code Form is subject to the
// terms of the Mozilla Public License, v.
// 2.0. If a copy of the MPL was not
// distributed with this file, You can
// obtain one at
// http://mozilla.org/MPL/2.0/.
//
// This Source Code Form is "Incompatible
// With Secondary Licenses", as defined by
// the Mozilla Public License, v. 2.0.

use std::{str, ptr};
use std::ffi::{CStr, CString};
use std::thread;
use std::net::TcpStream;
use std::sync::mpsc::{channel, Sender, Receiver};

use super::libc::{c_int, c_char};
use super::simple_stream::bstream::Bstream;

extern "C" {
    fn register_writer_tx(tx: *mut Sender<Vec<u8>>);
    fn register_stop_tx(tx: *mut Sender<()>);
}

/// Connects to the provided address, (eg "123.123.123.123:3000") and registers
/// the on data received handler
#[no_mangle]
pub extern "C" fn connect(address: *const c_char,
    handler: extern fn(*const c_char, c_int),
    on_connect_handler: extern fn(),
    on_disconnect_handler: extern fn()) -> c_int {

    println!("Rust - connect");
    println!("Rust - connect");
    println!("Rust - connect");
    println!("Rust - connect");
    println!("Rust - connect");
    println!("Rust - connect");
    println!("Rust - connect");
    println!("Rust - connect");
    println!("Rust - connect");
    println!("Rust - connect");
    println!("Rust - connect");
    println!("Rust - connect");
    println!("Rust - connect");
    println!("Rust - connect");
    println!("Rust - connect");
    println!("Rust - connect");
    println!("Rust - connect");
    println!("Rust - connect");
    println!("Rust - connect");
    println!("Rust - connect");

    78 as c_int

    // let mut r_address;
    // unsafe {
    //     r_address = CStr::from_ptr(address);
    // }
    // let s_address = r_address.to_bytes();
    // let host_address = match str::from_utf8(s_address) {
    //     Ok(safe_str) => safe_str,
    //     Err(_) => {
    //         println!("Invalid host address");
    //         return -1 as c_int;
    //     }
    // };
    //
    // // Create and register a way to kill this client
    // let (k_tx, kill_rx): (Sender<()>, Receiver<()>) = channel();
    // let kill_tx = k_tx.clone();
    // let mut k_tx_ptr = Box::new(k_tx);
    // unsafe {
    //     register_stop_tx(&mut *k_tx_ptr);
    // }
    //
    // // Writer thread's channel
    // let (w_tx, w_rx): (Sender<Vec<u8>>, Receiver<Vec<u8>>) = channel();
    // let mut w_tx_ptr = Box::new(w_tx);
    // unsafe {
    //     register_writer_tx(&mut *w_tx_ptr);
    // }
    //
    // println!("Attempting connect to: {}", host_address);
    //
    // let result = TcpStream::connect(host_address);
    // if result.is_err() {
    //     println!("Error connecting to {} - {}", host_address, result.unwrap_err());
    //     return -1 as c_int;
    // }
    // println!("Connected");
    // on_connect_handler();
    //
    // let stream = result.unwrap();
    // let client = Bstream::new(stream);
    //
    // let r_client = client.clone();
    // let r_kill_tx = kill_tx.clone();
    //
    // let w_client = client.clone();
    // let w_kill_tx = kill_tx.clone();
    //
    // // Start the reader thread
    // thread::Builder::new()
    //     .name("ReaderThread".to_string())
    //     .spawn(move||{
    //         reader_thread(r_client, handler, r_kill_tx)
    //     }).unwrap();
    //
    // // Start the writer thread
    // thread::Builder::new()
    //     .name("WriterThread".to_string())
    //     .spawn(move||{
    //         writer_thread(w_rx, w_client, w_kill_tx)
    //     }).unwrap();
    //
    // // Wait for the kill signal
    // match kill_rx.recv() {
    //     Ok(_) => { }
    //     Err(e) => {
    //         println!("Error on kill channel: {}", e);
    //         return -1 as c_int;
    //     }
    // };
    // on_disconnect_handler();
    //
    // // Exit out in standard C fashion
    // 0 as c_int
}

/// Writes the complete contents of buffer to the server
/// Returns -1 on error
#[no_mangle]
pub extern "C" fn send_to_writer(w_tx: *mut Sender<Vec<u8>>,
                                 buffer: *const c_char,
                                 count: c_int,
                                 k_tx: *mut Sender<()>) -> c_int {
    if count < 1 {
        println!("Error - count must be greater than zero");
        return -1 as c_int;
    }

    let num_elts = count as usize;
    let mut n_buffer = Vec::<u8>::with_capacity(num_elts);
    for x in 0..num_elts as isize {
        unsafe {
            n_buffer.push(ptr::read(buffer.offset(x)) as u8);
        }
    }

    unsafe {
        match (*w_tx).send(n_buffer) {
            Ok(_) => { }
            Err(e) => {
                println!("Error sending buffer: {}", e);
                let _ = (*k_tx).send(());
                return -1 as c_int;
            }
        };
    }

    0 as c_int
}

/// Forever listens to incoming data and when a complete message is received,
/// the passed callback is hit
fn reader_thread(client: Bstream,
                 event_handler: extern fn(*const c_char, c_int),
                 kill_tx: Sender<()>) {

    let mut reader = client.clone();
    loop {
        match reader.read() {
            Ok(buffer) => {
                // Launch the handler in a new thread
                thread::Builder::new()
                    .name("Reader-Worker".to_string())
                    .spawn(move||{
                        let slice = &buffer[..];
                        let c_buffer = CString::new(slice).unwrap();
                        event_handler(c_buffer.as_ptr(), buffer.len() as c_int);
                    }).unwrap();
            }
            Err(e) => {
                println!("Error: {}", e);
                break;
            }
        };
    }
    println!("Reader thread finished");
    let _ = kill_tx.send(());
}

/// Forever listens to Receiver<Vec<u8>> waiting on messages to come in
/// Once available, blocks until the entire message has been written
fn writer_thread(rx: Receiver<Vec<u8>>, client: Bstream, kill_tx: Sender<()>) {
    let mut writer = client.clone();
    loop {
        match rx.recv() {
            Ok(ref mut buffer) => {
                match writer.write(buffer) {
                    Ok(_) => { }
                    Err(e) => {
                        println!("Error: {}", e);
                        break;
                    }
                };
            }
            Err(e) => {
                println!("Error: {}", e);
                break;
            }
        };
    }
    println!("Writer thread finished");
    let _ = kill_tx.send(());
}
<|endoftext|>


<commit_before>a<commit_msg>Delete mod.rs<commit_after><|endoftext|>


<commit_before>//! Generates `assists.md` documentation.

use std::{fs, path::Path};

use crate::{
    codegen::{self, extract_comment_blocks_with_empty_lines, Mode},
    project_root, rust_files, Result,
};

pub fn generate_assists_docs(mode: Mode) -> Result<()> {
    let assists = collect_assists()?;
    generate_tests(&assists, mode)?;
    generate_docs(&assists, mode)?;
    Ok(())
}

#[derive(Debug)]
struct Assist {
    id: String,
    doc: String,
    before: String,
    after: String,
}

fn hide_hash_comments(text: &str) -> String {
    text.split('\n') // want final newline
        .filter(|&it| !(it.starts_with("# ") || it == "#"))
        .map(|it| format!("{}\n", it))
        .collect()
}

fn reveal_hash_comments(text: &str) -> String {
    text.split('\n') // want final newline
        .map(|it| {
            if it.starts_with("# ") {
                &it[2..]
            } else if it == "#" {
                ""
            } else {
                it
            }
        })
        .map(|it| format!("{}\n", it))
        .collect()
}

fn collect_assists() -> Result<Vec<Assist>> {
    let mut res = Vec::new();
    for path in rust_files(&project_root().join(codegen::ASSISTS_DIR)) {
        collect_file(&mut res, path.as_path())?;
    }
    res.sort_by(|lhs, rhs| lhs.id.cmp(&rhs.id));
    return Ok(res);

    fn collect_file(acc: &mut Vec<Assist>, path: &Path) -> Result<()> {
        let text = fs::read_to_string(path)?;
        let comment_blocks = extract_comment_blocks_with_empty_lines(&text);

        for block in comment_blocks {
            // FIXME: doesn't support blank lines yet, need to tweak
            // `extract_comment_blocks` for that.
            let mut lines = block.iter();
            let first_line = lines.next().unwrap();
            if !first_line.starts_with("Assist: ") {
                continue;
            }
            let id = first_line["Assist: ".len()..].to_string();
            assert!(
                id.chars().all(|it| it.is_ascii_lowercase() || it == '_'),
                "invalid assist id: {:?}",
                id
            );

            let doc = take_until(lines.by_ref(), "```").trim().to_string();
            assert!(
                doc.chars().next().unwrap().is_ascii_uppercase() && doc.ends_with('.'),
                "\n\n{}: assist docs should be proper sentences, with capitalization and a full stop at the end.\n\n{}\n\n",
                id, doc,
            );

            let before = take_until(lines.by_ref(), "```");

            assert_eq!(lines.next().unwrap().as_str(), "->");
            assert_eq!(lines.next().unwrap().as_str(), "```");
            let after = take_until(lines.by_ref(), "```");
            acc.push(Assist { id, doc, before, after })
        }

        fn take_until<'a>(lines: impl Iterator<Item = &'a String>, marker: &str) -> String {
            let mut buf = Vec::new();
            for line in lines {
                if line == marker {
                    break;
                }
                buf.push(line.clone());
            }
            buf.join("\n")
        }
        Ok(())
    }
}

fn generate_tests(assists: &[Assist], mode: Mode) -> Result<()> {
    let mut buf = String::from("use super::check_doc_test;\n");

    for assist in assists.iter() {
        let test = format!(
            r######"
#[test]
fn doctest_{}() {{
    check_doc_test(
        "{}",
r#####"
{}"#####, r#####"
{}"#####)
}}
"######,
            assist.id,
            assist.id,
            reveal_hash_comments(&assist.before),
            reveal_hash_comments(&assist.after)
        );

        buf.push_str(&test)
    }
    let buf = crate::reformat(buf)?;
    codegen::update(&project_root().join(codegen::ASSISTS_TESTS), &buf, mode)
}

fn generate_docs(assists: &[Assist], mode: Mode) -> Result<()> {
    let mut buf = String::from(
        "# Assists\n\nCursor position or selection is signified by `┃` character.\n\n",
    );

    for assist in assists {
        let before = assist.before.replace("<|>", "┃"); // Unicode pseudo-graphics bar
        let after = assist.after.replace("<|>", "┃");
        let docs = format!(
            "
## `{}`

{}

```rust
// BEFORE
{}
// AFTER
{}```
",
            assist.id,
            assist.doc,
            hide_hash_comments(&before),
            hide_hash_comments(&after)
        );
        buf.push_str(&docs);
    }

    codegen::update(&project_root().join(codegen::ASSISTS_DOCS), &buf, mode)
}
<commit_msg>minor<commit_after>//! Generates `assists.md` documentation.

use std::{fs, path::Path};

use crate::{
    codegen::{self, extract_comment_blocks_with_empty_lines, Mode},
    project_root, rust_files, Result,
};

pub fn generate_assists_docs(mode: Mode) -> Result<()> {
    let assists = Assist::collect()?;
    generate_tests(&assists, mode)?;
    generate_docs(&assists, mode)?;
    Ok(())
}

#[derive(Debug)]
struct Assist {
    id: String,
    doc: String,
    before: String,
    after: String,
}

impl Assist {
    fn collect() -> Result<Vec<Assist>> {
        let mut res = Vec::new();
        for path in rust_files(&project_root().join(codegen::ASSISTS_DIR)) {
            collect_file(&mut res, path.as_path())?;
        }
        res.sort_by(|lhs, rhs| lhs.id.cmp(&rhs.id));
        return Ok(res);

        fn collect_file(acc: &mut Vec<Assist>, path: &Path) -> Result<()> {
            let text = fs::read_to_string(path)?;
            let comment_blocks = extract_comment_blocks_with_empty_lines(&text);

            for block in comment_blocks {
                // FIXME: doesn't support blank lines yet, need to tweak
                // `extract_comment_blocks` for that.
                let mut lines = block.iter();
                let first_line = lines.next().unwrap();
                if !first_line.starts_with("Assist: ") {
                    continue;
                }
                let id = first_line["Assist: ".len()..].to_string();
                assert!(
                    id.chars().all(|it| it.is_ascii_lowercase() || it == '_'),
                    "invalid assist id: {:?}",
                    id
                );

                let doc = take_until(lines.by_ref(), "```").trim().to_string();
                assert!(
                    doc.chars().next().unwrap().is_ascii_uppercase() && doc.ends_with('.'),
                    "\n\n{}: assist docs should be proper sentences, with capitalization and a full stop at the end.\n\n{}\n\n",
                    id, doc,
                );

                let before = take_until(lines.by_ref(), "```");

                assert_eq!(lines.next().unwrap().as_str(), "->");
                assert_eq!(lines.next().unwrap().as_str(), "```");
                let after = take_until(lines.by_ref(), "```");
                acc.push(Assist { id, doc, before, after })
            }

            fn take_until<'a>(lines: impl Iterator<Item = &'a String>, marker: &str) -> String {
                let mut buf = Vec::new();
                for line in lines {
                    if line == marker {
                        break;
                    }
                    buf.push(line.clone());
                }
                buf.join("\n")
            }
            Ok(())
        }
    }
}

fn generate_tests(assists: &[Assist], mode: Mode) -> Result<()> {
    let mut buf = String::from("use super::check_doc_test;\n");

    for assist in assists.iter() {
        let test = format!(
            r######"
#[test]
fn doctest_{}() {{
    check_doc_test(
        "{}",
r#####"
{}"#####, r#####"
{}"#####)
}}
"######,
            assist.id,
            assist.id,
            reveal_hash_comments(&assist.before),
            reveal_hash_comments(&assist.after)
        );

        buf.push_str(&test)
    }
    let buf = crate::reformat(buf)?;
    codegen::update(&project_root().join(codegen::ASSISTS_TESTS), &buf, mode)
}

fn generate_docs(assists: &[Assist], mode: Mode) -> Result<()> {
    let mut buf = String::from(
        "# Assists\n\nCursor position or selection is signified by `┃` character.\n\n",
    );

    for assist in assists {
        let before = assist.before.replace("<|>", "┃"); // Unicode pseudo-graphics bar
        let after = assist.after.replace("<|>", "┃");
        let docs = format!(
            "
## `{}`

{}

```rust
// BEFORE
{}
// AFTER
{}```
",
            assist.id,
            assist.doc,
            hide_hash_comments(&before),
            hide_hash_comments(&after)
        );
        buf.push_str(&docs);
    }

    codegen::update(&project_root().join(codegen::ASSISTS_DOCS), &buf, mode)
}

fn hide_hash_comments(text: &str) -> String {
    text.split('\n') // want final newline
        .filter(|&it| !(it.starts_with("# ") || it == "#"))
        .map(|it| format!("{}\n", it))
        .collect()
}

fn reveal_hash_comments(text: &str) -> String {
    text.split('\n') // want final newline
        .map(|it| {
            if it.starts_with("# ") {
                &it[2..]
            } else if it == "#" {
                ""
            } else {
                it
            }
        })
        .map(|it| format!("{}\n", it))
        .collect()
}
<|endoftext|>


<commit_before><commit_msg>examples: Fix b64-tcp-server.rs<commit_after><|endoftext|>