<commit_before>#[crate_type = "rlib"];

use std::{char, iter};
use std::io::buffered::BufferedReader;
use std::io::File;

pub static EXPECTED_ANSWER: &'static str = "24702";

static BOARD_WIDTH: uint = 9;
static BOARD_HEIGHT: uint = 9;
static GROUP_WIDTH: uint = 3;
static GROUP_HEIGHT: uint = 3;
static MAX_NUMBER: uint = 9;
type BITS = u16;
static MASK_ALL: BITS = 0x1ff;

struct SuDoku {
    name: ~str,
    map: [[BITS, .. BOARD_WIDTH], .. BOARD_HEIGHT]
}

impl TotalEq for SuDoku {
    #[inline(always)]
    fn equals(&self, other: &SuDoku) -> bool {
        self.name == other.name && range(0, BOARD_HEIGHT).all(|y| self.map[y] == other.map[y])
    }
}

impl Eq for SuDoku {
    #[inline(always)]
    fn eq(&self, other: &SuDoku) -> bool { self.equals(other) }
    #[inline(always)]
    fn ne(&self, other: &SuDoku) -> bool { !self.equals(other) }
}

impl ToStr for SuDoku {
    fn to_str(&self) -> ~str {
        let rows = self.map.map(|row| {
            let cells = row.map(|&cell| {
                if cell.population_count() == 1 {
                    ~"_"
                } else {
                    (1u << cell.trailing_zeros()).to_str()
                }
            });
            cells.concat()
        });
        self.name + "\n" + rows.connect("\n")
    }
}

impl Clone for SuDoku {
    fn clone(&self) -> SuDoku {
        SuDoku { name: self.name.clone(), map: self.map }
    }
}

impl SuDoku {
    fn get_num(&self, x: uint, y: uint) -> BITS {
        match self.map[y][x].population_count() {
            0 => -1,
            1 => self.map[y][x].trailing_zeros() + 1,
            _ => 0
        }
    }
}

fn read_sudoku<T: Reader>(r: &mut BufferedReader<T>) -> Option<SuDoku> {
    r.read_line()
        .and_then(|name| {
            let mut sudoku = SuDoku {
                name: name,
                map: [[MASK_ALL, .. BOARD_WIDTH], .. BOARD_HEIGHT]
            };
            for y in range(0, BOARD_HEIGHT) {
                match r.read_line() {
                    None => return None,
                    Some(line) => {
                        for x in range(0, BOARD_WIDTH) {
                            let n = char::to_digit(line[x] as char, 10).unwrap();
                            if n != 0 { sudoku.map[y][x] = 1 << (n - 1); }
                        }
                    }
                }
            }
            Some(sudoku)
        })
}

fn solve_sudoku(mut puzzle: SuDoku) -> ~[SuDoku] {
    let group_it = range(0, GROUP_WIDTH * GROUP_HEIGHT)
        .map(|i| (i % GROUP_WIDTH, i / GROUP_WIDTH))
        .to_owned_vec();

    loop {
        let bkup = puzzle.clone();

        for y in range(0, BOARD_HEIGHT) {
            for x in range(0, BOARD_WIDTH) {
                if puzzle.map[y][x].population_count() != 1 { continue }

                let (x0, y0) = (x / GROUP_WIDTH * GROUP_WIDTH,
                                y / GROUP_HEIGHT * GROUP_HEIGHT);
                let row = range(0, BOARD_WIDTH).map(|x| (x, y));
                let col = range(0, BOARD_HEIGHT).map(|y| (x, y));
                let grp = group_it.iter().map(|&(dx, dy)| (x0 + dx, y0 + dy));

                let mut it = row.chain(col).chain(grp)
                    .filter(|&pos: &(uint, uint)| pos != (x, y));
                let mask = !puzzle.map[y][x] & MASK_ALL;
                for (x, y) in it { puzzle.map[y][x] &= mask; }
            }
        }

        for n in range(0, MAX_NUMBER) {
            let bit = 1 << n;

            for y in range(0, BOARD_HEIGHT) {
                let mut it = range(0, BOARD_WIDTH)
                    .filter(|&x| puzzle.map[y][x] & bit != 0);
                let next = it.next();
                if next.is_none() || it.next().is_some() { continue }
                puzzle.map[y][next.unwrap()] = bit;
            }

            for x in range(0, BOARD_WIDTH) {
                let mut it = range(0, BOARD_HEIGHT)
                    .filter(|&y| puzzle.map[y][x] & bit != 0);
                let next = it.next();
                if next.is_none() || it.next().is_some() { continue }
                puzzle.map[next.unwrap()][x] = bit;
            }

            for y0 in iter::range_step(0, BOARD_HEIGHT, GROUP_WIDTH) {
                for x0 in iter::range_step(0, BOARD_WIDTH, GROUP_HEIGHT) {
                    let mut it = group_it
                        .iter()
                        .map(|&(dx, dy)| (x0 + dx, y0 + dy))
                        .filter(|&(x, y)| puzzle.map[y][x] & bit != 0);
                    let next = it.next();
                    if next.is_some() && it.next().is_none() {
                        let (x, y) = next.unwrap();
                        puzzle.map[y][x] = bit;
                    }
                }
            }
        }

        if puzzle == bkup { break }
    }

    let it = range(0, BOARD_HEIGHT * BOARD_WIDTH)
        .map(|i| (i % BOARD_WIDTH, i / BOARD_WIDTH))
        .map(|(x, y)| (x, y, puzzle.map[y][x].population_count()))
        .to_owned_vec();

    if it.iter().any(|&(_x, _y, cnt)| cnt == 0) { return ~[]; }
    if it.iter().all(|&(_x, _y, cnt)| cnt == 1) { return ~[puzzle]; }

    let (x, y, _cnt) = *it.iter()
        .filter(|& &(_x, _y, cnt)| cnt > 1)
        .min_by(|& &(_x, _y, cnt)| cnt)
        .unwrap();

    let mut answers = ~[];
    for n in range(0, MAX_NUMBER) {
        let bit = 1 << n;
        if puzzle.map[y][x] & bit == 0 { continue }

        let mut p2 = puzzle.clone();
        p2.map[y][x] = bit;
        answers.push_all(solve_sudoku(p2));
    }

    answers
}

pub fn solve() -> ~str {
    let mut br = BufferedReader::new(
        File::open(&Path::new("files/sudoku.txt")).expect("file not found."));

    let mut puzzles = ~[];
    loop {
        match read_sudoku(&mut br) {
            Some(sudoku) => puzzles.push(sudoku),
            None => break
        }
    }
    let mut answers = puzzles
        .move_iter()
        .map(solve_sudoku)
        .map(|ans| { assert_eq!(ans.len(), 1); ans[0] });
    let mut sum = 0;
    for ans in answers {
        sum += 100 * ans.get_num(0, 0) + 10 * ans.get_num(1, 0) + ans.get_num(2, 0);
    }
    sum.to_str()
}
<commit_msg>problem 96 にコメントを追加<commit_after>#[crate_type = "rlib"];

use std::{char, iter};
use std::io::buffered::BufferedReader;
use std::io::File;

pub static EXPECTED_ANSWER: &'static str = "24702";

static BOARD_WIDTH: uint = 9;
static BOARD_HEIGHT: uint = 9;
static GROUP_WIDTH: uint = 3;
static GROUP_HEIGHT: uint = 3;
static MAX_NUMBER: uint = 9;
type BITS = u16;
static MASK_ALL: BITS = 0x1ff;

struct SuDoku {
    name: ~str,
    map: [[BITS, .. BOARD_WIDTH], .. BOARD_HEIGHT]
}

// #7622 (rust): #[deriving(TotalEq, Eq, Clone)] cannnot be used
impl TotalEq for SuDoku {
    #[inline]
    fn equals(&self, other: &SuDoku) -> bool {
        self.name == other.name && range(0, BOARD_HEIGHT).all(|y| self.map[y] == other.map[y])
    }
}

impl Eq for SuDoku {
    #[inline]
    fn eq(&self, other: &SuDoku) -> bool { self.equals(other) }
    #[inline]
    fn ne(&self, other: &SuDoku) -> bool { !self.equals(other) }
}

impl ToStr for SuDoku {
    fn to_str(&self) -> ~str {
        let rows = self.map.map(|row| {
            let cells = row.map(|&cell| {
                if cell.population_count() == 1 {
                    ~"_"
                } else {
                    (1u << cell.trailing_zeros()).to_str()
                }
            });
            cells.concat()
        });
        self.name + "\n" + rows.connect("\n")
    }
}

impl Clone for SuDoku {
    #[inline]
    fn clone(&self) -> SuDoku {
        SuDoku { name: self.name.clone(), map: self.map }
    }
}

impl SuDoku {
    #[inline]
    fn get_num(&self, x: uint, y: uint) -> BITS {
        match self.map[y][x].population_count() {
            0 => -1,
            1 => self.map[y][x].trailing_zeros() + 1,
            _ => 0
        }
    }
}

fn read_sudoku<T: Reader>(r: &mut BufferedReader<T>) -> Option<SuDoku> {
    r.read_line()
        .and_then(|name| {
            let mut sudoku = SuDoku {
                name: name,
                map: [[MASK_ALL, .. BOARD_WIDTH], .. BOARD_HEIGHT]
            };
            for y in range(0, BOARD_HEIGHT) {
                match r.read_line() {
                    None => return None,
                    Some(line) => {
                        for x in range(0, BOARD_WIDTH) {
                            let n = char::to_digit(line[x] as char, 10).unwrap();
                            if n != 0 { sudoku.map[y][x] = 1 << (n - 1); }
                        }
                    }
                }
            }
            Some(sudoku)
        })
}

fn solve_sudoku(mut puzzle: SuDoku) -> ~[SuDoku] {
    let group_it = range(0, GROUP_WIDTH * GROUP_HEIGHT)
        .map(|i| (i % GROUP_WIDTH, i / GROUP_WIDTH))
        .to_owned_vec();

    loop {
        let bkup = puzzle.clone();

        for y in range(0, BOARD_HEIGHT) {
            for x in range(0, BOARD_WIDTH) {
                if puzzle.map[y][x].population_count() != 1 { continue }

                let (x0, y0) = (x / GROUP_WIDTH * GROUP_WIDTH,
                                y / GROUP_HEIGHT * GROUP_HEIGHT);
                let row = range(0, BOARD_WIDTH).map(|x| (x, y));
                let col = range(0, BOARD_HEIGHT).map(|y| (x, y));
                let grp = group_it.iter().map(|&(dx, dy)| (x0 + dx, y0 + dy));

                let mut it = row.chain(col).chain(grp)
                    .filter(|&pos: &(uint, uint)| pos != (x, y));
                let mask = !puzzle.map[y][x] & MASK_ALL;
                for (x, y) in it { puzzle.map[y][x] &= mask; }
            }
        }

        for n in range(0, MAX_NUMBER) {
            let bit = 1 << n;

            for y in range(0, BOARD_HEIGHT) {
                let mut it = range(0, BOARD_WIDTH)
                    .filter(|&x| puzzle.map[y][x] & bit != 0);
                let next = it.next();
                if next.is_none() || it.next().is_some() { continue }
                puzzle.map[y][next.unwrap()] = bit;
            }

            for x in range(0, BOARD_WIDTH) {
                let mut it = range(0, BOARD_HEIGHT)
                    .filter(|&y| puzzle.map[y][x] & bit != 0);
                let next = it.next();
                if next.is_none() || it.next().is_some() { continue }
                puzzle.map[next.unwrap()][x] = bit;
            }

            for y0 in iter::range_step(0, BOARD_HEIGHT, GROUP_WIDTH) {
                for x0 in iter::range_step(0, BOARD_WIDTH, GROUP_HEIGHT) {
                    let mut it = group_it
                        .iter()
                        .map(|&(dx, dy)| (x0 + dx, y0 + dy))
                        .filter(|&(x, y)| puzzle.map[y][x] & bit != 0);
                    let next = it.next();
                    if next.is_some() && it.next().is_none() {
                        let (x, y) = next.unwrap();
                        puzzle.map[y][x] = bit;
                    }
                }
            }
        }

        if puzzle == bkup { break }
    }

    let it = range(0, BOARD_HEIGHT * BOARD_WIDTH)
        .map(|i| (i % BOARD_WIDTH, i / BOARD_WIDTH))
        .map(|(x, y)| (x, y, puzzle.map[y][x].population_count()))
        .to_owned_vec();

    if it.iter().any(|&(_x, _y, cnt)| cnt == 0) { return ~[]; }
    if it.iter().all(|&(_x, _y, cnt)| cnt == 1) { return ~[puzzle]; }

    let (x, y, _cnt) = *it.iter()
        .filter(|& &(_x, _y, cnt)| cnt > 1)
        .min_by(|& &(_x, _y, cnt)| cnt)
        .unwrap();

    let mut answers = ~[];
    for n in range(0, MAX_NUMBER) {
        let bit = 1 << n;
        if puzzle.map[y][x] & bit == 0 { continue }

        let mut p2 = puzzle.clone();
        p2.map[y][x] = bit;
        answers.push_all(solve_sudoku(p2));
    }

    answers
}

pub fn solve() -> ~str {
    let mut br = BufferedReader::new(
        File::open(&Path::new("files/sudoku.txt")).expect("file not found."));

    let mut puzzles = ~[];
    loop {
        match read_sudoku(&mut br) {
            Some(sudoku) => puzzles.push(sudoku),
            None => break
        }
    }
    let mut answers = puzzles
        .move_iter()
        .map(solve_sudoku)
        .map(|ans| { assert_eq!(ans.len(), 1); ans[0] });
    let mut sum = 0;
    for ans in answers {
        sum += 100 * ans.get_num(0, 0) + 10 * ans.get_num(1, 0) + ans.get_num(2, 0);
    }
    sum.to_str()
}
<|endoftext|>


<commit_before>use std::cell::RefCell;
use std::collections::{HashMap, HashSet};
use std::io;
use std::rc::Rc;
use std::{i32, u64};

use bytes::BytesMut;
use futures::sync::mpsc;
use futures::{self, Async, Poll, StartSend, Stream};
use tokio_core::reactor::Handle;
use tokio_io::codec::{Decoder, Encoder, Framed};
use tokio_io::{AsyncRead, AsyncWrite};
use tokio_proto::multiplex::{ClientProto, RequestId};

use asnom::common::TagClass;
use asnom::{Consumer, ConsumerState, Input, IResult, Move};
use asnom::structure::{PL, StructureTag};
use asnom::structures::{ASNTag, Integer, Null, Sequence, Tag};
use asnom::parse::parse_uint;
use asnom::parse::Parser;
use asnom::universal::Types;
use asnom::write;

use ldap::LdapOp;
use search::SearchItem;

pub type LdapRequestId = i32;

pub struct ProtoBundle {
    pub search_helpers: HashMap<RequestId, SearchHelper>,
    pub id_map: HashMap<LdapRequestId, RequestId>,
    pub next_id: LdapRequestId,
    pub handle: Handle,
}

impl ProtoBundle {
    fn create_search_helper(&mut self, id: RequestId, tx: mpsc::UnboundedSender<SearchItem>) {
        self.search_helpers.insert(id, SearchHelper {
            seen: false,
            msgid: 0,           // not valid, must be properly initialized later
            tx: tx,
        });
    }

    fn inc_next_id(&mut self) -> LdapRequestId {
        if self.next_id == i32::MAX {
            self.next_id = 0;
        }
        self.next_id += 1;
        self.next_id
    }
}

pub struct SearchHelper {
    pub seen: bool,
    pub msgid: LdapRequestId,
    pub tx: mpsc::UnboundedSender<SearchItem>,
}

impl SearchHelper {
    fn send_item(&mut self, item: SearchItem) -> io::Result<()> {
        self.tx.send(item).map_err(|e| io::Error::new(io::ErrorKind::Other, format!("{:?}", e)))
    }
}

#[derive(Debug)]
pub struct LdapResult {
    pub rc: u8,
    pub matched: String,
    pub text: String,
    pub refs: Vec<HashSet<String>>,
}

impl From<Tag> for LdapResult {
    fn from(t: Tag) -> LdapResult {
        let t = match t {
            Tag::StructureTag(t) => t,
            _ => unimplemented!(),
        };
        let mut tags = t.expect_constructed().expect("result sequence").into_iter();
        let rc = match parse_uint(tags.next().expect("element")
                .match_class(TagClass::Universal)
                .and_then(|t| t.match_id(Types::Enumerated as u64))
                .and_then(|t| t.expect_primitive()).expect("result code")
                .as_slice()) {
            IResult::Done(_, rc) => rc as u8,
            _ => panic!("failed to parse result code"),
        };
        let matched = String::from_utf8(tags.next().expect("element").expect_primitive().expect("octet string"))
            .expect("matched dn");
        let text = String::from_utf8(tags.next().expect("element").expect_primitive().expect("octet string"))
            .expect("diagnostic message");
        let mut refs = Vec::new();
        match tags.next() {
            None => (),
            Some(raw_refs) => {
                let raw_refs = match raw_refs.match_class(TagClass::Context)
                        .and_then(|t| t.match_id(3))
                        .and_then(|t| t.expect_constructed()) {
                    Some(rr) => rr,
                    None => panic!("failed to parse referrals"),
                };
                refs.push(raw_refs.into_iter()
                    .map(|t| t.expect_primitive().expect("octet string"))
                    .map(String::from_utf8)
                    .map(|s| s.expect("uri"))
                    .collect());
            },
        }
        LdapResult {
            rc: rc,
            matched: matched,
            text: text,
            refs: refs,
        }
    }
}

pub struct LdapCodec {
    bundle: Rc<RefCell<ProtoBundle>>,
}

impl Decoder for LdapCodec {
    type Item = (RequestId, (Tag, Option<StructureTag>));
    type Error = io::Error;

    fn decode(&mut self, buf: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
        let decoding_error = io::Error::new(io::ErrorKind::Other, "decoding error");
        let mut parser = Parser::new();
        let (amt, tag) = match *parser.handle(Input::Element(buf)) {
            ConsumerState::Continue(_) => return Ok(None),
            ConsumerState::Error(_e) => return Err(decoding_error),
            ConsumerState::Done(amt, ref tag) => (amt, tag),
        };
        let amt = match amt {
            Move::Await(_) => return Ok(None),
            Move::Seek(_) => return Err(decoding_error),
            Move::Consume(amt) => amt,
        };
        buf.split_to(amt);
        let tag = tag.clone();
        let mut tags = match tag.match_id(Types::Sequence as u64).and_then(|t| t.expect_constructed()) {
            Some(tags) => tags,
            None => return Err(decoding_error),
        };
        let maybe_controls = tags.pop().expect("element");
        let has_controls = match maybe_controls {
            StructureTag { id, class, ref payload } if class == TagClass::Context && id == 0 => match *payload {
                PL::C(_) => true,
                PL::P(_) => return Err(decoding_error),
            },
            _ => false,
        };
        let (protoop, controls) = if has_controls {
            (tags.pop().expect("element"), Some(maybe_controls))
        } else {
            (maybe_controls, None)
        };
        let msgid = match parse_uint(tags.pop().expect("element")
                .match_class(TagClass::Universal)
                .and_then(|t| t.match_id(Types::Integer as u64))
                .and_then(|t| t.expect_primitive()).expect("message id")
                .as_slice()) {
            IResult::Done(_, id) => id as i32,
            _ => return Err(decoding_error),
        };
        let id = match self.bundle.borrow().id_map.get(&msgid) {
            Some(&id) => id,
            None => return Err(io::Error::new(io::ErrorKind::Other, format!("no id found for message id: {}", msgid))),
        };
        match protoop.id {
            op_id @ 4 | op_id @ 5 | op_id @ 19 => {
                let null_tag = Tag::Null(Null { ..Default::default() });
                let id_tag = Tag::Integer(Integer {
                    inner: id as i64,
                    .. Default::default()
                });
                let mut bundle = self.bundle.borrow_mut();
                let mut helper = match bundle.search_helpers.get_mut(&id) {
                    Some(h) => h,
                    None => return Err(io::Error::new(io::ErrorKind::Other, format!("id mismatch: {}", id))),
                };
                helper.send_item(match op_id {
                    4 => SearchItem::Entry(protoop),
                    5 => SearchItem::Done(id, Tag::StructureTag(protoop).into(), controls),
                    19 => SearchItem::Referral(protoop),
                    _ => panic!("impossible op_id"),
                })?;
                if helper.seen {
                    Ok(Some((u64::MAX, (null_tag, None))))
                } else {
                    helper.seen = true;
                    Ok(Some((id, (id_tag, None))))
                }
            },
            _ => Ok(Some((id, (Tag::StructureTag(protoop), controls)))),
        }
    }
}

impl Encoder for LdapCodec {
    type Item = (RequestId, LdapOp);
    type Error = io::Error;

    fn encode(&mut self, msg: Self::Item, into: &mut BytesMut) -> io::Result<()> {
        let (id, op) = msg;
        let tag = match op {
            LdapOp::Single(tag) => tag,
            LdapOp::Multi(tag, tx) => {
                self.bundle.borrow_mut().create_search_helper(id, tx);
                tag
            },
            _ => unimplemented!(),
        };
        let outstruct = {
            // tokio-proto ids are u64, and LDAP (client) message ids are i32 > 0,
            // so we must have wraparound logic and a mapping from the latter to
            // the former
            let mut bundle = self.bundle.borrow_mut();
            let prev_ldap_id = bundle.next_id;
            let mut next_ldap_id = prev_ldap_id;
            while bundle.id_map.entry(next_ldap_id).or_insert(id) != &id {
                next_ldap_id = bundle.inc_next_id();
                assert_ne!(next_ldap_id, prev_ldap_id, "LDAP message id wraparound with no free slots");
            }
            bundle.inc_next_id();
            match bundle.search_helpers.get_mut(&id) {
                Some(ref mut helper) => helper.msgid = next_ldap_id,
                None => (),
            }
            Tag::Sequence(Sequence {
                inner: vec![
                    Tag::Integer(Integer {
                        inner: next_ldap_id as i64,
                        .. Default::default()
                    }),
                    tag,
                ],
                .. Default::default()
            })
        }.into_structure();
        trace!("Sending packet: {:?}", &outstruct);
        write::encode_into(into, outstruct)?;
        Ok(())
    }
}

pub struct LdapProto {
    bundle: Rc<RefCell<ProtoBundle>>,
}

impl LdapProto {
    pub fn new(handle: Handle) -> LdapProto {
        LdapProto {
            bundle: Rc::new(RefCell::new(ProtoBundle {
                search_helpers: HashMap::new(),
                id_map: HashMap::new(),
                next_id: 1,
                handle: handle,
            }))
        }
    }

    pub fn bundle(&self) -> Rc<RefCell<ProtoBundle>> {
        self.bundle.clone()
    }
}

pub struct ResponseFilter<T> {
    upstream: T,
}

impl<T> Stream for ResponseFilter<T>
    where T: Stream<Item=(RequestId, (Tag, Option<StructureTag>)), Error=io::Error>
{
    type Item = (RequestId, (Tag, Option<StructureTag>));
    type Error = io::Error;

    fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
        loop {
            match try_ready!(self.upstream.poll()) {
                Some((id, _)) if id == u64::MAX => continue,
                msg => return Ok(Async::Ready(msg)),
            }
        }
    }
}

impl<T> futures::Sink for ResponseFilter<T>
    where T: futures::Sink<SinkItem=(RequestId, LdapOp), SinkError=io::Error>
{
    type SinkItem = (RequestId, LdapOp);
    type SinkError = io::Error;

    fn start_send(&mut self, item: Self::SinkItem) -> StartSend<Self::SinkItem, Self::SinkError> {
        self.upstream.start_send(item)
    }

    fn poll_complete(&mut self) -> Poll<(), Self::SinkError> {
        self.upstream.poll_complete()
    }
}

impl<T: AsyncRead + AsyncWrite + 'static> ClientProto<T> for LdapProto {
    type Request = LdapOp;
    type Response = (Tag, Option<StructureTag>);

    type Transport = ResponseFilter<Framed<T, LdapCodec>>;
    type BindTransport = Result<Self::Transport, io::Error>;

    fn bind_transport(&self, io: T) -> Self::BindTransport {
        let ldapcodec = LdapCodec {
            bundle: self.bundle.clone(),
        };
        Ok(ResponseFilter {
            upstream: io.framed(ldapcodec),
        })
    }
}
<commit_msg>Hide From impl for LdapResult<commit_after>use std::cell::RefCell;
use std::collections::{HashMap, HashSet};
use std::io;
use std::rc::Rc;
use std::{i32, u64};

use bytes::BytesMut;
use futures::sync::mpsc;
use futures::{self, Async, Poll, StartSend, Stream};
use tokio_core::reactor::Handle;
use tokio_io::codec::{Decoder, Encoder, Framed};
use tokio_io::{AsyncRead, AsyncWrite};
use tokio_proto::multiplex::{ClientProto, RequestId};

use asnom::common::TagClass;
use asnom::{Consumer, ConsumerState, Input, IResult, Move};
use asnom::structure::{PL, StructureTag};
use asnom::structures::{ASNTag, Integer, Null, Sequence, Tag};
use asnom::parse::parse_uint;
use asnom::parse::Parser;
use asnom::universal::Types;
use asnom::write;

use ldap::LdapOp;
use search::SearchItem;

pub type LdapRequestId = i32;

pub struct ProtoBundle {
    pub search_helpers: HashMap<RequestId, SearchHelper>,
    pub id_map: HashMap<LdapRequestId, RequestId>,
    pub next_id: LdapRequestId,
    pub handle: Handle,
}

impl ProtoBundle {
    fn create_search_helper(&mut self, id: RequestId, tx: mpsc::UnboundedSender<SearchItem>) {
        self.search_helpers.insert(id, SearchHelper {
            seen: false,
            msgid: 0,           // not valid, must be properly initialized later
            tx: tx,
        });
    }

    fn inc_next_id(&mut self) -> LdapRequestId {
        if self.next_id == i32::MAX {
            self.next_id = 0;
        }
        self.next_id += 1;
        self.next_id
    }
}

pub struct SearchHelper {
    pub seen: bool,
    pub msgid: LdapRequestId,
    pub tx: mpsc::UnboundedSender<SearchItem>,
}

impl SearchHelper {
    fn send_item(&mut self, item: SearchItem) -> io::Result<()> {
        self.tx.send(item).map_err(|e| io::Error::new(io::ErrorKind::Other, format!("{:?}", e)))
    }
}

#[derive(Debug)]
pub struct LdapResult {
    pub rc: u8,
    pub matched: String,
    pub text: String,
    pub refs: Vec<HashSet<String>>,
}

#[doc(hidden)]
impl From<Tag> for LdapResult {
    fn from(t: Tag) -> LdapResult {
        let t = match t {
            Tag::StructureTag(t) => t,
            _ => unimplemented!(),
        };
        let mut tags = t.expect_constructed().expect("result sequence").into_iter();
        let rc = match parse_uint(tags.next().expect("element")
                .match_class(TagClass::Universal)
                .and_then(|t| t.match_id(Types::Enumerated as u64))
                .and_then(|t| t.expect_primitive()).expect("result code")
                .as_slice()) {
            IResult::Done(_, rc) => rc as u8,
            _ => panic!("failed to parse result code"),
        };
        let matched = String::from_utf8(tags.next().expect("element").expect_primitive().expect("octet string"))
            .expect("matched dn");
        let text = String::from_utf8(tags.next().expect("element").expect_primitive().expect("octet string"))
            .expect("diagnostic message");
        let mut refs = Vec::new();
        match tags.next() {
            None => (),
            Some(raw_refs) => {
                let raw_refs = match raw_refs.match_class(TagClass::Context)
                        .and_then(|t| t.match_id(3))
                        .and_then(|t| t.expect_constructed()) {
                    Some(rr) => rr,
                    None => panic!("failed to parse referrals"),
                };
                refs.push(raw_refs.into_iter()
                    .map(|t| t.expect_primitive().expect("octet string"))
                    .map(String::from_utf8)
                    .map(|s| s.expect("uri"))
                    .collect());
            },
        }
        LdapResult {
            rc: rc,
            matched: matched,
            text: text,
            refs: refs,
        }
    }
}

pub struct LdapCodec {
    bundle: Rc<RefCell<ProtoBundle>>,
}

impl Decoder for LdapCodec {
    type Item = (RequestId, (Tag, Option<StructureTag>));
    type Error = io::Error;

    fn decode(&mut self, buf: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
        let decoding_error = io::Error::new(io::ErrorKind::Other, "decoding error");
        let mut parser = Parser::new();
        let (amt, tag) = match *parser.handle(Input::Element(buf)) {
            ConsumerState::Continue(_) => return Ok(None),
            ConsumerState::Error(_e) => return Err(decoding_error),
            ConsumerState::Done(amt, ref tag) => (amt, tag),
        };
        let amt = match amt {
            Move::Await(_) => return Ok(None),
            Move::Seek(_) => return Err(decoding_error),
            Move::Consume(amt) => amt,
        };
        buf.split_to(amt);
        let tag = tag.clone();
        let mut tags = match tag.match_id(Types::Sequence as u64).and_then(|t| t.expect_constructed()) {
            Some(tags) => tags,
            None => return Err(decoding_error),
        };
        let maybe_controls = tags.pop().expect("element");
        let has_controls = match maybe_controls {
            StructureTag { id, class, ref payload } if class == TagClass::Context && id == 0 => match *payload {
                PL::C(_) => true,
                PL::P(_) => return Err(decoding_error),
            },
            _ => false,
        };
        let (protoop, controls) = if has_controls {
            (tags.pop().expect("element"), Some(maybe_controls))
        } else {
            (maybe_controls, None)
        };
        let msgid = match parse_uint(tags.pop().expect("element")
                .match_class(TagClass::Universal)
                .and_then(|t| t.match_id(Types::Integer as u64))
                .and_then(|t| t.expect_primitive()).expect("message id")
                .as_slice()) {
            IResult::Done(_, id) => id as i32,
            _ => return Err(decoding_error),
        };
        let id = match self.bundle.borrow().id_map.get(&msgid) {
            Some(&id) => id,
            None => return Err(io::Error::new(io::ErrorKind::Other, format!("no id found for message id: {}", msgid))),
        };
        match protoop.id {
            op_id @ 4 | op_id @ 5 | op_id @ 19 => {
                let null_tag = Tag::Null(Null { ..Default::default() });
                let id_tag = Tag::Integer(Integer {
                    inner: id as i64,
                    .. Default::default()
                });
                let mut bundle = self.bundle.borrow_mut();
                let mut helper = match bundle.search_helpers.get_mut(&id) {
                    Some(h) => h,
                    None => return Err(io::Error::new(io::ErrorKind::Other, format!("id mismatch: {}", id))),
                };
                helper.send_item(match op_id {
                    4 => SearchItem::Entry(protoop),
                    5 => SearchItem::Done(id, Tag::StructureTag(protoop).into(), controls),
                    19 => SearchItem::Referral(protoop),
                    _ => panic!("impossible op_id"),
                })?;
                if helper.seen {
                    Ok(Some((u64::MAX, (null_tag, None))))
                } else {
                    helper.seen = true;
                    Ok(Some((id, (id_tag, None))))
                }
            },
            _ => Ok(Some((id, (Tag::StructureTag(protoop), controls)))),
        }
    }
}

impl Encoder for LdapCodec {
    type Item = (RequestId, LdapOp);
    type Error = io::Error;

    fn encode(&mut self, msg: Self::Item, into: &mut BytesMut) -> io::Result<()> {
        let (id, op) = msg;
        let tag = match op {
            LdapOp::Single(tag) => tag,
            LdapOp::Multi(tag, tx) => {
                self.bundle.borrow_mut().create_search_helper(id, tx);
                tag
            },
            _ => unimplemented!(),
        };
        let outstruct = {
            // tokio-proto ids are u64, and LDAP (client) message ids are i32 > 0,
            // so we must have wraparound logic and a mapping from the latter to
            // the former
            let mut bundle = self.bundle.borrow_mut();
            let prev_ldap_id = bundle.next_id;
            let mut next_ldap_id = prev_ldap_id;
            while bundle.id_map.entry(next_ldap_id).or_insert(id) != &id {
                next_ldap_id = bundle.inc_next_id();
                assert_ne!(next_ldap_id, prev_ldap_id, "LDAP message id wraparound with no free slots");
            }
            bundle.inc_next_id();
            match bundle.search_helpers.get_mut(&id) {
                Some(ref mut helper) => helper.msgid = next_ldap_id,
                None => (),
            }
            Tag::Sequence(Sequence {
                inner: vec![
                    Tag::Integer(Integer {
                        inner: next_ldap_id as i64,
                        .. Default::default()
                    }),
                    tag,
                ],
                .. Default::default()
            })
        }.into_structure();
        trace!("Sending packet: {:?}", &outstruct);
        write::encode_into(into, outstruct)?;
        Ok(())
    }
}

pub struct LdapProto {
    bundle: Rc<RefCell<ProtoBundle>>,
}

impl LdapProto {
    pub fn new(handle: Handle) -> LdapProto {
        LdapProto {
            bundle: Rc::new(RefCell::new(ProtoBundle {
                search_helpers: HashMap::new(),
                id_map: HashMap::new(),
                next_id: 1,
                handle: handle,
            }))
        }
    }

    pub fn bundle(&self) -> Rc<RefCell<ProtoBundle>> {
        self.bundle.clone()
    }
}

pub struct ResponseFilter<T> {
    upstream: T,
}

impl<T> Stream for ResponseFilter<T>
    where T: Stream<Item=(RequestId, (Tag, Option<StructureTag>)), Error=io::Error>
{
    type Item = (RequestId, (Tag, Option<StructureTag>));
    type Error = io::Error;

    fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
        loop {
            match try_ready!(self.upstream.poll()) {
                Some((id, _)) if id == u64::MAX => continue,
                msg => return Ok(Async::Ready(msg)),
            }
        }
    }
}

impl<T> futures::Sink for ResponseFilter<T>
    where T: futures::Sink<SinkItem=(RequestId, LdapOp), SinkError=io::Error>
{
    type SinkItem = (RequestId, LdapOp);
    type SinkError = io::Error;

    fn start_send(&mut self, item: Self::SinkItem) -> StartSend<Self::SinkItem, Self::SinkError> {
        self.upstream.start_send(item)
    }

    fn poll_complete(&mut self) -> Poll<(), Self::SinkError> {
        self.upstream.poll_complete()
    }
}

impl<T: AsyncRead + AsyncWrite + 'static> ClientProto<T> for LdapProto {
    type Request = LdapOp;
    type Response = (Tag, Option<StructureTag>);

    type Transport = ResponseFilter<Framed<T, LdapCodec>>;
    type BindTransport = Result<Self::Transport, io::Error>;

    fn bind_transport(&self, io: T) -> Self::BindTransport {
        let ldapcodec = LdapCodec {
            bundle: self.bundle.clone(),
        };
        Ok(ResponseFilter {
            upstream: io.framed(ldapcodec),
        })
    }
}
<|endoftext|>


<commit_before>use std::any::Any;

use direction::Direction;
use vec::Vec2;
use view::{Selector, View};
use Printer;
use event::{Event, EventResult};

/// Generic wrapper around a view.
///
/// Default implementation forwards all calls to the child view.
/// Overrides some methods as desired.
///
/// You can use the [`wrap_impl!`] macro to define `get_view` and
/// `get_view_mut` for you.
///
/// [`wrap_impl!`]: ../macro.wrap_impl.html
pub trait ViewWrapper {
    /// Type that this view wraps.
    type V: View;

    /// Get an immutable reference to the wrapped view.
    fn get_view(&self) -> &Self::V;

    /// Get a mutable reference to the wrapped view.
    fn get_view_mut(&mut self) -> &mut Self::V;

    /// Wraps the `draw` method.
    fn wrap_draw(&self, printer: &Printer) {
        self.get_view().draw(printer);
    }

    /// Wraps the `get_min_size` method.
    fn wrap_get_min_size(&mut self, req: Vec2) -> Vec2 {
        self.get_view_mut().get_min_size(req)
    }

    /// Wraps the `on_event` method.
    fn wrap_on_event(&mut self, ch: Event) -> EventResult {
        self.get_view_mut().on_event(ch)
    }

    /// Wraps the `layout` method.
    fn wrap_layout(&mut self, size: Vec2) {
        self.get_view_mut().layout(size);
    }

    /// Wraps the `take_focus` method.
    fn wrap_take_focus(&mut self, source: Direction) -> bool {
        self.get_view_mut().take_focus(source)
    }

    /// Wraps the `find` method.
    fn wrap_find_any(&mut self, selector: &Selector) -> Option<&mut Any> {
        self.get_view_mut().find_any(selector)
    }

    /// Wraps the `needs_relayout` method.
    fn wrap_needs_relayout(&self) -> bool {
        self.get_view().needs_relayout()
    }
}

impl<T: ViewWrapper> View for T {
    fn draw(&self, printer: &Printer) {
        self.wrap_draw(printer);
    }

    fn get_min_size(&mut self, req: Vec2) -> Vec2 {
        self.wrap_get_min_size(req)
    }

    fn on_event(&mut self, ch: Event) -> EventResult {
        self.wrap_on_event(ch)
    }

    fn layout(&mut self, size: Vec2) {
        self.wrap_layout(size);
    }

    fn take_focus(&mut self, source: Direction) -> bool {
        self.wrap_take_focus(source)
    }

    fn find_any(&mut self, selector: &Selector) -> Option<&mut Any> {
        self.wrap_find_any(selector)
    }

    fn needs_relayout(&self) -> bool {
        self.wrap_needs_relayout()
    }
}

/// Convenient macro to implement the [`ViewWrapper`] trait.
///
/// It defines the `get_view` and `get_view_mut` implementations,
/// as well as the `type V` declaration.
///
/// [`ViewWrapper`]: view/trait.ViewWrapper.html
///
/// # Examples
///
/// ```no_run
/// # #[macro_use] extern crate cursive;
/// # use cursive::view::{View,ViewWrapper};
/// struct FooView<T: View> {
///     view: T,
/// }
///
/// impl <T: View> ViewWrapper for FooView<T> {
///     wrap_impl!(self.view: T);
/// }
/// # fn main() { }
/// ```
#[macro_export]
macro_rules! wrap_impl {
    (self.$v:ident: $t:path) => {
        type V = $t;

        fn get_view(&self) -> &Self::V {
            &self.$v
        }

        fn get_view_mut(&mut self) -> &mut Self::V {
            &mut self.$v
        }
    };
}
<commit_msg>Use better type for `wrap_impl!` macro<commit_after>use std::any::Any;

use direction::Direction;
use vec::Vec2;
use view::{Selector, View};
use Printer;
use event::{Event, EventResult};

/// Generic wrapper around a view.
///
/// Default implementation forwards all calls to the child view.
/// Overrides some methods as desired.
///
/// You can use the [`wrap_impl!`] macro to define `get_view` and
/// `get_view_mut` for you.
///
/// [`wrap_impl!`]: ../macro.wrap_impl.html
pub trait ViewWrapper {
    /// Type that this view wraps.
    type V: View;

    /// Get an immutable reference to the wrapped view.
    fn get_view(&self) -> &Self::V;

    /// Get a mutable reference to the wrapped view.
    fn get_view_mut(&mut self) -> &mut Self::V;

    /// Wraps the `draw` method.
    fn wrap_draw(&self, printer: &Printer) {
        self.get_view().draw(printer);
    }

    /// Wraps the `get_min_size` method.
    fn wrap_get_min_size(&mut self, req: Vec2) -> Vec2 {
        self.get_view_mut().get_min_size(req)
    }

    /// Wraps the `on_event` method.
    fn wrap_on_event(&mut self, ch: Event) -> EventResult {
        self.get_view_mut().on_event(ch)
    }

    /// Wraps the `layout` method.
    fn wrap_layout(&mut self, size: Vec2) {
        self.get_view_mut().layout(size);
    }

    /// Wraps the `take_focus` method.
    fn wrap_take_focus(&mut self, source: Direction) -> bool {
        self.get_view_mut().take_focus(source)
    }

    /// Wraps the `find` method.
    fn wrap_find_any(&mut self, selector: &Selector) -> Option<&mut Any> {
        self.get_view_mut().find_any(selector)
    }

    /// Wraps the `needs_relayout` method.
    fn wrap_needs_relayout(&self) -> bool {
        self.get_view().needs_relayout()
    }
}

impl<T: ViewWrapper> View for T {
    fn draw(&self, printer: &Printer) {
        self.wrap_draw(printer);
    }

    fn get_min_size(&mut self, req: Vec2) -> Vec2 {
        self.wrap_get_min_size(req)
    }

    fn on_event(&mut self, ch: Event) -> EventResult {
        self.wrap_on_event(ch)
    }

    fn layout(&mut self, size: Vec2) {
        self.wrap_layout(size);
    }

    fn take_focus(&mut self, source: Direction) -> bool {
        self.wrap_take_focus(source)
    }

    fn find_any(&mut self, selector: &Selector) -> Option<&mut Any> {
        self.wrap_find_any(selector)
    }

    fn needs_relayout(&self) -> bool {
        self.wrap_needs_relayout()
    }
}

/// Convenient macro to implement the [`ViewWrapper`] trait.
///
/// It defines the `get_view` and `get_view_mut` implementations,
/// as well as the `type V` declaration.
///
/// [`ViewWrapper`]: view/trait.ViewWrapper.html
///
/// # Examples
///
/// ```no_run
/// # #[macro_use] extern crate cursive;
/// # use cursive::view::{View,ViewWrapper};
/// struct FooView<T: View> {
///     view: T,
/// }
///
/// impl <T: View> ViewWrapper for FooView<T> {
///     wrap_impl!(self.view: T);
/// }
/// # fn main() { }
/// ```
#[macro_export]
macro_rules! wrap_impl {
    (self.$v:ident: $t:ty) => {
        type V = $t;

        fn get_view(&self) -> &Self::V {
            &self.$v
        }

        fn get_view_mut(&mut self) -> &mut Self::V {
            &mut self.$v
        }
    };
}
<|endoftext|>


<commit_before>//! Iron's HTTP Response representation and associated methods.

use std::io::{IoResult, File, MemReader};
use std::path::BytesContainer;

use typemap::TypeMap;
use plugin::Extensible;
use super::status;
use super::status::Status;
use http::headers::response::HeaderCollection;
use http::headers::content_type::MediaType;

pub use http::server::response::ResponseWriter as HttpResponse;

use content_type::get_content_type;

/// The response representation given to `Middleware`
pub struct Response {
    /// The body of the response.
    ///
    /// This is a Reader for generality, most data should
    /// be sent using either `serve` or `serve_file`.
    ///
    /// Arbitrary Readers can be sent by assigning to body.
    pub body: Option<Box<Reader + Send>>,

    /// The headers of the response.
    pub headers: HeaderCollection,

    /// The response status-code.
    pub status: Option<Status>,

    /// A TypeMap to be used as an extensible storage for data
    /// associated with this Response.
    pub extensions: TypeMap
}

impl Response {
    /// Construct a blank Response
    pub fn new() -> Response {
        Response {
            headers: HeaderCollection::new(),
            status: None, // Start with no response code.
            body: None, // Start with no body.
            extensions: TypeMap::new()
        }
    }

    /// Create a new response with the status.
    pub fn status(status: status::Status) -> Response {
        Response {
            body: None,
            headers: HeaderCollection::new(),
            status: Some(status),
            extensions: TypeMap::new()
        }
    }

    /// Create a new response with the specified body and status.
    pub fn with<B: BytesContainer>(status: status::Status, body: B) -> Response {
        Response {
            body: Some(box MemReader::new(body.container_as_bytes().to_vec()) as Box<Reader + Send>),
            headers: HeaderCollection::new(),
            status: Some(status),
            extensions: TypeMap::new()
        }
    }

    /// Create a response from a file on disk.
    ///
    /// The status code is set to 200 OK and the content type is autodetected based on
    /// the file extension.
    pub fn from_file(path: &Path) -> IoResult<Response> {
        let file = try!(File::open(path));
        let mut response = Response::new();
        response.status = Some(status::Ok);
        response.body = Some(box file as Box<Reader + Send>);
        response.headers.content_type = path.extension_str().and_then(get_content_type);
        Ok(response)
    }

    // `write_back` is used to put all the data added to `self`
    // back onto an `HttpResponse` so that it is sent back to the
    // client.
    //
    // `write_back` consumes the `Response`.
    #[doc(hidden)]
    pub fn write_back(self, http_res: &mut HttpResponse) {
        http_res.headers = self.headers.clone();

        // Default to a 404 if no response code was set
        http_res.status = self.status.clone().unwrap_or(status::NotFound);

        // Read the body into the http_res body
        let mut body = self.body.unwrap_or_else(|| box MemReader::new(vec![]) as Box<Reader + Send>);
        let _ = match body.read_to_end() {
            Ok(body_content) => {
                let plain_txt = MediaType {
                    type_: "text".to_string(),
                    subtype: "plain".to_string(),
                    parameters: vec![]
                };

                // Set content length and type
                http_res.headers.content_length =
                    Some(body_content.len());
                http_res.headers.content_type =
                    Some(http_res.headers.content_type.clone().unwrap_or(plain_txt));

                // Write the body
                http_res.write(body_content.as_slice())
            },
            Err(e) => Err(e)
        // Catch errors from reading + writing
        }.map_err(|e| {
            error!("Error reading/writing body: {}", e);
            http_res.status = status::InternalServerError;
            let _ = http_res.write(b"Internal Server Error")
                // Something is really, really wrong.
                .map_err(|e| error!("Error writing error message: {}", e));
        });
    }
}

// Allow plugins to attach to responses.
impl Extensible for Response {
    fn extensions(&self) -> &TypeMap {
        &self.extensions
    }

    fn extensions_mut(&mut self) -> &mut TypeMap {
        &mut self.extensions
    }
}

#[test]
fn matches_content_type () {
    let path = &Path::new("test.txt");
    let content_type = path.extension_str().and_then(get_content_type).unwrap();

    assert_eq!(content_type.type_.as_slice(), "text");
    assert_eq!(content_type.subtype.as_slice(), "plain");
}
<commit_msg>(feat) Show impl for Response.<commit_after>//! Iron's HTTP Response representation and associated methods.

use std::io::{IoResult, File, MemReader};
use std::path::BytesContainer;
use std::fmt::{Show, Formatter, FormatError};

use typemap::TypeMap;
use plugin::Extensible;
use super::status;
use super::status::Status;
use http::headers::response::HeaderCollection;
use http::headers::content_type::MediaType;

pub use http::server::response::ResponseWriter as HttpResponse;

use content_type::get_content_type;

/// The response representation given to `Middleware`
pub struct Response {
    /// The body of the response.
    ///
    /// This is a Reader for generality, most data should
    /// be sent using either `serve` or `serve_file`.
    ///
    /// Arbitrary Readers can be sent by assigning to body.
    pub body: Option<Box<Reader + Send>>,

    /// The headers of the response.
    pub headers: HeaderCollection,

    /// The response status-code.
    pub status: Option<Status>,

    /// A TypeMap to be used as an extensible storage for data
    /// associated with this Response.
    pub extensions: TypeMap
}

impl Response {
    /// Construct a blank Response
    pub fn new() -> Response {
        Response {
            headers: HeaderCollection::new(),
            status: None, // Start with no response code.
            body: None, // Start with no body.
            extensions: TypeMap::new()
        }
    }

    /// Create a new response with the status.
    pub fn status(status: status::Status) -> Response {
        Response {
            body: None,
            headers: HeaderCollection::new(),
            status: Some(status),
            extensions: TypeMap::new()
        }
    }

    /// Create a new response with the specified body and status.
    pub fn with<B: BytesContainer>(status: status::Status, body: B) -> Response {
        Response {
            body: Some(box MemReader::new(body.container_as_bytes().to_vec()) as Box<Reader + Send>),
            headers: HeaderCollection::new(),
            status: Some(status),
            extensions: TypeMap::new()
        }
    }

    /// Create a response from a file on disk.
    ///
    /// The status code is set to 200 OK and the content type is autodetected based on
    /// the file extension.
    pub fn from_file(path: &Path) -> IoResult<Response> {
        let file = try!(File::open(path));
        let mut response = Response::new();
        response.status = Some(status::Ok);
        response.body = Some(box file as Box<Reader + Send>);
        response.headers.content_type = path.extension_str().and_then(get_content_type);
        Ok(response)
    }

    // `write_back` is used to put all the data added to `self`
    // back onto an `HttpResponse` so that it is sent back to the
    // client.
    //
    // `write_back` consumes the `Response`.
    #[doc(hidden)]
    pub fn write_back(self, http_res: &mut HttpResponse) {
        http_res.headers = self.headers.clone();

        // Default to a 404 if no response code was set
        http_res.status = self.status.clone().unwrap_or(status::NotFound);

        // Read the body into the http_res body
        let mut body = self.body.unwrap_or_else(|| box MemReader::new(vec![]) as Box<Reader + Send>);
        let _ = match body.read_to_end() {
            Ok(body_content) => {
                let plain_txt = MediaType {
                    type_: "text".to_string(),
                    subtype: "plain".to_string(),
                    parameters: vec![]
                };

                // Set content length and type
                http_res.headers.content_length =
                    Some(body_content.len());
                http_res.headers.content_type =
                    Some(http_res.headers.content_type.clone().unwrap_or(plain_txt));

                // Write the body
                http_res.write(body_content.as_slice())
            },
            Err(e) => Err(e)
        // Catch errors from reading + writing
        }.map_err(|e| {
            error!("Error reading/writing body: {}", e);
            http_res.status = status::InternalServerError;
            let _ = http_res.write(b"Internal Server Error")
                // Something is really, really wrong.
                .map_err(|e| error!("Error writing error message: {}", e));
        });
    }
}

impl Show for Response {
    fn fmt(&self, f: &mut Formatter) -> Result<(), FormatError> {
        try!(writeln!(f, "Response {{"));

        try!(writeln!(f, "    status: {}", self.status));

        try!(write!(f, "}}"));
        Ok(())
    }
}

// Allow plugins to attach to responses.
impl Extensible for Response {
    fn extensions(&self) -> &TypeMap {
        &self.extensions
    }

    fn extensions_mut(&mut self) -> &mut TypeMap {
        &mut self.extensions
    }
}

#[test]
fn matches_content_type () {
    let path = &Path::new("test.txt");
    let content_type = path.extension_str().and_then(get_content_type).unwrap();

    assert_eq!(content_type.type_.as_slice(), "text");
    assert_eq!(content_type.subtype.as_slice(), "plain");
}
<|endoftext|>


<commit_before>use common::{Header, HTTPVersion, StatusCode};
use std::io::{IoResult, MemReader};
use std::io::fs::File;
use std::io::util;
use std::io::util::NullReader;

/// Object representing an HTTP response whose purpose is to be given to a `Request`.
///
/// Some headers cannot be changed. Trying to define the value
///  of one of these will have no effect:
///
///  - `Accept-Ranges`
///  - `Connection`
///  - `Content-Range`
///  - `Trailer`
///  - `Transfer-Encoding`
///  - `Upgrade`
///
/// Some headers have special behaviors:
///
///  - `Content-Encoding`: If you define this header, the library
///      will assume that the data from the `Reader` has the specified encoding
///      and will just pass-through.
///  - `Content-Length`: If you define this header to `N`, only the first `N` bytes
///      of the `Reader` will be read. If the `Reader` reaches `EOF` before `N` bytes have
///      been read, `0`s will be sent. Also, this header may not be passed to the final
///      output.
///
#[experimental]
pub struct Response<R> {
    reader: R,
    status_code: StatusCode,
    headers: Vec<Header>,
    data_length: Option<uint>,
}

/// Transfer encoding to use when sending the message.
/// Note that only *supported* encoding are listed here.
enum TransferEncoding {
    Identity,
    Chunked,
}

impl ::std::from_str::FromStr for TransferEncoding {
    fn from_str(input: &str) -> Option<TransferEncoding> {
        use std::ascii::StrAsciiExt;

        if input.eq_ignore_ascii_case("identity") {
            return Some(Identity);
        } else if input.eq_ignore_ascii_case("chunked") {
            return Some(Chunked);
        }

        None
    }
}

/// Builds a Date: header with the current date.
// TODO: this is optimisable
fn build_date_header() -> Header {
    use time;
    let date = time::now_utc().strftime("%a, %d %b %Y %H:%M:%S GMT");
    from_str(format!("Date: {}", date).as_slice()).unwrap()
}

fn write_message_header<W: Writer>(mut writer: W, http_version: &HTTPVersion,
                                   status_code: &StatusCode, headers: &[Header])
    -> IoResult<()>
{
    // writing status line
    try!(write!(writer, "HTTP/{} {} {}\r\n",
        http_version,
        status_code.as_uint(),
        status_code.get_default_reason_phrase()
    ));

    // writing headers
    for header in headers.iter() {
        try!(write!(writer, "{}: {}\r\n", header.field, header.value));
    }

    // separator between header and data
    try!(write!(writer, "\r\n"));

    Ok(())
}

fn choose_transfer_encoding(request_headers: &[Header], http_version: &HTTPVersion,
                            entity_length: &Option<uint>)
    -> TransferEncoding
{
    use util;

    // HTTP 1.0 doesn't support other encoding
    if *http_version <= HTTPVersion(1, 0) {
        return Identity;
    }

    // parsing the request's TE header
    let user_request = request_headers.iter()
        // finding TE
        .find(|h| h.field.equiv(&"TE"))

        // getting its value
        .map(|h| h.value.as_slice())

        // getting the corresponding TransferEncoding
        .and_then(|value| {
            // getting list of requested elements
            let mut parse = util::parse_header_value(value);

            // sorting elements by most priority
            parse.sort_by(|a, b| b.val1().partial_cmp(&a.val1()).unwrap_or(Equal));

            // trying to parse each requested encoding
            for value in parse.iter() {
                match from_str::<TransferEncoding>(value.val0()) {
                    Some(te) => return Some(te),
                    _ => ()     // unrecognized/unsupported encoding
                };
            }

            // encoding not found
            None
        });

    //
    if user_request.is_some() {
        return user_request.unwrap();
    }

    // if we don't have a Content-Length, or if the Content-Length is too big, using chunks writer
    let chunks_threshold = 32768;
    if entity_length.as_ref().filtered(|val| **val < chunks_threshold).is_none() {
        return Chunked;
    }

    // Identity by default
    Identity
}

impl<R: Reader> Response<R> {
    #[experimental]
    pub fn new(status_code: StatusCode, headers: Vec<Header>,
               data: R, data_length: Option<uint>) -> Response<R>
    {
        let mut response = Response {
            reader: data,
            status_code: status_code,
            headers: Vec::new(),
            data_length: data_length,
        };

        for h in headers.move_iter() {
            response.add_header(h)
        }

        response
    }

    /// Adds a header to the list.
    /// Does all the checks.
    #[experimental]
    pub fn add_header(&mut self, header: Header) {
        // ignoring forbidden headers
        if header.field.equiv(&"Accept-Ranges") ||
           header.field.equiv(&"Connection") ||
           header.field.equiv(&"Content-Range") ||
           header.field.equiv(&"Trailer") ||
           header.field.equiv(&"Transfer-Encoding") ||
           header.field.equiv(&"Upgrade")
        {
            return;
        }

        // if the header is Content-Length, setting the data length
        if header.field.equiv(&"Content-Length") {
            match from_str::<uint>(header.value.as_slice()) {
                Some(val) => self.data_length = Some(val),
                None => ()      // wrong value for content-length
            };

            return;
        }

        self.headers.push(header)
    }

    /// Returns the same request, but with an additional header.
    ///
    /// Some headers cannot be modified and some other have a
    ///  special behavior. See the documentation above.
    #[unstable]
    pub fn with_header(mut self, header: Header) -> Response<R> {
        self.add_header(header);
        self
    }

    /// Returns the same request, but with a different status code.
    #[unstable]
    pub fn with_status_code(mut self, code: StatusCode) -> Response<R> {
        self.status_code = code;
        self
    }

    /// Prints the HTTP response to a writer.
    ///
    /// This function is the one used to send the response to the client's socket.
    /// Therefore you shouldn't expect anything pretty-printed or even readable.
    ///
    /// The HTTP version and headers passed as arguments are used to
    ///  decide which features (most notably, encoding) to use.
    #[unstable]
    pub fn raw_print<W: Writer>(mut self, mut writer: W, http_version: HTTPVersion,
                                request_headers: &[Header], do_not_send_body: bool)
                                -> IoResult<()>
    {
        let transfer_encoding = choose_transfer_encoding(request_headers,
                                    &http_version, &self.data_length);

        // add `Date` if not in the headers
        if self.headers.iter().find(|h| h.field.equiv(&"Date")).is_none() {
            self.headers.unshift(build_date_header());
        }

        // add `Server` if not in the headers
        if self.headers.iter().find(|h| h.field.equiv(&"Server")).is_none() {
            self.headers.unshift(
                from_str("Server: tiny-http (Rust)").unwrap()
            );
        }

        // checking whether to ignore the body of the response
        let do_not_send_body = do_not_send_body ||
            match self.status_code.as_uint() {
                // sattus code 1xx, 204 and 304 MUST not include a body
                100..199 | 204 | 304 => true,
                _ => false
            };

        // preparing headers for transfer
        match transfer_encoding {
            Chunked => {
                self.headers.push(
                    from_str("Transfer-Encoding: chunked").unwrap()
                )
            },

            Identity => {
                assert!(self.data_length.is_some());
                let data_length = self.data_length.unwrap();

                self.headers.push(
                    from_str(format!("Content-Length: {}", data_length).as_slice()).unwrap()
                )
            }
        };

        // sending headers
        try!(write_message_header(writer.by_ref(), &http_version,
            &self.status_code, self.headers.as_slice()));

        // sending the body
        if !do_not_send_body {
            match transfer_encoding {

                Chunked => {
                    use util::ChunksEncoder;

                    let mut writer = ChunksEncoder::new(writer);
                    try!(util::copy(&mut self.reader, &mut writer));
                },

                Identity => {
                    use util::EqualReader;

                    assert!(self.data_length.is_some());
                    let data_length = self.data_length.unwrap();

                    if data_length >= 1 {
                        let (mut equ_reader, _) =
                            EqualReader::new(self.reader.by_ref(), data_length);
                        try!(util::copy(&mut equ_reader, &mut writer));
                    }
                }

            }
        }

        Ok(())
    }
}

impl Response<File> {
    /// Builds a new `Response` from a `File`.
    ///
    /// The `Content-Type` will **not** be automatically detected,
    ///  you must set it yourself.
    #[experimental]
    pub fn from_file(mut file: File) -> Response<File> {
        let file_size = file.stat().ok().map(|v| v.size as uint);

        Response::new(
            StatusCode(200),
            Vec::new(),
            file,
            file_size
        )
    }
}

impl Response<MemReader> {
    #[experimental]
    pub fn from_data(data: Vec<u8>) -> Response<MemReader> {
        let data_len = data.len();

        Response::new(
            StatusCode(200),
            Vec::new(),
            MemReader::new(data),
            Some(data_len)
        )
    }

    #[experimental]
    pub fn from_string(data: String) -> Response<MemReader> {
        let data_len = data.len();

        Response::new(
            StatusCode(200),
            vec!(
                from_str("Content-Type: text/plain; charset=UTF-8").unwrap()
            ),
            MemReader::new(data.into_bytes()),
            Some(data_len)
        )
    }
}

impl Response<NullReader> {
    /// Builds an empty `Response` with the given status code.
    #[experimental]
    pub fn new_empty(status_code: StatusCode) -> Response<NullReader> {
        Response::new(
            status_code,
            Vec::new(),
            NullReader,
            Some(0)
        )
    }
}
<commit_msg>TE with q=0 are ignored<commit_after>use common::{Header, HTTPVersion, StatusCode};
use std::io::{IoResult, MemReader};
use std::io::fs::File;
use std::io::util;
use std::io::util::NullReader;

/// Object representing an HTTP response whose purpose is to be given to a `Request`.
///
/// Some headers cannot be changed. Trying to define the value
///  of one of these will have no effect:
///
///  - `Accept-Ranges`
///  - `Connection`
///  - `Content-Range`
///  - `Trailer`
///  - `Transfer-Encoding`
///  - `Upgrade`
///
/// Some headers have special behaviors:
///
///  - `Content-Encoding`: If you define this header, the library
///      will assume that the data from the `Reader` has the specified encoding
///      and will just pass-through.
///  - `Content-Length`: If you define this header to `N`, only the first `N` bytes
///      of the `Reader` will be read. If the `Reader` reaches `EOF` before `N` bytes have
///      been read, `0`s will be sent. Also, this header may not be passed to the final
///      output.
///
#[experimental]
pub struct Response<R> {
    reader: R,
    status_code: StatusCode,
    headers: Vec<Header>,
    data_length: Option<uint>,
}

/// Transfer encoding to use when sending the message.
/// Note that only *supported* encoding are listed here.
enum TransferEncoding {
    Identity,
    Chunked,
}

impl ::std::from_str::FromStr for TransferEncoding {
    fn from_str(input: &str) -> Option<TransferEncoding> {
        use std::ascii::StrAsciiExt;

        if input.eq_ignore_ascii_case("identity") {
            return Some(Identity);
        } else if input.eq_ignore_ascii_case("chunked") {
            return Some(Chunked);
        }

        None
    }
}

/// Builds a Date: header with the current date.
// TODO: this is optimisable
fn build_date_header() -> Header {
    use time;
    let date = time::now_utc().strftime("%a, %d %b %Y %H:%M:%S GMT");
    from_str(format!("Date: {}", date).as_slice()).unwrap()
}

fn write_message_header<W: Writer>(mut writer: W, http_version: &HTTPVersion,
                                   status_code: &StatusCode, headers: &[Header])
    -> IoResult<()>
{
    // writing status line
    try!(write!(writer, "HTTP/{} {} {}\r\n",
        http_version,
        status_code.as_uint(),
        status_code.get_default_reason_phrase()
    ));

    // writing headers
    for header in headers.iter() {
        try!(write!(writer, "{}: {}\r\n", header.field, header.value));
    }

    // separator between header and data
    try!(write!(writer, "\r\n"));

    Ok(())
}

fn choose_transfer_encoding(request_headers: &[Header], http_version: &HTTPVersion,
                            entity_length: &Option<uint>)
    -> TransferEncoding
{
    use util;

    // HTTP 1.0 doesn't support other encoding
    if *http_version <= HTTPVersion(1, 0) {
        return Identity;
    }

    // parsing the request's TE header
    let user_request = request_headers.iter()
        // finding TE
        .find(|h| h.field.equiv(&"TE"))

        // getting its value
        .map(|h| h.value.as_slice())

        // getting the corresponding TransferEncoding
        .and_then(|value| {
            // getting list of requested elements
            let mut parse = util::parse_header_value(value);

            // sorting elements by most priority
            parse.sort_by(|a, b| b.val1().partial_cmp(&a.val1()).unwrap_or(Equal));

            // trying to parse each requested encoding
            for value in parse.iter() {
                // q=0 are ignored
                if value.val1() <= 0.0 { continue }

                match from_str::<TransferEncoding>(value.val0()) {
                    Some(te) => return Some(te),
                    _ => ()     // unrecognized/unsupported encoding
                };
            }

            // encoding not found
            None
        });

    //
    if user_request.is_some() {
        return user_request.unwrap();
    }

    // if we don't have a Content-Length, or if the Content-Length is too big, using chunks writer
    let chunks_threshold = 32768;
    if entity_length.as_ref().filtered(|val| **val < chunks_threshold).is_none() {
        return Chunked;
    }

    // Identity by default
    Identity
}

impl<R: Reader> Response<R> {
    #[experimental]
    pub fn new(status_code: StatusCode, headers: Vec<Header>,
               data: R, data_length: Option<uint>) -> Response<R>
    {
        let mut response = Response {
            reader: data,
            status_code: status_code,
            headers: Vec::new(),
            data_length: data_length,
        };

        for h in headers.move_iter() {
            response.add_header(h)
        }

        response
    }

    /// Adds a header to the list.
    /// Does all the checks.
    #[experimental]
    pub fn add_header(&mut self, header: Header) {
        // ignoring forbidden headers
        if header.field.equiv(&"Accept-Ranges") ||
           header.field.equiv(&"Connection") ||
           header.field.equiv(&"Content-Range") ||
           header.field.equiv(&"Trailer") ||
           header.field.equiv(&"Transfer-Encoding") ||
           header.field.equiv(&"Upgrade")
        {
            return;
        }

        // if the header is Content-Length, setting the data length
        if header.field.equiv(&"Content-Length") {
            match from_str::<uint>(header.value.as_slice()) {
                Some(val) => self.data_length = Some(val),
                None => ()      // wrong value for content-length
            };

            return;
        }

        self.headers.push(header)
    }

    /// Returns the same request, but with an additional header.
    ///
    /// Some headers cannot be modified and some other have a
    ///  special behavior. See the documentation above.
    #[unstable]
    pub fn with_header(mut self, header: Header) -> Response<R> {
        self.add_header(header);
        self
    }

    /// Returns the same request, but with a different status code.
    #[unstable]
    pub fn with_status_code(mut self, code: StatusCode) -> Response<R> {
        self.status_code = code;
        self
    }

    /// Prints the HTTP response to a writer.
    ///
    /// This function is the one used to send the response to the client's socket.
    /// Therefore you shouldn't expect anything pretty-printed or even readable.
    ///
    /// The HTTP version and headers passed as arguments are used to
    ///  decide which features (most notably, encoding) to use.
    #[unstable]
    pub fn raw_print<W: Writer>(mut self, mut writer: W, http_version: HTTPVersion,
                                request_headers: &[Header], do_not_send_body: bool)
                                -> IoResult<()>
    {
        let transfer_encoding = choose_transfer_encoding(request_headers,
                                    &http_version, &self.data_length);

        // add `Date` if not in the headers
        if self.headers.iter().find(|h| h.field.equiv(&"Date")).is_none() {
            self.headers.unshift(build_date_header());
        }

        // add `Server` if not in the headers
        if self.headers.iter().find(|h| h.field.equiv(&"Server")).is_none() {
            self.headers.unshift(
                from_str("Server: tiny-http (Rust)").unwrap()
            );
        }

        // checking whether to ignore the body of the response
        let do_not_send_body = do_not_send_body ||
            match self.status_code.as_uint() {
                // sattus code 1xx, 204 and 304 MUST not include a body
                100..199 | 204 | 304 => true,
                _ => false
            };

        // preparing headers for transfer
        match transfer_encoding {
            Chunked => {
                self.headers.push(
                    from_str("Transfer-Encoding: chunked").unwrap()
                )
            },

            Identity => {
                assert!(self.data_length.is_some());
                let data_length = self.data_length.unwrap();

                self.headers.push(
                    from_str(format!("Content-Length: {}", data_length).as_slice()).unwrap()
                )
            }
        };

        // sending headers
        try!(write_message_header(writer.by_ref(), &http_version,
            &self.status_code, self.headers.as_slice()));

        // sending the body
        if !do_not_send_body {
            match transfer_encoding {

                Chunked => {
                    use util::ChunksEncoder;

                    let mut writer = ChunksEncoder::new(writer);
                    try!(util::copy(&mut self.reader, &mut writer));
                },

                Identity => {
                    use util::EqualReader;

                    assert!(self.data_length.is_some());
                    let data_length = self.data_length.unwrap();

                    if data_length >= 1 {
                        let (mut equ_reader, _) =
                            EqualReader::new(self.reader.by_ref(), data_length);
                        try!(util::copy(&mut equ_reader, &mut writer));
                    }
                }

            }
        }

        Ok(())
    }
}

impl Response<File> {
    /// Builds a new `Response` from a `File`.
    ///
    /// The `Content-Type` will **not** be automatically detected,
    ///  you must set it yourself.
    #[experimental]
    pub fn from_file(mut file: File) -> Response<File> {
        let file_size = file.stat().ok().map(|v| v.size as uint);

        Response::new(
            StatusCode(200),
            Vec::new(),
            file,
            file_size
        )
    }
}

impl Response<MemReader> {
    #[experimental]
    pub fn from_data(data: Vec<u8>) -> Response<MemReader> {
        let data_len = data.len();

        Response::new(
            StatusCode(200),
            Vec::new(),
            MemReader::new(data),
            Some(data_len)
        )
    }

    #[experimental]
    pub fn from_string(data: String) -> Response<MemReader> {
        let data_len = data.len();

        Response::new(
            StatusCode(200),
            vec!(
                from_str("Content-Type: text/plain; charset=UTF-8").unwrap()
            ),
            MemReader::new(data.into_bytes()),
            Some(data_len)
        )
    }
}

impl Response<NullReader> {
    /// Builds an empty `Response` with the given status code.
    #[experimental]
    pub fn new_empty(status_code: StatusCode) -> Response<NullReader> {
        Response::new(
            status_code,
            Vec::new(),
            NullReader,
            Some(0)
        )
    }
}
<|endoftext|>


<commit_before>//  Copyright (C) 2020 Éloïs SANCHEZ.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

#![deny(
    clippy::unwrap_used,
    missing_debug_implementations,
    missing_copy_implementations,
    trivial_casts,
    trivial_numeric_casts,
    unsafe_code,
    unstable_features,
    unused_import_braces
)]

mod config;
mod daemon;
mod duniter_ts_args;
mod sync;
mod wizard_gva;

use anyhow::{anyhow, Result};
use daemonize_me::Daemon;
use logwatcher::{LogWatcher, LogWatcherAction};
use nix::{errno::Errno, sys::signal::Signal, unistd::Pid, Error};
use std::{
    fs::File, io::prelude::*, path::Path, path::PathBuf, process::Command, process::Output,
    process::Stdio,
};
use structopt::{clap::Shell, StructOpt};

const APP_NAME: &str = "duniter";
const DEFAULT_PORT: u16 = 443;
const DEFAULT_PROFILE: &str = "duniter_default";
const DUNITER_EXE_PATH: &str = "/opt/duniter/bin/duniter";
const DUNITER_EXE_LINK_PATH: &str = "/usr/bin/duniter";
const DUNITER_JS_CURRENT_DIR: &str = "/opt/duniter/";
const DUNITER_JS_PATH: &str = "/opt/duniter/bin/duniter_js";
const DUNITER_JS_DEV_PATH: &str = "bin/duniter_js";
const EMBEDDED_NODE_PATH: &str = "/opt/duniter/node/bin/node";
const EXIT_CODE_DUNITER_NOT_RUNNING: i32 = 4;
const LOG_FILE: &str = "duniter.log";
const NODE_VERSION_BEGIN: &str = "v10.";

#[derive(StructOpt)]
#[structopt(name = APP_NAME, about = "Crypto-currency software to operate Ğ1 libre currency.")]
struct DuniterArgs {
    /// Path to Duniter HOME (defaults to "$HOME/.config/duniter").
    #[structopt(short, long, parse(from_os_str))]
    home: Option<PathBuf>,
    /// Logs level (If not specified, use the logs level defined in the configuration or INFO by default).
    #[structopt(short, long, alias("loglevel"), case_insensitive(true), possible_values = &["OFF", "ERROR", "WARN", "INFO", "DEBUG", "TRACE"])]
    log: Option<log::LevelFilter>,
    /// Profile name (defauld "duniter_default")
    #[structopt(short, long, alias("mdb"))]
    profile: Option<String>,
    #[structopt(subcommand)]
    command: DuniterCommand,
}

#[derive(StructOpt)]
#[structopt(rename_all = "snake")]
enum DuniterCommand {
    /// Duniter configuration options.
    #[structopt(
        display_order(0),
        after_help("Some advanced options are hidden for readability.")
    )]
    Config(Box<config::DuniterConfigArgs>),
    /// Launch the configuration wizard.
    #[structopt(display_order(1))]
    Wizard(WizardCommand),
    /// WS2P operations for configuration and diagnosis tasks.
    #[structopt(display_order(2))]
    WS2P(WS2PCommand),
    /// Synchronize blockchain from a remote Duniter node.
    #[structopt(display_order(3))]
    Sync(sync::DuniterSyncArgs),
    /// Start Duniter node with direct output, non-daemonized.
    #[structopt(display_order(4))]
    DirectStart {
        /// Force to use the keypair given by user prompt.
        #[structopt(long)]
        keyprompt: bool,
        #[structopt(flatten)]
        start_args: DuniterStartArgs,
    },
    /// Start Duniter node with its web interface with direct output, non-daemonized.
    #[structopt(display_order(5))]
    DirectWebstart {
        /// Force to use the keypair given by user prompt.
        #[structopt(long)]
        keyprompt: bool,
        #[structopt(flatten)]
        start_args: DuniterStartArgs,
        #[structopt(flatten)]
        webstart_args: DuniterWebstartArgs,
    },
    /// Starts Duniter as a daemon (background task).
    #[structopt(display_order(6))]
    Start(DuniterStartArgs),
    /// Starts Duniter (with its web interface) as a daemon (background task).
    #[structopt(display_order(7))]
    Webstart {
        #[structopt(flatten)]
        start_args: DuniterStartArgs,
        #[structopt(flatten)]
        webstart_args: DuniterWebstartArgs,
    },
    /// Get Duniter daemon status.
    #[structopt(display_order(8))]
    Status,
    /// Follow duniter logs.
    #[structopt(display_order(9))]
    Logs,
    /// Stops Duniter daemon and restart it.
    #[structopt(display_order(10), alias = "webrestart")]
    Restart,
    /// Stops Duniter daemon if it is running.
    #[structopt(display_order(11))]
    Stop,
    /// Reset configuration, data, peers, transactions or everything in the database
    #[structopt(display_order(12))]
    Reset(ResetCommand),
    /// Generate tab-completion script for your shell
    #[structopt(display_order(13))]
    Completions {
        #[structopt(case_insensitive(true))]
        shell: Shell,
    },
}

#[derive(StructOpt)]
enum ResetCommand {
    #[structopt(display_order(0))]
    Config,
    #[structopt(display_order(1))]
    Data,
    #[structopt(display_order(2))]
    Peers,
    #[structopt(display_order(3))]
    Stats,
    #[structopt(display_order(4))]
    All,
}

#[derive(StructOpt)]
enum WizardCommand {
    #[structopt(display_order(0))]
    Key {
        /// Scrypt `N` CPU/memory cost parameter. Must be a power of 2. Defaults to 4096.
        #[structopt(short)]
        n: Option<usize>,
        /// "Scrypt `r` The blocksize parameter, which fine-tunes sequential memory read size and performance. Defaults to 16."
        #[structopt(short)]
        r: Option<usize>,
        /// Scrypt `p` Parallelization parameter. Defaults to 1.
        #[structopt(short)]
        p: Option<usize>,
    },
    #[structopt(display_order(1))]
    Gva,
    #[structopt(display_order(2), alias = "network")]
    Bma,
}

#[derive(StructOpt)]
enum WS2PCommand {
    #[structopt(display_order(0))]
    ListNodes,
    #[structopt(display_order(0))]
    ListPrefered,
    #[structopt(display_order(0))]
    ListPrivileged,
    #[structopt(display_order(0))]
    ShowConf,
}

#[derive(StructOpt)]
struct DuniterStartArgs {
    /// Force to use the keypair of the given YAML file. File must contain `pub:` and `sec:` fields.
    #[structopt(long, parse(from_os_str), env("DUNITER_KEYFILE"))]
    keyfile: Option<PathBuf>,
}

#[derive(StructOpt)]
struct DuniterWebstartArgs {
    /// Web user interface host (IP) to listen to.
    #[structopt(long, alias = "webmhost", env("DUNITER_WEB_UI_HOST"))]
    web_ui_host: Option<String>,
    /// Web user interface port (IP) to listen to.
    #[structopt(long, alias = "webmport")]
    web_ui_port: Option<String>,
}

fn main() -> Result<()> {
    let args = DuniterArgs::from_args();

    if let DuniterCommand::Completions { shell } = args.command {
        DuniterArgs::clap().gen_completions_to(APP_NAME, shell, &mut std::io::stdout());
        Ok(())
    } else {
        let profile_path = get_profile_path(args.profile.as_deref())?;

        if let DuniterCommand::Wizard(WizardCommand::Gva) = args.command {
            wizard_gva::wizard_gva(args.profile.as_deref(), profile_path)
        } else {
            let current_exe = std::env::current_exe()?;
            let prod = current_exe == PathBuf::from(DUNITER_EXE_LINK_PATH)
                || current_exe == PathBuf::from(DUNITER_EXE_PATH);

            let duniter_ts_args = duniter_ts_args::gen_duniter_ts_args(&args, duniter_js_exe()?);

            match args.command {
                DuniterCommand::Restart => {
                    daemon::start(prod, &profile_path, &daemon::stop(&profile_path)?)
                }
                DuniterCommand::Start(_) | DuniterCommand::Webstart { .. } => {
                    daemon::start(prod, &profile_path, &duniter_ts_args)
                }
                DuniterCommand::Status => daemon::status(&profile_path),
                DuniterCommand::Stop => {
                    daemon::stop(&profile_path)?;
                    Ok(())
                }
                DuniterCommand::Logs => watch_logs(profile_path),
                _ => {
                    ctrlc::set_handler(move || {
                        // This empty handler is necessary otherwise the Rust process is stopped immediately
                        // without waiting for the child process (duniter_js) to finish stopping.
                    })?;
                    let mut duniter_js_command = Command::new(get_node_path()?);
                    if prod {
                        duniter_js_command.current_dir(DUNITER_JS_CURRENT_DIR);
                    }
                    //println!("TMP duniter_ts_args={:?}", duniter_ts_args);
                    let exit_code_opt = duniter_js_command.args(duniter_ts_args).status()?.code();
                    if let Some(exit_code) = exit_code_opt {
                        std::process::exit(exit_code);
                    } else {
                        Ok(())
                    }
                }
            }
        }
    }
}

fn duniter_js_exe() -> Result<String> {
    let current_exe = std::env::current_exe()?;
    Ok(
        if current_exe == PathBuf::from(DUNITER_EXE_LINK_PATH)
            || current_exe == PathBuf::from(DUNITER_EXE_PATH)
        {
            DUNITER_JS_PATH.to_owned()
        } else {
            DUNITER_JS_DEV_PATH.to_owned()
        },
    )
}

pub(crate) fn get_node_path() -> Result<&'static str> {
    let current_exe = std::env::current_exe()?;
    if current_exe == PathBuf::from(DUNITER_EXE_LINK_PATH)
        || current_exe == PathBuf::from(DUNITER_EXE_PATH)
    {
        let node_path = PathBuf::from(EMBEDDED_NODE_PATH);
        if node_path.exists() {
            Ok(EMBEDDED_NODE_PATH)
        } else {
            eprintln!("Node.js is not embedded in this version of Duniter");
            std::process::exit(1);
        }
    } else if get_node_version("node")?.starts_with(NODE_VERSION_BEGIN) {
        Ok("node")
    } else {
        eprintln!("Duniter require Node.js v10.x");
        std::process::exit(1);
    }
}

pub(crate) fn get_node_version(node_path: &str) -> Result<String> {
    let Output {
        status,
        stdout,
        stderr,
    } = Command::new(node_path).arg("-v").output()?;

    if status.success() {
        Ok(String::from_utf8(stdout)
            .unwrap_or_else(|_| "Output is not a valid utf8 string".to_owned()))
    } else {
        eprintln!(
            "{}",
            String::from_utf8(stderr)
                .unwrap_or_else(|_| "Error message is not a valid utf8 string".to_owned())
        );
        std::process::exit(1);
    }
}

fn get_profile_path(profile: Option<&str>) -> Result<PathBuf> {
    let mut profile_path = dirs::config_dir().expect("unsupported operating system");
    profile_path.push(APP_NAME);
    profile_path.push(profile.unwrap_or(DEFAULT_PROFILE));
    if !profile_path.exists() {
        std::fs::create_dir_all(&profile_path)?;
    }
    Ok(profile_path)
}

fn watch_logs(profile_path: PathBuf) -> Result<()> {
    let mut log_watcher = LogWatcher::register(profile_path.join(LOG_FILE))?;

    log_watcher.watch(&mut move |line: String| {
        println!("{}", line);
        LogWatcherAction::None
    });

    Ok(())
}
<commit_msg>[doc] typo on profile option desc<commit_after>//  Copyright (C) 2020 Éloïs SANCHEZ.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

#![deny(
    clippy::unwrap_used,
    missing_debug_implementations,
    missing_copy_implementations,
    trivial_casts,
    trivial_numeric_casts,
    unsafe_code,
    unstable_features,
    unused_import_braces
)]

mod config;
mod daemon;
mod duniter_ts_args;
mod sync;
mod wizard_gva;

use anyhow::{anyhow, Result};
use daemonize_me::Daemon;
use logwatcher::{LogWatcher, LogWatcherAction};
use nix::{errno::Errno, sys::signal::Signal, unistd::Pid, Error};
use std::{
    fs::File, io::prelude::*, path::Path, path::PathBuf, process::Command, process::Output,
    process::Stdio,
};
use structopt::{clap::Shell, StructOpt};

const APP_NAME: &str = "duniter";
const DEFAULT_PORT: u16 = 443;
const DEFAULT_PROFILE: &str = "duniter_default";
const DUNITER_EXE_PATH: &str = "/opt/duniter/bin/duniter";
const DUNITER_EXE_LINK_PATH: &str = "/usr/bin/duniter";
const DUNITER_JS_CURRENT_DIR: &str = "/opt/duniter/";
const DUNITER_JS_PATH: &str = "/opt/duniter/bin/duniter_js";
const DUNITER_JS_DEV_PATH: &str = "bin/duniter_js";
const EMBEDDED_NODE_PATH: &str = "/opt/duniter/node/bin/node";
const EXIT_CODE_DUNITER_NOT_RUNNING: i32 = 4;
const LOG_FILE: &str = "duniter.log";
const NODE_VERSION_BEGIN: &str = "v10.";

#[derive(StructOpt)]
#[structopt(name = APP_NAME, about = "Crypto-currency software to operate Ğ1 libre currency.")]
struct DuniterArgs {
    /// Path to Duniter HOME (defaults to "$HOME/.config/duniter").
    #[structopt(short, long, parse(from_os_str))]
    home: Option<PathBuf>,
    /// Logs level (If not specified, use the logs level defined in the configuration or INFO by default).
    #[structopt(short, long, alias("loglevel"), case_insensitive(true), possible_values = &["OFF", "ERROR", "WARN", "INFO", "DEBUG", "TRACE"])]
    log: Option<log::LevelFilter>,
    /// Profile name (default "duniter_default")
    #[structopt(short, long, alias("mdb"))]
    profile: Option<String>,
    #[structopt(subcommand)]
    command: DuniterCommand,
}

#[derive(StructOpt)]
#[structopt(rename_all = "snake")]
enum DuniterCommand {
    /// Duniter configuration options.
    #[structopt(
        display_order(0),
        after_help("Some advanced options are hidden for readability.")
    )]
    Config(Box<config::DuniterConfigArgs>),
    /// Launch the configuration wizard.
    #[structopt(display_order(1))]
    Wizard(WizardCommand),
    /// WS2P operations for configuration and diagnosis tasks.
    #[structopt(display_order(2))]
    WS2P(WS2PCommand),
    /// Synchronize blockchain from a remote Duniter node.
    #[structopt(display_order(3))]
    Sync(sync::DuniterSyncArgs),
    /// Start Duniter node with direct output, non-daemonized.
    #[structopt(display_order(4))]
    DirectStart {
        /// Force to use the keypair given by user prompt.
        #[structopt(long)]
        keyprompt: bool,
        #[structopt(flatten)]
        start_args: DuniterStartArgs,
    },
    /// Start Duniter node with its web interface with direct output, non-daemonized.
    #[structopt(display_order(5))]
    DirectWebstart {
        /// Force to use the keypair given by user prompt.
        #[structopt(long)]
        keyprompt: bool,
        #[structopt(flatten)]
        start_args: DuniterStartArgs,
        #[structopt(flatten)]
        webstart_args: DuniterWebstartArgs,
    },
    /// Starts Duniter as a daemon (background task).
    #[structopt(display_order(6))]
    Start(DuniterStartArgs),
    /// Starts Duniter (with its web interface) as a daemon (background task).
    #[structopt(display_order(7))]
    Webstart {
        #[structopt(flatten)]
        start_args: DuniterStartArgs,
        #[structopt(flatten)]
        webstart_args: DuniterWebstartArgs,
    },
    /// Get Duniter daemon status.
    #[structopt(display_order(8))]
    Status,
    /// Follow duniter logs.
    #[structopt(display_order(9))]
    Logs,
    /// Stops Duniter daemon and restart it.
    #[structopt(display_order(10), alias = "webrestart")]
    Restart,
    /// Stops Duniter daemon if it is running.
    #[structopt(display_order(11))]
    Stop,
    /// Reset configuration, data, peers, transactions or everything in the database
    #[structopt(display_order(12))]
    Reset(ResetCommand),
    /// Generate tab-completion script for your shell
    #[structopt(display_order(13))]
    Completions {
        #[structopt(case_insensitive(true))]
        shell: Shell,
    },
}

#[derive(StructOpt)]
enum ResetCommand {
    #[structopt(display_order(0))]
    Config,
    #[structopt(display_order(1))]
    Data,
    #[structopt(display_order(2))]
    Peers,
    #[structopt(display_order(3))]
    Stats,
    #[structopt(display_order(4))]
    All,
}

#[derive(StructOpt)]
enum WizardCommand {
    #[structopt(display_order(0))]
    Key {
        /// Scrypt `N` CPU/memory cost parameter. Must be a power of 2. Defaults to 4096.
        #[structopt(short)]
        n: Option<usize>,
        /// "Scrypt `r` The blocksize parameter, which fine-tunes sequential memory read size and performance. Defaults to 16."
        #[structopt(short)]
        r: Option<usize>,
        /// Scrypt `p` Parallelization parameter. Defaults to 1.
        #[structopt(short)]
        p: Option<usize>,
    },
    #[structopt(display_order(1))]
    Gva,
    #[structopt(display_order(2), alias = "network")]
    Bma,
}

#[derive(StructOpt)]
enum WS2PCommand {
    #[structopt(display_order(0))]
    ListNodes,
    #[structopt(display_order(0))]
    ListPrefered,
    #[structopt(display_order(0))]
    ListPrivileged,
    #[structopt(display_order(0))]
    ShowConf,
}

#[derive(StructOpt)]
struct DuniterStartArgs {
    /// Force to use the keypair of the given YAML file. File must contain `pub:` and `sec:` fields.
    #[structopt(long, parse(from_os_str), env("DUNITER_KEYFILE"))]
    keyfile: Option<PathBuf>,
}

#[derive(StructOpt)]
struct DuniterWebstartArgs {
    /// Web user interface host (IP) to listen to.
    #[structopt(long, alias = "webmhost", env("DUNITER_WEB_UI_HOST"))]
    web_ui_host: Option<String>,
    /// Web user interface port (IP) to listen to.
    #[structopt(long, alias = "webmport")]
    web_ui_port: Option<String>,
}

fn main() -> Result<()> {
    let args = DuniterArgs::from_args();

    if let DuniterCommand::Completions { shell } = args.command {
        DuniterArgs::clap().gen_completions_to(APP_NAME, shell, &mut std::io::stdout());
        Ok(())
    } else {
        let profile_path = get_profile_path(args.profile.as_deref())?;

        if let DuniterCommand::Wizard(WizardCommand::Gva) = args.command {
            wizard_gva::wizard_gva(args.profile.as_deref(), profile_path)
        } else {
            let current_exe = std::env::current_exe()?;
            let prod = current_exe == PathBuf::from(DUNITER_EXE_LINK_PATH)
                || current_exe == PathBuf::from(DUNITER_EXE_PATH);

            let duniter_ts_args = duniter_ts_args::gen_duniter_ts_args(&args, duniter_js_exe()?);

            match args.command {
                DuniterCommand::Restart => {
                    daemon::start(prod, &profile_path, &daemon::stop(&profile_path)?)
                }
                DuniterCommand::Start(_) | DuniterCommand::Webstart { .. } => {
                    daemon::start(prod, &profile_path, &duniter_ts_args)
                }
                DuniterCommand::Status => daemon::status(&profile_path),
                DuniterCommand::Stop => {
                    daemon::stop(&profile_path)?;
                    Ok(())
                }
                DuniterCommand::Logs => watch_logs(profile_path),
                _ => {
                    ctrlc::set_handler(move || {
                        // This empty handler is necessary otherwise the Rust process is stopped immediately
                        // without waiting for the child process (duniter_js) to finish stopping.
                    })?;
                    let mut duniter_js_command = Command::new(get_node_path()?);
                    if prod {
                        duniter_js_command.current_dir(DUNITER_JS_CURRENT_DIR);
                    }
                    //println!("TMP duniter_ts_args={:?}", duniter_ts_args);
                    let exit_code_opt = duniter_js_command.args(duniter_ts_args).status()?.code();
                    if let Some(exit_code) = exit_code_opt {
                        std::process::exit(exit_code);
                    } else {
                        Ok(())
                    }
                }
            }
        }
    }
}

fn duniter_js_exe() -> Result<String> {
    let current_exe = std::env::current_exe()?;
    Ok(
        if current_exe == PathBuf::from(DUNITER_EXE_LINK_PATH)
            || current_exe == PathBuf::from(DUNITER_EXE_PATH)
        {
            DUNITER_JS_PATH.to_owned()
        } else {
            DUNITER_JS_DEV_PATH.to_owned()
        },
    )
}

pub(crate) fn get_node_path() -> Result<&'static str> {
    let current_exe = std::env::current_exe()?;
    if current_exe == PathBuf::from(DUNITER_EXE_LINK_PATH)
        || current_exe == PathBuf::from(DUNITER_EXE_PATH)
    {
        let node_path = PathBuf::from(EMBEDDED_NODE_PATH);
        if node_path.exists() {
            Ok(EMBEDDED_NODE_PATH)
        } else {
            eprintln!("Node.js is not embedded in this version of Duniter");
            std::process::exit(1);
        }
    } else if get_node_version("node")?.starts_with(NODE_VERSION_BEGIN) {
        Ok("node")
    } else {
        eprintln!("Duniter require Node.js v10.x");
        std::process::exit(1);
    }
}

pub(crate) fn get_node_version(node_path: &str) -> Result<String> {
    let Output {
        status,
        stdout,
        stderr,
    } = Command::new(node_path).arg("-v").output()?;

    if status.success() {
        Ok(String::from_utf8(stdout)
            .unwrap_or_else(|_| "Output is not a valid utf8 string".to_owned()))
    } else {
        eprintln!(
            "{}",
            String::from_utf8(stderr)
                .unwrap_or_else(|_| "Error message is not a valid utf8 string".to_owned())
        );
        std::process::exit(1);
    }
}

fn get_profile_path(profile: Option<&str>) -> Result<PathBuf> {
    let mut profile_path = dirs::config_dir().expect("unsupported operating system");
    profile_path.push(APP_NAME);
    profile_path.push(profile.unwrap_or(DEFAULT_PROFILE));
    if !profile_path.exists() {
        std::fs::create_dir_all(&profile_path)?;
    }
    Ok(profile_path)
}

fn watch_logs(profile_path: PathBuf) -> Result<()> {
    let mut log_watcher = LogWatcher::register(profile_path.join(LOG_FILE))?;

    log_watcher.watch(&mut move |line: String| {
        println!("{}", line);
        LogWatcherAction::None
    });

    Ok(())
}
<|endoftext|>