<commit_before>extern crate alpm;
extern crate clap;
extern crate curl;
extern crate env_logger;
extern crate itertools;
extern crate json;
#[macro_use]
extern crate log;
extern crate select;

use clap::{Arg, App};
use curl::easy::Easy;
use itertools::Itertools;
use select::document::Document;
use select::predicate::Name;
use std::cmp::Ordering;
use std::collections::HashMap;
use std::collections::hash_map::Entry::{Occupied, Vacant};
use std::default::Default;
use std::str;

#[derive(Debug)]
struct ASA {
    cve: Vec<String>,
    version: Option<String>,
}

impl Default for ASA {
    fn default() -> ASA {
        ASA {
            cve: Vec::new(),
            version: None,
        }
    }
}

#[derive(Debug)]
struct Options {
    format: Option<String>,
    quiet: u64,
    upgradable_only: bool,
}

impl Default for Options {
    fn default() -> Options {
        Options {
            format: None,
            quiet: 0,
            upgradable_only: false
        }
    }
}

fn main() {
    env_logger::init().unwrap();

    let args = App::new("arch-audit")
                        .version("0.1.2")
                        .arg(Arg::with_name("format")
                             .short("f")
                             .long("format")
                             .takes_value(true)
                             .help("Specify a format to control the output. Placeholders are %n (pkgname) and %c (CVEs)"))
                        .arg(Arg::with_name("quiet")
                             .short("q")
                             .long("quiet")
                             .multiple(true)
                             .help("Show only vulnerable package names and their versions"))
                        .arg(Arg::with_name("upgradable")
                             .short("u")
                             .long("upgradable")
                             .help("Show only packages that have already been fixed"))
                        .get_matches();

    let options = Options {
        format: { match args.value_of("format") {
            Some(f) => { Some(f.to_string()) },
            None => { None },
            } },
        quiet: args.occurrences_of("quiet"),
        upgradable_only: args.is_present("upgradable"),
    };

    let mut wikipage = String::new();
    {
        info!("Downloading CVE wiki page...");
        let wikipage_url = "https://wiki.archlinux.org/api.php?format=json&action=parse&page=CVE&section=5";

        let mut easy = Easy::new();
        easy.url(wikipage_url).unwrap();
        let mut transfer = easy.transfer();
        transfer.write_function(|data| {
            wikipage.push_str(str::from_utf8(data).unwrap());
            Ok(data.len())
        }).unwrap();
        transfer.perform().unwrap();
    }

    let mut infos: HashMap<String, Vec<_>> = HashMap::new();
    {
        let json = json::parse(wikipage.as_str()).unwrap();
        let document = Document::from(json["parse"]["text"]["*"].as_str().unwrap());

        for tr in document.find(Name("tbody")).find(Name("tr")).iter() {
            let tds = tr.find(Name("td"));

            match tds.first() {
                Some(td) => {
                    let mut next = tds.next().next();
                    let pkgname = next.first().unwrap().text().trim().to_string();
                    next = next.next().next().next().next().next().next();
                    let info = ASA {
                        cve: td.text().split_whitespace().filter(|s| s.starts_with("CVE")).map(|s| s.to_string()).collect(),
                        version: {
                            let v = next.first().unwrap().text().trim().to_string();
                            if !v.is_empty() && v != "?".to_string() && v != "-".to_string() { Some(v) } else { None }
                        },
                    };
                    next = next.next().next().next().next();
                    let status = next.first().unwrap().text().trim().to_string();

                    if !status.starts_with("Not Affected") {
                        match infos.entry(pkgname) {
                            Occupied(c) => { c.into_mut() },
                            Vacant(c) => { c.insert(vec![]) },
                        }.push(info);
                    }
                },
                None => {},
            };
        }
    }

    let pacman = alpm::Alpm::new().unwrap();
    for (pkg, cves) in infos {
        match pacman.query_package_version(pkg.clone()) {
            Ok(v) => {
                info!("Found installed version {} for package {}", v, pkg);
                for cve in cves {
                    match cve.version {
                        Some(version) => {
                            info!("Comparing with fixed version {}", version);
                            match pacman.vercmp(v.clone(), version.clone()).unwrap() {
                                Ordering::Less => { print_asa(&options, &pkg, cve.cve, Some(version) ) },
                                _ => {},
                            };
                        },
                        None => { print_asa(&options, &pkg, cve.cve, None) },
                    };
                };
            },
            Err(_) => { debug!("Package {} not installed", pkg) },
        }
    }
}

fn print_asa(options: &Options, pkgname: &String, cve: Vec<String>, version: Option<String>) {
    let msg = format!("Package {} is affected by {:?}", pkgname, cve);

    match version {
        Some(v) => {
            if options.quiet == 1 {
                println!("{}>={}", pkgname, v);
            } else if options.quiet >= 2 {
                println!("{}", pkgname);
            } else {
                match options.format {
                    Some(ref f) => { println!("{}", f.replace("%n", pkgname).replace("%c", cve.iter().join(",").as_str())) },
                    None => { println!("{}. Update to {}!", msg, v) },
                }
            }
        }
        None => {
            if !options.upgradable_only {
                if options.quiet > 0 {
                    println!("{}", pkgname);
                } else {
                    match options.format {
                        Some(ref f) => { println!("{}", f.replace("%n", pkgname).replace("%c", cve.iter().join(",").as_str())) },
                        None => { println!("{}. VULNERABLE!", msg) },
                    }
                }
            }
        }
    }
}
<commit_msg>fixes issue #2<commit_after>extern crate alpm;
extern crate clap;
extern crate curl;
extern crate env_logger;
extern crate itertools;
extern crate json;
#[macro_use]
extern crate log;
extern crate select;

use clap::{Arg, App};
use curl::easy::Easy;
use itertools::Itertools;
use select::document::Document;
use select::predicate::Name;
use std::cmp::Ordering;
use std::collections::HashMap;
use std::collections::hash_map::Entry::{Occupied, Vacant};
use std::default::Default;
use std::str;

#[derive(Debug,Clone)]
struct ASA {
    cve: Vec<String>,
    version: Option<String>,
}

impl Default for ASA {
    fn default() -> ASA {
        ASA {
            cve: Vec::new(),
            version: None,
        }
    }
}

#[derive(Debug)]
struct Options {
    format: Option<String>,
    quiet: u64,
    upgradable_only: bool,
}

impl Default for Options {
    fn default() -> Options {
        Options {
            format: None,
            quiet: 0,
            upgradable_only: false
        }
    }
}

fn main() {
    env_logger::init().unwrap();

    let args = App::new("arch-audit")
                        .version("0.1.2")
                        .arg(Arg::with_name("format")
                             .short("f")
                             .long("format")
                             .takes_value(true)
                             .help("Specify a format to control the output. Placeholders are %n (pkgname) and %c (CVEs)"))
                        .arg(Arg::with_name("quiet")
                             .short("q")
                             .long("quiet")
                             .multiple(true)
                             .help("Show only vulnerable package names and their versions"))
                        .arg(Arg::with_name("upgradable")
                             .short("u")
                             .long("upgradable")
                             .help("Show only packages that have already been fixed"))
                        .get_matches();

    let options = Options {
        format: { match args.value_of("format") {
            Some(f) => { Some(f.to_string()) },
            None => { None },
            } },
        quiet: args.occurrences_of("quiet"),
        upgradable_only: args.is_present("upgradable"),
    };

    let mut wikipage = String::new();
    {
        info!("Downloading CVE wiki page...");
        let wikipage_url = "https://wiki.archlinux.org/api.php?format=json&action=parse&page=CVE&section=5";

        let mut easy = Easy::new();
        easy.url(wikipage_url).unwrap();
        let mut transfer = easy.transfer();
        transfer.write_function(|data| {
            wikipage.push_str(str::from_utf8(data).unwrap());
            Ok(data.len())
        }).unwrap();
        transfer.perform().unwrap();
    }

    let mut infos: HashMap<String, Vec<_>> = HashMap::new();
    {
        let json = json::parse(wikipage.as_str()).unwrap();
        let document = Document::from(json["parse"]["text"]["*"].as_str().unwrap());

        for tr in document.find(Name("tbody")).find(Name("tr")).iter() {
            let tds = tr.find(Name("td"));

            match tds.first() {
                Some(td) => {
                    let mut next = tds.next().next();
                    let pkgname = next.first().unwrap().text().trim().to_string();
                    next = next.next().next().next().next().next().next();
                    let info = ASA {
                        cve: td.text().split_whitespace().filter(|s| s.starts_with("CVE")).map(|s| s.to_string()).collect(),
                        version: {
                            let v = next.first().unwrap().text().trim().to_string();
                            if !v.is_empty() && v != "?".to_string() && v != "-".to_string() { Some(v) } else { None }
                        },
                    };
                    next = next.next().next().next().next();
                    let status = next.first().unwrap().text().trim().to_string();

                    if !status.starts_with("Not Affected") {
                        match infos.entry(pkgname) {
                            Occupied(c) => { c.into_mut() },
                            Vacant(c) => { c.insert(vec![]) },
                        }.push(info);
                    }
                },
                None => {},
            };
        }
    }

    let pacman = alpm::Alpm::new().unwrap();
    for (pkg, cves) in infos {
        let pkg_cves = get_asas_for_package_by_version(&pacman, &pkg, &cves);

        let (relevant_cves, version) = cves_with_relevant_version(pkg_cves, &pacman);

        if !relevant_cves.is_empty() {
            print_asa(&options, &pkg, relevant_cves, version);
        }
    }
}

/// gets a map from version to ASAs for a specific package, returns list of CVEs for that
/// package and a version, which is the most updated
fn cves_with_relevant_version(pkg_cves: HashMap<Option<String>, Vec<ASA>>, pacman: &alpm::Alpm) -> (Vec<String>, Option<String>) {
    let mut cves: Vec<String> = Vec::new();
    // the newest version, which fixes all/most ? cves
    let mut version: Option<String> = None;
    for (v, asas) in pkg_cves {
        for asa in asas {
            for cve in asa.cve {
                cves.push(cve);
            }
        }
        match v {
            Some(ref v) => {
                version = match version {
                    None => Some(v.clone()),
                    Some(ref vv) => {
                        match pacman.vercmp(v.clone(), vv.clone()).unwrap() {
                            Ordering::Greater => Some(v.clone()),
                            _ => version.clone()
                        }
                    }
                }
            }
            _ => {}
        }
    }
    (cves, version)
}

#[test]
fn test_cves_with_relevant_version() {
    let mut map = HashMap::new();
    map.insert(Some("1.0".to_string()), vec![ASA{cve: vec!["a".to_string(), "b".to_string()], version: Some("1.0".to_string())}]);
    map.insert(Some("2.0".to_string()), vec![ASA{cve: vec!["c".to_string()], version: Some("2.0".to_string())}]);
    map.insert(Some("3.0".to_string()), vec![ASA{cve: Vec::new(), version: Some("3.0".to_string())}]);

    let pacman = alpm::Alpm::new().unwrap();
    let (mut cves, version) = cves_with_relevant_version(map, &pacman);

    cves.sort();
    assert_eq!(cves, vec!["a".to_string(), "b".to_string(), "c".to_string()]);
    assert_eq!(version, Some("3.0".to_string()));
}

/// creates a map between version and ASAs from a list of ASAs
fn get_asas_for_package_by_version(pacman: &alpm::Alpm, pkg: &String, cves: &Vec<ASA>) -> HashMap<Option<String>, Vec<ASA>> {
    let mut pkg_cves: HashMap<Option<String>, Vec<ASA>> = HashMap::new();
    match pacman.query_package_version(pkg.clone()) {
        Ok(v) => {
            info!("Found installed version {} for package {}", v, pkg);
            for cve in cves {
                match cve.version {
                    Some(ref version) => {
                        info!("Comparing with fixed version {}", version);
                        match pacman.vercmp(v.clone(), version.clone()).unwrap() {
                            Ordering::Less => {
                                let mut vec = match pkg_cves.get_mut(&Some(v.clone())) {
                                    Some(v) => v.clone(),
                                    None => Vec::new(),
                                };
                                vec.push(cve.clone());
                                pkg_cves.insert(Some(version.clone()), vec);
                            },
                            _ => {},
                        };
                    },
                    None => {
                        let mut vec = match pkg_cves.get_mut(&None) {
                            Some(v) => v.clone(),
                            None => Vec::new(),
                        };
                        vec.push(cve.clone());
                        pkg_cves.insert(None, vec);
                    },
                };
            };
        },
        Err(_) => { debug!("Package {} not installed", pkg) },
    }
    pkg_cves
}

fn print_asa(options: &Options, pkgname: &String, cve: Vec<String>, version: Option<String>) {
    let msg = format!("Package {} is affected by {:?}", pkgname, cve);

    match version {
        Some(v) => {
            if options.quiet == 1 {
                println!("{}>={}", pkgname, v);
            } else if options.quiet >= 2 {
                println!("{}", pkgname);
            } else {
                match options.format {
                    Some(ref f) => { println!("{}", f.replace("%n", pkgname).replace("%c", cve.iter().join(",").as_str())) },
                    None => { println!("{}. Update to {}!", msg, v) },
                }
            }
        }
        None => {
            if !options.upgradable_only {
                if options.quiet > 0 {
                    println!("{}", pkgname);
                } else {
                    match options.format {
                        Some(ref f) => { println!("{}", f.replace("%n", pkgname).replace("%c", cve.iter().join(",").as_str())) },
                        None => { println!("{}. VULNERABLE!", msg) },
                    }
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 Robert Collins. Licensed under the Apache-2.0 license.
extern crate libc;

extern crate netmap;
extern crate pnet;

extern crate rusty_rail;

use std::env;
use std::io;
use std::net::{IpAddr, Ipv4Addr};

// use netmap::Direction;
use pnet::util::{get_network_interfaces, NetworkInterface};

use rusty_rail::configuration::Config;
use rusty_rail::error::BrokenRail;
use rusty_rail::{move_packets, TransferStatus};

pub fn poll(pollfds: &mut Vec<libc::pollfd>,
            wire_read: bool,
            host_read: bool)
            -> Result<u8, BrokenRail> {
    // XXX: this needs to be more sophisticated: where we have pending packets for a descriptor, we
    // want POLLOUT; otherwise POLLIN. We don't want POLLIN on a device where the next packet is
    // for an output blocked device.
    //
    // TODO: fix layering violations.
    // 0, 2 we read from.
    // 1 we only write to.
    // Need to have a marker for switching to block-until-we-can-write.
    if wire_read {
        pollfds[0].events = libc::POLLIN;
        pollfds[1].events = 0
    } else {
        pollfds[0].events = 0;
        pollfds[1].events = libc::POLLOUT
    }
    if host_read {
        pollfds[2].events = libc::POLLIN
    } else {
        pollfds[2].events = libc::POLLOUT
    }

    let poll_len = pollfds.len();
    if let Some(first) = pollfds.first_mut() {
        let rv = unsafe { libc::poll(first as *mut libc::pollfd, poll_len as u64, 1000) };
        if rv < 0 {
            Err(BrokenRail::IO(io::Error::last_os_error()))
        } else {
            Ok(rv as u8)
        }
    } else {
        // Nothing to poll, no error.
        return Ok(0);
    }
}

fn pollfd(fd: i32) -> libc::pollfd {
    libc::pollfd {
        fd: fd,
        events: 0,
        revents: 0,
    }
}

fn device_name(device: &String, suffix: &str) -> String {
    device.clone() + suffix
}

fn extract_ipv4(interface: &NetworkInterface) -> Result<Ipv4Addr, BrokenRail> {

    if let Some(ref ips) = interface.ips {
        for ip in ips {
            if let &IpAddr::V4(ipv4) = ip {
                println!("{}", ip);
                return Ok(ipv4);
            }
        }
    }
    return Err(BrokenRail::NoIPV4Address);
}

fn stuff() -> Result<(), BrokenRail> {
    let mut pollfds: Vec<libc::pollfd> = Vec::with_capacity(2);
    let config = try!(Config::new(env::vars()));

    let interfaces = get_network_interfaces();
    let interface_names_match = {
        |iface: &NetworkInterface| iface.name == config.device
    };
    let interface = interfaces.into_iter()
        .filter(interface_names_match)
        .next()
        .unwrap();
    let interface_ipv4 = try!(extract_ipv4(&interface));
    println!("interface {}", interface.mac_address());
    let interface_mac = interface.mac_address();
    // netmap-rs iterators lock the whole NetmapDescriptor, so we open two descriptors for the
    // adapter: one RX only, and on TX only. We open a single bidirectional descriptor for the host
    // side as we have no use case today for looping packets back to the host side.

    let mut nm_in = try!(netmap::NetmapDescriptor::new(&device_name(&config.device, "/R")));
    pollfds.push(pollfd(nm_in.get_fd()));
    println!("wire RX fd {}", pollfds[0].fd);

    let mut nm_out = try!(netmap::NetmapDescriptor::new(&device_name(&config.device, "/T")));
    pollfds.push(pollfd(nm_out.get_fd()));
    println!("wire RX fd {}", pollfds[1].fd);

    let mut nm_host = try!(netmap::NetmapDescriptor::new(&device_name(&config.device, "^")));
    pollfds.push(pollfd(nm_host.get_fd()));
    println!("host fd {}", pollfds[2].fd);

    let mut host_read = true;
    let mut wire_read = true;

    loop {
        if 0 == try!(poll(&mut pollfds, wire_read, host_read)) {
            //       println!("Poll timeout");
            continue;
        }
        host_read = true;
        wire_read = true;
        // A netmap poll error can mean the rings get reset: loop again.
        for pollfd in pollfds.iter() {
            if pollfd.revents & libc::POLLERR == libc::POLLERR {
                continue;
            }
        }
        // println!("Host -> Wire");
        match try!(move_packets(&mut nm_host,
                                &mut nm_out,
                                None,
                                &interface_ipv4,
                                &interface_mac,
                                &config.target_ip,
                                &config.target_mac)) {
            TransferStatus::BlockedDestination |
            TransferStatus::BlockedWire => {
                host_read = false;
                wire_read = false
            }
            TransferStatus::Complete => (),
        }
        // println!("Wire -> Host queue");
        match try!(move_packets(&mut nm_in,
                                &mut nm_host,
                                Some(&mut nm_out),
                                &interface_ipv4,
                                &interface_mac,
                                &config.target_ip,
                                &config.target_mac)) {
            TransferStatus::BlockedDestination => wire_read = false,
            TransferStatus::BlockedWire => host_read = false,
            TransferStatus::Complete => (),
        }
    }
}

fn main() {
    match stuff() {
        Ok(()) => println!("Actual cannibal unreachable code"),
        Err(err) => panic!("{}", err),
    };

}
<commit_msg>Work with pnet 0.12.<commit_after>// Copyright (c) 2016 Robert Collins. Licensed under the Apache-2.0 license.
extern crate libc;

extern crate netmap;
extern crate pnet;

extern crate rusty_rail;

use std::env;
use std::io;
use std::net::{IpAddr, Ipv4Addr};

// use netmap::Direction;
use pnet::datalink::NetworkInterface;
use pnet::util::get_network_interfaces;

use rusty_rail::configuration::Config;
use rusty_rail::error::BrokenRail;
use rusty_rail::{move_packets, TransferStatus};

pub fn poll(pollfds: &mut Vec<libc::pollfd>,
            wire_read: bool,
            host_read: bool)
            -> Result<u8, BrokenRail> {
    // XXX: this needs to be more sophisticated: where we have pending packets for a descriptor, we
    // want POLLOUT; otherwise POLLIN. We don't want POLLIN on a device where the next packet is
    // for an output blocked device.
    //
    // TODO: fix layering violations.
    // 0, 2 we read from.
    // 1 we only write to.
    // Need to have a marker for switching to block-until-we-can-write.
    if wire_read {
        pollfds[0].events = libc::POLLIN;
        pollfds[1].events = 0
    } else {
        pollfds[0].events = 0;
        pollfds[1].events = libc::POLLOUT
    }
    if host_read {
        pollfds[2].events = libc::POLLIN
    } else {
        pollfds[2].events = libc::POLLOUT
    }

    let poll_len = pollfds.len();
    if let Some(first) = pollfds.first_mut() {
        let rv = unsafe { libc::poll(first as *mut libc::pollfd, poll_len as u64, 1000) };
        if rv < 0 {
            Err(BrokenRail::IO(io::Error::last_os_error()))
        } else {
            Ok(rv as u8)
        }
    } else {
        // Nothing to poll, no error.
        return Ok(0);
    }
}

fn pollfd(fd: i32) -> libc::pollfd {
    libc::pollfd {
        fd: fd,
        events: 0,
        revents: 0,
    }
}

fn device_name(device: &String, suffix: &str) -> String {
    device.clone() + suffix
}

fn extract_ipv4(interface: &NetworkInterface) -> Result<Ipv4Addr, BrokenRail> {

    if let Some(ref ips) = interface.ips {
        for ip in ips {
            if let &IpAddr::V4(ipv4) = ip {
                println!("{}", ip);
                return Ok(ipv4);
            }
        }
    }
    return Err(BrokenRail::NoIPV4Address);
}

fn stuff() -> Result<(), BrokenRail> {
    let mut pollfds: Vec<libc::pollfd> = Vec::with_capacity(2);
    let config = try!(Config::new(env::vars()));

    let interfaces = get_network_interfaces();
    let interface_names_match = {
        |iface: &NetworkInterface| iface.name == config.device
    };
    let interface = interfaces.into_iter()
        .filter(interface_names_match)
        .next()
        .unwrap();
    let interface_ipv4 = try!(extract_ipv4(&interface));
    println!("interface {}", interface.mac_address());
    let interface_mac = interface.mac_address();
    // netmap-rs iterators lock the whole NetmapDescriptor, so we open two descriptors for the
    // adapter: one RX only, and on TX only. We open a single bidirectional descriptor for the host
    // side as we have no use case today for looping packets back to the host side.

    let mut nm_in = try!(netmap::NetmapDescriptor::new(&device_name(&config.device, "/R")));
    pollfds.push(pollfd(nm_in.get_fd()));
    println!("wire RX fd {}", pollfds[0].fd);

    let mut nm_out = try!(netmap::NetmapDescriptor::new(&device_name(&config.device, "/T")));
    pollfds.push(pollfd(nm_out.get_fd()));
    println!("wire RX fd {}", pollfds[1].fd);

    let mut nm_host = try!(netmap::NetmapDescriptor::new(&device_name(&config.device, "^")));
    pollfds.push(pollfd(nm_host.get_fd()));
    println!("host fd {}", pollfds[2].fd);

    let mut host_read = true;
    let mut wire_read = true;

    loop {
        if 0 == try!(poll(&mut pollfds, wire_read, host_read)) {
            //       println!("Poll timeout");
            continue;
        }
        host_read = true;
        wire_read = true;
        // A netmap poll error can mean the rings get reset: loop again.
        for pollfd in pollfds.iter() {
            if pollfd.revents & libc::POLLERR == libc::POLLERR {
                continue;
            }
        }
        // println!("Host -> Wire");
        match try!(move_packets(&mut nm_host,
                                &mut nm_out,
                                None,
                                &interface_ipv4,
                                &interface_mac,
                                &config.target_ip,
                                &config.target_mac)) {
            TransferStatus::BlockedDestination |
            TransferStatus::BlockedWire => {
                host_read = false;
                wire_read = false
            }
            TransferStatus::Complete => (),
        }
        // println!("Wire -> Host queue");
        match try!(move_packets(&mut nm_in,
                                &mut nm_host,
                                Some(&mut nm_out),
                                &interface_ipv4,
                                &interface_mac,
                                &config.target_ip,
                                &config.target_mac)) {
            TransferStatus::BlockedDestination => wire_read = false,
            TransferStatus::BlockedWire => host_read = false,
            TransferStatus::Complete => (),
        }
    }
}

fn main() {
    match stuff() {
        Ok(()) => println!("Actual cannibal unreachable code"),
        Err(err) => panic!("{}", err),
    };

}
<|endoftext|>


<commit_before>#![forbid(unsafe_code)]

pub mod api;
pub mod db;
pub mod importer;
pub mod indexer;
pub mod model;
pub mod opening;
pub mod util;

use std::{
    cmp::{max, min},
    mem,
    net::SocketAddr,
    path::PathBuf,
    sync::Arc,
    time::Duration,
};

use axum::{
    extract::{Extension, Path, Query},
    http::StatusCode,
    routing::{get, put},
    AddExtensionLayer, Json, Router,
};
use clap::Parser;
use futures_util::stream::Stream;
use serde::Deserialize;
use serde_with::{serde_as, DisplayFromStr};
use shakmaty::{
    san::{San, SanPlus},
    uci::Uci,
    variant::VariantPosition,
};
use tokio::sync::watch;

use crate::{
    api::{
        Error, ExplorerGame, ExplorerGameWithUci, ExplorerMove, ExplorerResponse, LichessQuery,
        Limits, MastersQuery, NdJson, PlayerQuery, PlayerQueryFilter,
    },
    db::{Database, LichessDatabase},
    importer::{LichessGameImport, LichessImporter, MastersImporter},
    indexer::{IndexerOpt, IndexerStub},
    model::{
        GameId, KeyBuilder, KeyPrefix, MastersGame, MastersGameWithId, PreparedMove, UserId, Year,
    },
    opening::{Opening, Openings},
    util::DedupStreamExt as _,
};

#[derive(Parser)]
struct Opt {
    /// Binding address. Note that administrative endpoints must be protected
    /// using a reverse proxy.
    #[clap(long, default_value = "127.0.0.1:9002")]
    bind: SocketAddr,
    /// Path to RocksDB database
    #[clap(long, default_value = "_db")]
    db: PathBuf,
    /// Allow access from all origins.
    #[clap(long)]
    cors: bool,
    #[clap(flatten)]
    indexer: IndexerOpt,
}

#[tokio::main]
async fn main() {
    env_logger::Builder::from_env(
        env_logger::Env::new()
            .filter("EXPLORER_LOG")
            .write_style("EXPLORER_LOG_STYLE"),
    )
    .format_timestamp(None)
    .format_module_path(false)
    .format_target(false)
    .init();

    let opt = Opt::parse();

    let openings: &'static Openings = Box::leak(Box::new(Openings::build_table()));
    let db = Arc::new(Database::open(opt.db).expect("db"));
    let (indexer, join_handles) = IndexerStub::spawn(Arc::clone(&db), opt.indexer);
    let masters_importer = MastersImporter::new(Arc::clone(&db));
    let lichess_importer = LichessImporter::new(Arc::clone(&db));

    let app = Router::new()
        .route("/monitor/cf/:cf/:prop", get(cf_prop))
        .route("/monitor/db/:prop", get(db_prop))
        .route("/monitor/indexing", get(num_indexing))
        .route("/import/masters", put(masters_import))
        .route("/import/lichess", put(lichess_import))
        .route("/masters/pgn/:id", get(masters_pgn))
        .route("/masters", get(masters))
        .route("/lichess", get(lichess))
        .route("/player", get(player))
        .route("/master/pgn/:id", get(masters_pgn)) // bc
        .route("/master", get(masters)) // bc
        .route("/personal", get(player)) // bc
        .layer(AddExtensionLayer::new(openings))
        .layer(AddExtensionLayer::new(db))
        .layer(AddExtensionLayer::new(masters_importer))
        .layer(AddExtensionLayer::new(lichess_importer))
        .layer(AddExtensionLayer::new(indexer));

    let app = if opt.cors {
        app.layer(tower_http::set_header::SetResponseHeaderLayer::<
            _,
            axum::body::Body,
        >::if_not_present(
            axum::http::header::ACCESS_CONTROL_ALLOW_ORIGIN,
            axum::http::HeaderValue::from_static("*"),
        ))
    } else {
        app
    };

    axum::Server::bind(&opt.bind)
        .serve(app.into_make_service())
        .await
        .expect("bind");

    for join_handle in join_handles {
        join_handle.await.expect("indexer");
    }
}

#[derive(Deserialize)]
struct ColumnFamilyProp {
    cf: String,
    prop: String,
}

async fn cf_prop(
    Path(path): Path<ColumnFamilyProp>,
    Extension(db): Extension<Arc<Database>>,
) -> Result<String, StatusCode> {
    db.inner
        .cf_handle(&path.cf)
        .and_then(|cf| {
            db.inner
                .property_value_cf(cf, &path.prop)
                .expect("property value")
        })
        .ok_or(StatusCode::NOT_FOUND)
}

async fn db_prop(
    Path(prop): Path<String>,
    Extension(db): Extension<Arc<Database>>,
) -> Result<String, StatusCode> {
    db.inner
        .property_value(&prop)
        .expect("property value")
        .ok_or(StatusCode::NOT_FOUND)
}

async fn num_indexing(Extension(indexer): Extension<IndexerStub>) -> String {
    indexer.num_indexing().await.to_string()
}

fn finalize_lichess_moves(
    moves: Vec<PreparedMove>,
    pos: &VariantPosition,
    lichess_db: &LichessDatabase,
) -> Vec<ExplorerMove> {
    moves
        .into_iter()
        .map(|p| ExplorerMove {
            stats: p.stats,
            san: p.uci.to_move(pos).map_or(
                SanPlus {
                    san: San::Null,
                    suffix: None,
                },
                |m| SanPlus::from_move(pos.clone(), &m),
            ),
            uci: p.uci,
            average_rating: p.average_rating,
            average_opponent_rating: p.average_opponent_rating,
            game: p.game.and_then(|id| {
                lichess_db
                    .game(id)
                    .expect("get game")
                    .map(|info| ExplorerGame::from_lichess(id, info))
            }),
        })
        .collect()
}

fn finalize_lichess_games(
    games: Vec<(Uci, GameId)>,
    lichess_db: &LichessDatabase,
) -> Vec<ExplorerGameWithUci> {
    games
        .into_iter()
        .flat_map(|(uci, id)| {
            lichess_db
                .game(id)
                .expect("get game")
                .map(|info| ExplorerGameWithUci {
                    uci,
                    row: ExplorerGame::from_lichess(id, info),
                })
        })
        .collect()
}

struct PlayerStreamState {
    indexing: Option<watch::Receiver<()>>,
    key: KeyPrefix,
    db: Arc<Database>,
    filter: PlayerQueryFilter,
    limits: Limits,
    pos: VariantPosition,
    opening: Option<&'static Opening>,
    first: bool,
    done: bool,
}

async fn player(
    Extension(openings): Extension<&'static Openings>,
    Extension(db): Extension<Arc<Database>>,
    Extension(indexer): Extension<IndexerStub>,
    Query(query): Query<PlayerQuery>,
) -> Result<NdJson<impl Stream<Item = ExplorerResponse>>, Error> {
    let player = UserId::from(query.player);
    let indexing = indexer.index_player(&player).await;
    let (variant, pos, opening) = query.play.position(openings)?;
    let key = KeyBuilder::player(&player, query.color).with_zobrist(variant, pos.zobrist_hash());

    let state = PlayerStreamState {
        filter: query.filter,
        limits: query.limits,
        db,
        indexing,
        opening,
        key,
        pos: pos.into_inner(),
        first: true,
        done: false,
    };

    Ok(NdJson(futures_util::stream::unfold(
        state,
        |mut state| async move {
            if state.done {
                return None;
            }

            let first = mem::replace(&mut state.first, false);
            state.done = match state.indexing {
                Some(ref mut indexing) => {
                    tokio::select! {
                        _ = indexing.changed() => true,
                        _ = tokio::time::sleep(Duration::from_millis(if first { 0 } else { 1000 })) => false,
                    }
                }
                None => true,
            };

            let lichess_db = state.db.lichess();
            let mut filtered = lichess_db
                .read_player(&state.key, state.filter.since, state.filter.until)
                .expect("read player")
                .prepare(&state.filter);

            filtered.moves.truncate(state.limits.moves.unwrap_or(usize::MAX));
            filtered.recent_games.truncate(state.limits.recent_games);

            Some((
                ExplorerResponse {
                    total: filtered.total,
                    moves: finalize_lichess_moves(filtered.moves, &state.pos, &lichess_db),
                    recent_games: Some(finalize_lichess_games(filtered.recent_games, &lichess_db)),
                    top_games: None,
                    opening: state.opening,
                },
                state,
            ))
        },
    ).dedup_by_key(|res| res.total.total())))
}

async fn masters_import(
    Json(body): Json<MastersGameWithId>,
    Extension(importer): Extension<MastersImporter>,
) -> Result<(), Error> {
    importer.import(body).await
}

#[serde_as]
#[derive(Deserialize)]
struct MastersGameId(#[serde_as(as = "DisplayFromStr")] GameId);

async fn masters_pgn(
    Path(MastersGameId(id)): Path<MastersGameId>,
    Extension(db): Extension<Arc<Database>>,
) -> Result<MastersGame, StatusCode> {
    match db.masters().game(id).expect("get masters game") {
        Some(game) => Ok(game),
        None => Err(StatusCode::NOT_FOUND),
    }
}

async fn masters(
    Extension(openings): Extension<&'static Openings>,
    Extension(db): Extension<Arc<Database>>,
    Query(query): Query<MastersQuery>,
) -> Result<Json<ExplorerResponse>, Error> {
    let (variant, pos, opening) = query.play.position(openings)?;
    let key = KeyBuilder::masters().with_zobrist(variant, pos.zobrist_hash());
    let masters_db = db.masters();
    let mut entry = masters_db
        .read(key, query.since, query.until)
        .expect("get masters")
        .prepare();

    entry.moves.truncate(query.limits.moves.unwrap_or(12));
    entry.top_games.truncate(query.limits.top_games);

    Ok(Json(ExplorerResponse {
        total: entry.total,
        moves: entry
            .moves
            .into_iter()
            .map(|p| ExplorerMove {
                san: p.uci.to_move(&pos).map_or(
                    SanPlus {
                        san: San::Null,
                        suffix: None,
                    },
                    |m| SanPlus::from_move(pos.clone(), &m),
                ),
                uci: p.uci,
                average_rating: p.average_rating,
                average_opponent_rating: p.average_opponent_rating,
                stats: p.stats,
                game: p.game.and_then(|id| {
                    masters_db
                        .game(id)
                        .expect("get masters game")
                        .map(|info| ExplorerGame::from_masters(id, info))
                }),
            })
            .collect(),
        top_games: Some(
            entry
                .top_games
                .into_iter()
                .flat_map(|(uci, id)| {
                    masters_db
                        .game(id)
                        .expect("get masters game")
                        .map(|info| ExplorerGameWithUci {
                            uci,
                            row: ExplorerGame::from_masters(id, info),
                        })
                })
                .collect(),
        ),
        opening,
        recent_games: None,
    }))
}

async fn lichess_import(
    Json(body): Json<Vec<LichessGameImport>>,
    Extension(importer): Extension<LichessImporter>,
) -> Result<(), Error> {
    for game in body {
        importer.import(game).await?;
    }
    Ok(())
}

async fn lichess(
    Extension(openings): Extension<&'static Openings>,
    Extension(db): Extension<Arc<Database>>,
    Query(query): Query<LichessQuery>,
) -> Result<Json<ExplorerResponse>, Error> {
    let (variant, pos, opening) = query.play.position(openings)?;
    let key = KeyBuilder::lichess().with_zobrist(variant, pos.zobrist_hash());
    let lichess_db = db.lichess();
    let mut filtered = lichess_db
        .read_lichess(&key, query.filter.since, query.filter.until)
        .expect("get lichess")
        .prepare(&query.filter);

    filtered.moves.truncate(query.limits.moves.unwrap_or(12));
    filtered.recent_games.truncate(query.limits.recent_games);
    filtered.top_games.truncate(query.limits.top_games);

    Ok(Json(ExplorerResponse {
        total: filtered.total,
        moves: finalize_lichess_moves(filtered.moves, pos.as_inner(), &lichess_db),
        recent_games: Some(finalize_lichess_games(filtered.recent_games, &lichess_db)),
        top_games: Some(finalize_lichess_games(filtered.top_games, &lichess_db)),
        opening,
    }))
}
<commit_msg>remove unused imports<commit_after>#![forbid(unsafe_code)]

pub mod api;
pub mod db;
pub mod importer;
pub mod indexer;
pub mod model;
pub mod opening;
pub mod util;

use std::{mem, net::SocketAddr, path::PathBuf, sync::Arc, time::Duration};

use axum::{
    extract::{Extension, Path, Query},
    http::StatusCode,
    routing::{get, put},
    AddExtensionLayer, Json, Router,
};
use clap::Parser;
use futures_util::stream::Stream;
use serde::Deserialize;
use serde_with::{serde_as, DisplayFromStr};
use shakmaty::{
    san::{San, SanPlus},
    uci::Uci,
    variant::VariantPosition,
};
use tokio::sync::watch;

use crate::{
    api::{
        Error, ExplorerGame, ExplorerGameWithUci, ExplorerMove, ExplorerResponse, LichessQuery,
        Limits, MastersQuery, NdJson, PlayerQuery, PlayerQueryFilter,
    },
    db::{Database, LichessDatabase},
    importer::{LichessGameImport, LichessImporter, MastersImporter},
    indexer::{IndexerOpt, IndexerStub},
    model::{GameId, KeyBuilder, KeyPrefix, MastersGame, MastersGameWithId, PreparedMove, UserId},
    opening::{Opening, Openings},
    util::DedupStreamExt as _,
};

#[derive(Parser)]
struct Opt {
    /// Binding address. Note that administrative endpoints must be protected
    /// using a reverse proxy.
    #[clap(long, default_value = "127.0.0.1:9002")]
    bind: SocketAddr,
    /// Path to RocksDB database
    #[clap(long, default_value = "_db")]
    db: PathBuf,
    /// Allow access from all origins.
    #[clap(long)]
    cors: bool,
    #[clap(flatten)]
    indexer: IndexerOpt,
}

#[tokio::main]
async fn main() {
    env_logger::Builder::from_env(
        env_logger::Env::new()
            .filter("EXPLORER_LOG")
            .write_style("EXPLORER_LOG_STYLE"),
    )
    .format_timestamp(None)
    .format_module_path(false)
    .format_target(false)
    .init();

    let opt = Opt::parse();

    let openings: &'static Openings = Box::leak(Box::new(Openings::build_table()));
    let db = Arc::new(Database::open(opt.db).expect("db"));
    let (indexer, join_handles) = IndexerStub::spawn(Arc::clone(&db), opt.indexer);
    let masters_importer = MastersImporter::new(Arc::clone(&db));
    let lichess_importer = LichessImporter::new(Arc::clone(&db));

    let app = Router::new()
        .route("/monitor/cf/:cf/:prop", get(cf_prop))
        .route("/monitor/db/:prop", get(db_prop))
        .route("/monitor/indexing", get(num_indexing))
        .route("/import/masters", put(masters_import))
        .route("/import/lichess", put(lichess_import))
        .route("/masters/pgn/:id", get(masters_pgn))
        .route("/masters", get(masters))
        .route("/lichess", get(lichess))
        .route("/player", get(player))
        .route("/master/pgn/:id", get(masters_pgn)) // bc
        .route("/master", get(masters)) // bc
        .route("/personal", get(player)) // bc
        .layer(AddExtensionLayer::new(openings))
        .layer(AddExtensionLayer::new(db))
        .layer(AddExtensionLayer::new(masters_importer))
        .layer(AddExtensionLayer::new(lichess_importer))
        .layer(AddExtensionLayer::new(indexer));

    let app = if opt.cors {
        app.layer(tower_http::set_header::SetResponseHeaderLayer::<
            _,
            axum::body::Body,
        >::if_not_present(
            axum::http::header::ACCESS_CONTROL_ALLOW_ORIGIN,
            axum::http::HeaderValue::from_static("*"),
        ))
    } else {
        app
    };

    axum::Server::bind(&opt.bind)
        .serve(app.into_make_service())
        .await
        .expect("bind");

    for join_handle in join_handles {
        join_handle.await.expect("indexer");
    }
}

#[derive(Deserialize)]
struct ColumnFamilyProp {
    cf: String,
    prop: String,
}

async fn cf_prop(
    Path(path): Path<ColumnFamilyProp>,
    Extension(db): Extension<Arc<Database>>,
) -> Result<String, StatusCode> {
    db.inner
        .cf_handle(&path.cf)
        .and_then(|cf| {
            db.inner
                .property_value_cf(cf, &path.prop)
                .expect("property value")
        })
        .ok_or(StatusCode::NOT_FOUND)
}

async fn db_prop(
    Path(prop): Path<String>,
    Extension(db): Extension<Arc<Database>>,
) -> Result<String, StatusCode> {
    db.inner
        .property_value(&prop)
        .expect("property value")
        .ok_or(StatusCode::NOT_FOUND)
}

async fn num_indexing(Extension(indexer): Extension<IndexerStub>) -> String {
    indexer.num_indexing().await.to_string()
}

fn finalize_lichess_moves(
    moves: Vec<PreparedMove>,
    pos: &VariantPosition,
    lichess_db: &LichessDatabase,
) -> Vec<ExplorerMove> {
    moves
        .into_iter()
        .map(|p| ExplorerMove {
            stats: p.stats,
            san: p.uci.to_move(pos).map_or(
                SanPlus {
                    san: San::Null,
                    suffix: None,
                },
                |m| SanPlus::from_move(pos.clone(), &m),
            ),
            uci: p.uci,
            average_rating: p.average_rating,
            average_opponent_rating: p.average_opponent_rating,
            game: p.game.and_then(|id| {
                lichess_db
                    .game(id)
                    .expect("get game")
                    .map(|info| ExplorerGame::from_lichess(id, info))
            }),
        })
        .collect()
}

fn finalize_lichess_games(
    games: Vec<(Uci, GameId)>,
    lichess_db: &LichessDatabase,
) -> Vec<ExplorerGameWithUci> {
    games
        .into_iter()
        .flat_map(|(uci, id)| {
            lichess_db
                .game(id)
                .expect("get game")
                .map(|info| ExplorerGameWithUci {
                    uci,
                    row: ExplorerGame::from_lichess(id, info),
                })
        })
        .collect()
}

struct PlayerStreamState {
    indexing: Option<watch::Receiver<()>>,
    key: KeyPrefix,
    db: Arc<Database>,
    filter: PlayerQueryFilter,
    limits: Limits,
    pos: VariantPosition,
    opening: Option<&'static Opening>,
    first: bool,
    done: bool,
}

async fn player(
    Extension(openings): Extension<&'static Openings>,
    Extension(db): Extension<Arc<Database>>,
    Extension(indexer): Extension<IndexerStub>,
    Query(query): Query<PlayerQuery>,
) -> Result<NdJson<impl Stream<Item = ExplorerResponse>>, Error> {
    let player = UserId::from(query.player);
    let indexing = indexer.index_player(&player).await;
    let (variant, pos, opening) = query.play.position(openings)?;
    let key = KeyBuilder::player(&player, query.color).with_zobrist(variant, pos.zobrist_hash());

    let state = PlayerStreamState {
        filter: query.filter,
        limits: query.limits,
        db,
        indexing,
        opening,
        key,
        pos: pos.into_inner(),
        first: true,
        done: false,
    };

    Ok(NdJson(futures_util::stream::unfold(
        state,
        |mut state| async move {
            if state.done {
                return None;
            }

            let first = mem::replace(&mut state.first, false);
            state.done = match state.indexing {
                Some(ref mut indexing) => {
                    tokio::select! {
                        _ = indexing.changed() => true,
                        _ = tokio::time::sleep(Duration::from_millis(if first { 0 } else { 1000 })) => false,
                    }
                }
                None => true,
            };

            let lichess_db = state.db.lichess();
            let mut filtered = lichess_db
                .read_player(&state.key, state.filter.since, state.filter.until)
                .expect("read player")
                .prepare(&state.filter);

            filtered.moves.truncate(state.limits.moves.unwrap_or(usize::MAX));
            filtered.recent_games.truncate(state.limits.recent_games);

            Some((
                ExplorerResponse {
                    total: filtered.total,
                    moves: finalize_lichess_moves(filtered.moves, &state.pos, &lichess_db),
                    recent_games: Some(finalize_lichess_games(filtered.recent_games, &lichess_db)),
                    top_games: None,
                    opening: state.opening,
                },
                state,
            ))
        },
    ).dedup_by_key(|res| res.total.total())))
}

async fn masters_import(
    Json(body): Json<MastersGameWithId>,
    Extension(importer): Extension<MastersImporter>,
) -> Result<(), Error> {
    importer.import(body).await
}

#[serde_as]
#[derive(Deserialize)]
struct MastersGameId(#[serde_as(as = "DisplayFromStr")] GameId);

async fn masters_pgn(
    Path(MastersGameId(id)): Path<MastersGameId>,
    Extension(db): Extension<Arc<Database>>,
) -> Result<MastersGame, StatusCode> {
    match db.masters().game(id).expect("get masters game") {
        Some(game) => Ok(game),
        None => Err(StatusCode::NOT_FOUND),
    }
}

async fn masters(
    Extension(openings): Extension<&'static Openings>,
    Extension(db): Extension<Arc<Database>>,
    Query(query): Query<MastersQuery>,
) -> Result<Json<ExplorerResponse>, Error> {
    let (variant, pos, opening) = query.play.position(openings)?;
    let key = KeyBuilder::masters().with_zobrist(variant, pos.zobrist_hash());
    let masters_db = db.masters();
    let mut entry = masters_db
        .read(key, query.since, query.until)
        .expect("get masters")
        .prepare();

    entry.moves.truncate(query.limits.moves.unwrap_or(12));
    entry.top_games.truncate(query.limits.top_games);

    Ok(Json(ExplorerResponse {
        total: entry.total,
        moves: entry
            .moves
            .into_iter()
            .map(|p| ExplorerMove {
                san: p.uci.to_move(&pos).map_or(
                    SanPlus {
                        san: San::Null,
                        suffix: None,
                    },
                    |m| SanPlus::from_move(pos.clone(), &m),
                ),
                uci: p.uci,
                average_rating: p.average_rating,
                average_opponent_rating: p.average_opponent_rating,
                stats: p.stats,
                game: p.game.and_then(|id| {
                    masters_db
                        .game(id)
                        .expect("get masters game")
                        .map(|info| ExplorerGame::from_masters(id, info))
                }),
            })
            .collect(),
        top_games: Some(
            entry
                .top_games
                .into_iter()
                .flat_map(|(uci, id)| {
                    masters_db
                        .game(id)
                        .expect("get masters game")
                        .map(|info| ExplorerGameWithUci {
                            uci,
                            row: ExplorerGame::from_masters(id, info),
                        })
                })
                .collect(),
        ),
        opening,
        recent_games: None,
    }))
}

async fn lichess_import(
    Json(body): Json<Vec<LichessGameImport>>,
    Extension(importer): Extension<LichessImporter>,
) -> Result<(), Error> {
    for game in body {
        importer.import(game).await?;
    }
    Ok(())
}

async fn lichess(
    Extension(openings): Extension<&'static Openings>,
    Extension(db): Extension<Arc<Database>>,
    Query(query): Query<LichessQuery>,
) -> Result<Json<ExplorerResponse>, Error> {
    let (variant, pos, opening) = query.play.position(openings)?;
    let key = KeyBuilder::lichess().with_zobrist(variant, pos.zobrist_hash());
    let lichess_db = db.lichess();
    let mut filtered = lichess_db
        .read_lichess(&key, query.filter.since, query.filter.until)
        .expect("get lichess")
        .prepare(&query.filter);

    filtered.moves.truncate(query.limits.moves.unwrap_or(12));
    filtered.recent_games.truncate(query.limits.recent_games);
    filtered.top_games.truncate(query.limits.top_games);

    Ok(Json(ExplorerResponse {
        total: filtered.total,
        moves: finalize_lichess_moves(filtered.moves, pos.as_inner(), &lichess_db),
        recent_games: Some(finalize_lichess_games(filtered.recent_games, &lichess_db)),
        top_games: Some(finalize_lichess_games(filtered.top_games, &lichess_db)),
        opening,
    }))
}
<|endoftext|>


<commit_before>
mod stdout_writer;

extern crate clap;
extern crate xor_utils;
extern crate hex;
extern crate base64;

#[macro_use] extern crate log;
extern crate env_logger;

use log::LogLevel;
use clap::{App, Arg, ArgMatches};
use std::io;
use std::io::{Cursor};
use std::fs;
use std::path::Path;
use std::fs::{File, OpenOptions, DirEntry};
use std::io::{Write, Read};
use xor_utils::Xor;
use xor_utils::*;
use hex::{ToHex};

fn main() {
    env_logger::init().unwrap();

    // Parse arguments and provide help.
    let matches = App::new("xor")
        .version("1.3.0")
        .about("XORs input against a provided key")
        .author("Gavyn Riebau")
        .arg(Arg::with_name("key")
             .help("The file containing the key data, or a provided string, against which input will be XOR'd. This should be larger than the given input data or will need to be repeated to encode the input data.")
             .long("key")
             .short("k")
             .required(true)
             .value_name("KEY"))
        .arg(Arg::with_name("input")
             .help("The file / directory from which input data will be read, if omitted input will be read from stdin.\nIf a directory is specified, all files inside the directory will be encryted.")
             .long("input")
             .short("i")
             .required(false)
             .value_name("FILE"))
        .arg(Arg::with_name("recursive")
             .help("Recursively encrypt files and subfolders starting at the given directory")
             .long("recursive")
             .short("r")
             .conflicts_with("output")
             .value_name("DIRECTORY"))
        .arg(Arg::with_name("output")
             .help("The file to which encoded data will be written, if omitted output will be written to stdout.\nIt's recommended to write output to a file for cases where the encoded data contains non-unicode characters.")
             .long("output")
             .short("o")
             .required(false)
             .value_name("FILE"))
        .arg(Arg::with_name("verbose")
             .help("Increases the level of feedback given")
             .long("verbose")
             .short("v")
             .required(false))
         .get_matches();

    let key_bytes = get_key_bytes(&matches);

    if matches.is_present("recursive") {
        // Recursively encrypt files and folders in the specified directory.
        let starting_dir_name = matches.value_of("recursive").unwrap();
        let starting_dir = Path::new(starting_dir_name);

        encrypt_path(starting_dir, &key_bytes);
    } else {
        // If the "file" argument was supplied input will be read from the file, otherwise
        // input is read from stdin.
        let input : Box<Read> = if matches.is_present("input") {
            Box::new(File::open(matches.value_of("input").unwrap()).unwrap())
        } else {
            Box::new(io::stdin())
        };

        // If "output" argument was supplied output will be written to a file, otherwise
        // it's written to stdout.
        let output : Box<Write> = if matches.is_present("output") {
            Box::new(OpenOptions::new()
                .write(true)
                .create(true)
                .truncate(true)
                .open(matches.value_of("output").unwrap())
                .unwrap())
        } else {
            Box::new(stdout_writer::StdoutWriter{})
        };

        encrypt_reader(input, &key_bytes, output);
    }
}

fn encrypt_reader(mut input : Box<Read>, key : &Vec<u8>, mut output : Box<Write>) {
    let encoded_bytes = input.by_ref().xor(&key);
    let _ = output.write_all(encoded_bytes.as_slice());
    output.flush().unwrap();
}

fn encrypt_path(p : &Path, key : &Vec<u8>) {
    for item in fs::read_dir(p).unwrap() {
        let entry = item.unwrap();
        xor_entry(&entry, key);
    }
}

fn xor_entry(entry : &DirEntry, key : &Vec<u8>) {
    if let Ok(entry_type) = entry.file_type() {
        if entry_type.is_dir() {
            xor_dir(entry, key);
        } else if entry_type.is_file() {
            xor_file(entry, key);
        } else if entry_type.is_symlink() {
            xor_symlink(entry, key);
        }
    }
}

fn xor_file(entry : &DirEntry, key : &Vec<u8>) {
    info!("Encrypting file {:?}", entry);

    if let Ok(mut file) = File::open(entry.path()) {
        let mut reader = &mut file as &mut Read;
        let cypher_text = reader.xor(&key);

        let mut writer = OpenOptions::new()
            .write(true)
            .truncate(true)
            .open(entry.path())
            .unwrap();

        writer.write_all(cypher_text.as_slice()).unwrap();
    }
}

fn xor_symlink(entry : &DirEntry, key : &Vec<u8>) {
    info!("Encrypting symlink {:?}", entry);
}

fn xor_dir(entry : &DirEntry, key : &Vec<u8>) {
    info!("Encrypting dir {:?}", entry);

    match fs::read_dir(entry.path()) {
        Ok(entries) => {
            for child in entries {
                if let Ok(child) = child {
                    xor_entry(&child, key);
                }
            }
        },
        Err(e) => {
            let mut stderr = io::stderr();
            let _ = stderr.write_fmt(format_args!("Failed to read directory: {}", e));
        }
    }

    // Encrypt the directory entry itself.
    let file_name = entry.file_name();

    if let Some(original_name) = file_name.to_str() {

        let mut encrypted = Vec::with_capacity(original_name.len());
        for (d, k) in original_name.as_bytes().iter().zip(key) {
            encrypted.push(d ^ k);
        }

        let hex_name = to_hex(encrypted);
        let encrypted_name = base64::encode(hex_name.as_slice());

        let full_path_buf = entry.path();
        let full_path = full_path_buf.as_path();
        let parent_path = full_path.parent().unwrap();

        let src_file_path_buf = parent_path.join(original_name);
        let dst_file_path_buf = parent_path.join(encrypted_name);
        let src_file_path = src_file_path_buf.as_path();
        let dst_file_path = dst_file_path_buf.as_path();

        // TODO: REMOVE
        //println!("full {:?} parent {:?}", full_path, parent);
        println!("Mv {:?} to {:?}", src_file_path, dst_file_path);

        fs::rename(src_file_path, dst_file_path).unwrap();
    }
}

fn to_hex(bytes : Vec<u8>) -> Vec<u8> {
    static CHARS: &'static [u8] = b"0123456789abcdef";

    let mut v = Vec::with_capacity(bytes.len() * 2);
    for &byte in bytes.iter() {
        v.push(CHARS[(byte >> 4) as usize]);
        v.push(CHARS[(byte & 0xf) as usize]);
    }

    v
}

fn get_key_bytes<'a>(matches: &'a ArgMatches<'a>) -> Vec<u8> {
    let mut key_bytes : Vec<u8> = Vec::new();

    let key = matches.value_of("key").unwrap();

    // If the key is a file, read the contents of the file.
    // Otherwise if key is a string, use the string bytes.
    if Path::new(key).exists() {
        File::open(key).unwrap().read_to_end(&mut key_bytes).unwrap();
    } else {
        key_bytes = key.to_string().into_bytes();
    }

    key_bytes
}

<commit_msg>Partway through rename files when recursively encrypting directories<commit_after>
mod stdout_writer;

extern crate clap;
extern crate xor_utils;
extern crate hex;
extern crate base64;

#[macro_use] extern crate log;
extern crate env_logger;

use log::LogLevel;
use clap::{App, Arg, ArgMatches};
use std::io;
use std::io::{Cursor};
use std::fs;
use std::path::Path;
use std::fs::{File, OpenOptions, DirEntry};
use std::io::{Write, Read};
use xor_utils::Xor;
use xor_utils::*;
use hex::{ToHex};

fn main() {
    env_logger::init().unwrap();

    // Parse arguments and provide help.
    let matches = App::new("xor")
        .version("1.3.0")
        .about("XORs input against a provided key")
        .author("Gavyn Riebau")
        .arg(Arg::with_name("key")
             .help("The file containing the key data, or a provided string, against which input will be XOR'd. This should be larger than the given input data or will need to be repeated to encode the input data.")
             .long("key")
             .short("k")
             .required(true)
             .value_name("KEY"))
        .arg(Arg::with_name("input")
             .help("The file / directory from which input data will be read, if omitted input will be read from stdin.\nIf a directory is specified, all files inside the directory will be encryted.")
             .long("input")
             .short("i")
             .required(false)
             .value_name("FILE"))
        .arg(Arg::with_name("recursive")
             .help("Recursively encrypt files and subfolders starting at the given directory")
             .long("recursive")
             .short("r")
             .conflicts_with("output")
             .value_name("DIRECTORY"))
        .arg(Arg::with_name("output")
             .help("The file to which encoded data will be written, if omitted output will be written to stdout.\nIt's recommended to write output to a file for cases where the encoded data contains non-unicode characters.")
             .long("output")
             .short("o")
             .required(false)
             .value_name("FILE"))
        .arg(Arg::with_name("verbose")
             .help("Increases the level of feedback given")
             .long("verbose")
             .short("v")
             .required(false))
         .get_matches();

    let key_bytes = get_key_bytes(&matches);

    if matches.is_present("recursive") {
        // Recursively encrypt files and folders in the specified directory.
        let starting_dir_name = matches.value_of("recursive").unwrap();
        let starting_dir = Path::new(starting_dir_name);

        encrypt_path(starting_dir, &key_bytes);
    } else {
        // If the "file" argument was supplied input will be read from the file, otherwise
        // input is read from stdin.
        let input : Box<Read> = if matches.is_present("input") {
            Box::new(File::open(matches.value_of("input").unwrap()).unwrap())
        } else {
            Box::new(io::stdin())
        };

        // If "output" argument was supplied output will be written to a file, otherwise
        // it's written to stdout.
        let output : Box<Write> = if matches.is_present("output") {
            Box::new(OpenOptions::new()
                .write(true)
                .create(true)
                .truncate(true)
                .open(matches.value_of("output").unwrap())
                .unwrap())
        } else {
            Box::new(stdout_writer::StdoutWriter{})
        };

        encrypt_reader(input, &key_bytes, output);
    }
}

fn encrypt_reader(mut input : Box<Read>, key : &Vec<u8>, mut output : Box<Write>) {
    let encoded_bytes = input.by_ref().xor(&key);
    let _ = output.write_all(encoded_bytes.as_slice());
    output.flush().unwrap();
}

fn encrypt_path(p : &Path, key : &Vec<u8>) {
    for item in fs::read_dir(p).unwrap() {
        let entry = item.unwrap();
        xor_entry(&entry, key);
    }
}

fn xor_entry(entry : &DirEntry, key : &Vec<u8>) {
    if let Ok(entry_type) = entry.file_type() {
        if entry_type.is_dir() {
            xor_dir(entry, key);
        } else if entry_type.is_file() {
            xor_file(entry, key);
        } else if entry_type.is_symlink() {
            xor_symlink(entry, key);
        }
    }
}

fn xor_file(entry : &DirEntry, key : &Vec<u8>) {
    info!("Encrypting file {:?}", entry);

    if let Ok(mut file) = File::open(entry.path()) {
        let mut reader = &mut file as &mut Read;
        let cypher_text = reader.xor(&key);

        let mut writer = OpenOptions::new()
            .write(true)
            .truncate(true)
            .open(entry.path())
            .unwrap();

        writer.write_all(cypher_text.as_slice()).unwrap();
    }
}

fn xor_symlink(entry : &DirEntry, key : &Vec<u8>) {
    info!("Encrypting symlink {:?}", entry);
}

fn xor_dir(entry : &DirEntry, key : &Vec<u8>) {
    info!("Encrypting dir {:?}", entry);

    match fs::read_dir(entry.path()) {
        Ok(entries) => {
            for child in entries {
                if let Ok(child) = child {
                    xor_entry(&child, key);
                }
            }
        },
        Err(e) => {
            let mut stderr = io::stderr();
            let _ = stderr.write_fmt(format_args!("Failed to read directory: {}", e));
        }
    }

    // Encrypt the directory entry itself.
    let file_name = entry.file_name();

    if let Some(original_name) = file_name.to_str() {

        println!("original_name: {}", original_name);

        let mut key_repeated = repeat_key(key, original_name.len());
        let mut encrypted = Vec::with_capacity(original_name.len());
        for (d, k) in original_name.as_bytes().iter().zip(key_repeated) {
            encrypted.push(d ^ k);
        }

        let encrypted_name = to_hex_string(encrypted);
        println!("encrypted_name: {}", encrypted_name);

        let full_path_buf = entry.path();

        let full_path = full_path_buf.as_path();
        println!("full_path: {:?}", full_path_buf);

        let parent_path = full_path.parent().unwrap();
        println!("parent_path: {:?}", parent_path);

        let src_file_path_buf = parent_path.join(original_name);
        println!("src_file_path_buf: {:?}", src_file_path_buf);

        let dst_file_path_buf = parent_path.join(encrypted_name);
        println!("dst_file_path_buf: {:?}", dst_file_path_buf);

        let src_file_path = src_file_path_buf.as_path();
        println!("src_file_path: {:?}", src_file_path);

        let dst_file_path = dst_file_path_buf.as_path();
        println!("dst_file_path: {:?}", dst_file_path);

        // TODO: REMOVE
        //println!("full {:?} parent {:?}", full_path, parent);
        println!("Mv {:?} to {:?}", src_file_path, dst_file_path);

        fs::rename(src_file_path, dst_file_path).unwrap();
    }
}

/// Create a vector of bytes equal in length to the name of the file.
/// If the key is too small it'll be repeated to make up the required length.
fn repeat_key(key : &Vec<u8>, required_len : usize) -> Vec<u8> {
    let mut key_repeated = Vec::with_capacity(required_len);

    while key_repeated.len() < required_len {
        for &b in key {
            key_repeated.push(b);

            if key_repeated.len() == required_len {
                break;
            }
        }
    }

    key_repeated
}

fn to_hex_string(bytes: Vec<u8>) -> String {

  let strings: Vec<String> = bytes.iter()
                               .map(|b| format!("{:02X}", b))
                               .collect();

  strings.join("")
}

fn get_key_bytes<'a>(matches: &'a ArgMatches<'a>) -> Vec<u8> {
    let mut key_bytes : Vec<u8> = Vec::new();

    let key = matches.value_of("key").unwrap();

    // If the key is a file, read the contents of the file.
    // Otherwise if key is a string, use the string bytes.
    if Path::new(key).exists() {
        File::open(key).unwrap().read_to_end(&mut key_bytes).unwrap();
    } else {
        key_bytes = key.to_string().into_bytes();
    }

    key_bytes
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn to_hex_string_works() {
        let input_string = String::from("hello");
        let input_bytes = input_string.into_bytes();

        let hex_string = to_hex_string(input_bytes);
        assert_eq!(hex_string, "68656C6C6F");
    }

}

<|endoftext|>


<commit_before>use std::io;
use std::io::prelude::*;
use std::iter::repeat;
use std::str::FromStr;

enum Hamming { Encode, Decode }

fn main() {
    let title = "RUSTYHAM: A HAMMING CODE GENERATOR IN RUST";
    let border: String = repeat('=').take(title.len()).collect::<>();
    println!("{}", border);
    println!("{}", title);
    println!("{}", border);

    loop {
        let mut linebuf = String::new();
        println!("(1) Encode ASCII");
        println!("*(2) Encode binary");
        println!("(3) Decode to ASCII");
        println!("*(4) Decode to binary");
        println!("Note: options marked as * are not supported yet.");
        print!("Enter your choice: ");
        io::stdout().flush().unwrap();
        io::stdin().read_line(&mut linebuf).unwrap();

        let num = i32::from_str(linebuf.trim());
        match num.ok() {
            Some(n) => {
                let mut good = true;
                println!("{}", match n {
                    1 => hamming(Hamming::Encode),
                    3 => hamming(Hamming::Decode),
                    2 | 4 => "Sorry, not implemented yet.".to_string(),
                    _ => { good = false; "Invalid input.".to_string() }
                });
                if good { break; }
            }
            None => {
                println!("Invalid input.");
            }
        };
    }
}

fn hamming(action: Hamming) -> String {
    match action {
        Hamming::Encode => {
            // prompt for ASCII input
            let mut message = String::new();
            print!("Enter string to encode: ");
            io::stdout().flush().unwrap();
            io::stdin().read_line(&mut message).unwrap();

            // compute block and message length
            message = message.trim().to_string();
            let mlen = message.len() as u32 * 7;
            let lenpow = (2..).find(|&r| 2u32.pow(r) - r - 1 >= mlen).unwrap();
            let len = 2us.pow(lenpow) - 1;

            // the thing we're storing the hamming code in
            let mut code: Vec<bool> = repeat(false).take(len).collect::<>();

            // convert ASCII string to binary
            // IMPORTANT NOTE: the following line takes ownership of the
            // `message' variable. We no longer have access to the original
            // string from this point onwards.
            let bytes = message.into_bytes();
            let bytes_str = bytes.iter().map(|&c| format!("{:0>1$b}", c, 7))
                .collect::<Vec<String>>().concat();

            // set data bits
            let mut bytes_iter = bytes_str.chars();
            for i in 1..len {
                if (i & (i - 1)) != 0 {  // if i is not a power of 2
                    code[i-1] = bytes_iter.next().unwrap_or('0') == '1';
                }
            }

            // set parity bits
            for i in 0..lenpow {
                let bi = 2us.pow(i) - 1;
                let (mut parity, mut ignore, mut counter) = (false, false, 0);
                for j in bi..len {
                    if !ignore && code[j] { parity = !parity; }
                    counter += 1;
                    if counter >= 2u32.pow(i) {
                        ignore = !ignore;
                        counter = 0;
                    }
                }
                code[bi] = parity;
            }

            code.into_iter().map(|x| if x {"1"} else {"0"}).collect::<Vec<_>>().concat()
        },
        Hamming::Decode => {
            // prompt for binary input
            let mut code_str = String::new();
            print!("Enter Hamming code to decode: ");
            io::stdout().flush().unwrap();
            io::stdin().read_line(&mut code_str).unwrap();

            // verify parity bits, fix 1-bit-flipped errors if any
            // TODO avoid code repetition in Hamming::Encode =>
            let len = code_str.len() - 1;
            let lenpow = ((len + 1) as f32).sqrt().round() as u32;
            let chars = code_str.chars().map(|x| x == '1').collect::<Vec<bool>>();
            if (0..lenpow).any(|i| calc_parity(&chars, i)) {
                println!("There is an error!");
            }

            // collect all bits at non-powers-of-2
            let data = &code_str.trim().chars().enumerate()
                .filter(|x| ((x.0 + 1) & x.0) != 0).map(|x| x.1)
                .collect::<Vec<char>>()[..];
            let chars = data.chunks(7)
                .map(|x| u8::from_str_radix(&x.iter().cloned().collect::<String>()[..], 2).unwrap())
                .collect::<Vec<u8>>();

            String::from_utf8(chars).unwrap()
        }
    }
}

fn calc_parity(code: &Vec<bool>, i: u32) -> bool {
    let bi = 2us.pow(i) - 1;
    let (mut parity, mut ignore, mut counter) = (false, false, 0);
    for j in bi..code.len() {
        if !ignore && code[j] { parity = !parity }
        counter += 1;
        if counter >= 2u32.pow(i) {
            ignore = !ignore;
            counter = 0;
        }
    }
    parity
}
<commit_msg>finally killed repetition in Encode and Decode<commit_after>use std::io;
use std::io::prelude::*;
use std::iter::repeat;
use std::str::FromStr;

enum Hamming { Encode, Decode }

fn main() {
    let title = "RUSTYHAM: A HAMMING CODE GENERATOR IN RUST";
    let border: String = repeat('=').take(title.len()).collect::<>();
    println!("{}", border);
    println!("{}", title);
    println!("{}", border);

    loop {
        let mut linebuf = String::new();
        println!("(1) Encode ASCII");
        println!("*(2) Encode binary");
        println!("(3) Decode to ASCII");
        println!("*(4) Decode to binary");
        println!("Note: options marked as * are not supported yet.");
        print!("Enter your choice: ");
        io::stdout().flush().unwrap();
        io::stdin().read_line(&mut linebuf).unwrap();

        let num = i32::from_str(linebuf.trim());
        match num.ok() {
            Some(n) => {
                let mut good = true;
                println!("{}", match n {
                    1 => hamming(Hamming::Encode),
                    3 => hamming(Hamming::Decode),
                    2 | 4 => "Sorry, not implemented yet.".to_string(),
                    _ => { good = false; "Invalid input.".to_string() }
                });
                if good { break; }
            }
            None => {
                println!("Invalid input.");
            }
        };
    }
}

fn hamming(action: Hamming) -> String {
    match action {
        Hamming::Encode => {
            // prompt for ASCII input
            let mut message = String::new();
            print!("Enter string to encode: ");
            io::stdout().flush().unwrap();
            io::stdin().read_line(&mut message).unwrap();

            // compute block and message length
            message = message.trim().to_string();
            let mlen = message.len() as u32 * 7;
            let lenpow = (2..).find(|&r| 2u32.pow(r) - r - 1 >= mlen).unwrap();
            let len = 2us.pow(lenpow) - 1;

            // the thing we're storing the hamming code in
            let mut code: Vec<bool> = repeat(false).take(len).collect::<>();

            // convert ASCII string to binary
            // IMPORTANT NOTE: the following line takes ownership of the
            // `message' variable. We no longer have access to the original
            // string from this point onwards.
            let bytes = message.into_bytes();
            let bytes_str = bytes.iter().map(|&c| format!("{:0>1$b}", c, 7))
                .collect::<Vec<String>>().concat();

            // set data bits
            let mut bytes_iter = bytes_str.chars();
            for i in 1..len {
                if (i & (i - 1)) != 0 {  // if i is not a power of 2
                    code[i-1] = bytes_iter.next().unwrap_or('0') == '1';
                }
            }

            // set parity bits
            for i in 0..lenpow {
                code[2us.pow(i) - 1] = calc_parity(&code, i);
            }

            code.into_iter().map(|x| if x {"1"} else {"0"}).collect::<Vec<_>>().concat()
        },
        Hamming::Decode => {
            // prompt for binary input
            let mut code_str = String::new();
            print!("Enter Hamming code to decode: ");
            io::stdout().flush().unwrap();
            io::stdin().read_line(&mut code_str).unwrap();

            // verify parity bits, fix 1-bit-flipped errors if any
            let len = code_str.len() - 1;
            let lenpow = ((len + 1) as f32).sqrt().round() as u32;
            let chars = code_str.chars().map(|x| x == '1').collect::<Vec<bool>>();
            if (0..lenpow).any(|i| calc_parity(&chars, i)) {
                println!("There is an error!");
            }

            // collect all bits at non-powers-of-2
            let data = &code_str.trim().chars().enumerate()
                .filter(|x| ((x.0 + 1) & x.0) != 0).map(|x| x.1)
                .collect::<Vec<char>>()[..];
            let chars = data.chunks(7)
                .map(|x| u8::from_str_radix(&x.iter().cloned().collect::<String>()[..], 2).unwrap())
                .collect::<Vec<u8>>();

            String::from_utf8(chars).unwrap()
        }
    }
}

fn calc_parity(code: &Vec<bool>, i: u32) -> bool {
    let bi = 2us.pow(i) - 1;
    let (mut parity, mut ignore, mut counter) = (false, false, 0);
    for j in bi..code.len() {
        if !ignore && code[j] { parity = !parity }
        counter += 1;
        if counter >= 2u32.pow(i) {
            ignore = !ignore;
            counter = 0;
        }
    }
    parity
}
<|endoftext|>


<commit_before>#![allow(unstable)]
extern crate hyper;

#[macro_use] extern crate log;

use std::io::File;
use std::io::net::ip::Ipv4Addr;

use hyper::Get;
use hyper::header::ContentLength;
use hyper::server::{Server, Request, Response};
use hyper::uri::RequestUri::AbsolutePath;

macro_rules! try_return(
    ($e:expr) => {{
        match $e {
            Ok(v) => v,
            Err(e) => { error!("Error: {}", e); return; }
        }
    }}
);

fn default(req: Request, mut res: Response) {
    match req.uri {
        AbsolutePath(ref path) => match (&req.method, path.as_slice()) {
            (&Get, _) => {
                if path.as_slice() == "/" {
                    let out = try_return!(File::open(&Path::new("/Users/gsquire/poly/senior_project/html/index.html")).read_to_string());
                    res.headers_mut().set(ContentLength(out.len() as u64));
                    let mut res = try_return!(res.start());
                    try_return!(res.write_str(out.as_slice()));
                    try_return!(res.end());
                    return;
                }
            },
            _ => {
                *res.status_mut() = hyper::NotFound;
                try_return!(res.start().and_then(|res| res.end()));
                return;
            }
        },
        _ => {
            try_return!(res.start().and_then(|res| res.end()));
            return;
        }
    };
}

fn main() {
    let server = Server::http(Ipv4Addr(127, 0, 0, 1), 42007);
    let mut listener = server.listen(default).unwrap();
    println!("Listening on port 42007...");
    listener.await();
}
<commit_msg>added some more file handling capability<commit_after>#![allow(unstable)]
extern crate hyper;

#[macro_use] extern crate log;

use std::io::File;
use std::io::fs::PathExtensions;
use std::io::net::ip::Ipv4Addr;

use hyper::Get;
use hyper::header::ContentLength;
use hyper::server::{Server, Request, Response};
use hyper::uri::RequestUri::AbsolutePath;

macro_rules! try_return(
    ($e:expr) => {{
        match $e {
            Ok(v) => v,
            Err(e) => { error!("Error: {}", e); return; }
        }
    }}
);

fn send_file(path: &str, mut res: Response) {
    let root = "/Users/gsquire/poly/senior_project/html";
    let out: String;

    if path == "/" {
        out = try_return!(File::open(&Path::new(root.to_string() + "/index.html")).read_to_string());
    } else {
        // Let us check if the path actually exists.
        let path = Path::new(root.to_string() + path);
        if path.exists() {
            out = try_return!(File::open(&path).read_to_string());
        } else {
            // Return a 404.
            *res.status_mut() = hyper::NotFound;
            try_return!(res.start().and_then(|res| res.end()));
            return;
        }
    }

    res.headers_mut().set(ContentLength(out.len() as u64));
    let mut res = try_return!(res.start());
    try_return!(res.write_str(out.as_slice()));
    try_return!(res.end());
}

fn base(req: Request, mut res: Response) {
    match req.uri {
        AbsolutePath(ref path) => match (&req.method, path.as_slice()) {
            (&Get, _) => {
                send_file(path.as_slice(), res);
                return;
            },
            _ => {
                *res.status_mut() = hyper::NotFound;
                try_return!(res.start().and_then(|res| res.end()));
                return;
            }
        },
        _ => {
            try_return!(res.start().and_then(|res| res.end()));
            return;
        }
    };
}

fn main() {
    let server = Server::http(Ipv4Addr(127, 0, 0, 1), 42007);
    let mut listener = server.listen(base).unwrap();
    println!("Listening on port 42007...");
    listener.await();
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate clap;
#[macro_use]
extern crate lazy_static;

extern crate libc;
extern crate scmp;
extern crate tempdir;

use std::fs;
use std::env;
use std::ffi::OsStr;
use std::ffi::CString;
use std::io::Error;
use std::os::raw::c_void;
use std::os::unix::process::CommandExt;
use std::os::unix::ffi::OsStrExt;
use std::path::Path;
use std::path::PathBuf;
use std::process::Command;
use scmp::*;

unsafe fn allow_syscall(ctx : *mut c_void, nr : u32) {
    assert!(seccomp_rule_add_array(ctx, SCMP_ACT_ALLOW, nr as i32, 0, std::ptr::null()) == 0);
}

fn setup_seccomp() -> Result<(), Error> {
    unsafe {
        let ctx0 = seccomp_init(SCMP_ACT_TRAP);
        assert!(!ctx0.is_null());
        allow_syscall(ctx0, __NR_execve);
        allow_syscall(ctx0, __NR_brk);
        allow_syscall(ctx0, __NR_access);
        allow_syscall(ctx0, __NR_mmap);
        allow_syscall(ctx0, __NR_open);
        allow_syscall(ctx0, __NR_read);
        allow_syscall(ctx0, __NR_write);
        allow_syscall(ctx0, __NR_stat);
        allow_syscall(ctx0, __NR_fstat);
        allow_syscall(ctx0, __NR_close);
        allow_syscall(ctx0, __NR_mprotect);
        allow_syscall(ctx0, __NR_arch_prctl);
        allow_syscall(ctx0, __NR_munmap);
        allow_syscall(ctx0, __NR_set_tid_address);
        allow_syscall(ctx0, __NR_set_robust_list);
        allow_syscall(ctx0, __NR_rt_sigaction);
        allow_syscall(ctx0, __NR_rt_sigprocmask);
        allow_syscall(ctx0, __NR_getrlimit);
        allow_syscall(ctx0, __NR_statfs);
        allow_syscall(ctx0, __NR_fcntl);
        allow_syscall(ctx0, __NR_geteuid);
        allow_syscall(ctx0, __NR_umask);
        allow_syscall(ctx0, __NR_pipe);
        allow_syscall(ctx0, __NR_clone);
        allow_syscall(ctx0, __NR_wait4);
        allow_syscall(ctx0, __NR_exit_group);
        allow_syscall(ctx0, __NR_dup);
        allow_syscall(ctx0, __NR_ioctl);
        allow_syscall(ctx0, __NR_getpid);
        allow_syscall(ctx0, __NR_gettid);
        allow_syscall(ctx0, __NR_tgkill);
        allow_syscall(ctx0, __NR_rt_sigreturn);
        allow_syscall(ctx0, __NR_socket);
        allow_syscall(ctx0, __NR_connect);
        allow_syscall(ctx0, __NR_lseek);
        allow_syscall(ctx0, __NR_openat);
        allow_syscall(ctx0, __NR_unlinkat);
        allow_syscall(ctx0, __NR_utimensat);
        allow_syscall(ctx0, __NR_lstat);
        allow_syscall(ctx0, __NR_fchmod);
        allow_syscall(ctx0, __NR_unlink);
        allow_syscall(ctx0, __NR_utime);
        allow_syscall(ctx0, __NR_futex);
        allow_syscall(ctx0, __NR_chmod);
        assert!(seccomp_load(ctx0) == 0);
    }
    return Ok(());
}

const RENAME_NOREPLACE : u64 = 1;

fn rename_noreplace(path0 : &Path, path1 : &Path) -> Result<(), Error> {
    unsafe {
        if libc::syscall(__NR_renameat2 as i64, libc::AT_FDCWD, CString::new(path0.as_os_str().as_bytes()).unwrap().as_ptr(), libc::AT_FDCWD, CString::new(path1.as_os_str().as_bytes()).unwrap().as_ptr(), RENAME_NOREPLACE, 0) == 0 {
            return Ok(());
        } else {
            return Err(Error::last_os_error());
        }
    }
}

fn rename_or_suffix(path0 : &Path, path1 : &Path) -> Result<PathBuf, Error> {
    let rv = rename_noreplace(path0, path1);
    if let Err(err) = rv {
        if err.raw_os_error().expect("Error fails to wrap errno") != libc::EEXIST {
            return Err(err);
        }
        for suffix in 1.. {
            let mut builder = path1.as_os_str().to_os_string();
            builder.push(format!(".{}", suffix));
            let rv = rename_noreplace(path0, Path::new(&builder));
            if let Err(err) = rv {
                if err.raw_os_error().expect("Error fails to wrap errno") != libc::EEXIST {
                    return Err(err);
                }
                continue;
            }
            return Ok(Path::new(&builder).to_path_buf());
        }
        return Ok(PathBuf::new()); // XXX Unreachable
    } else {
        return Ok(path1.to_path_buf());
    }
}

struct ArchiveType {
    extensions: Vec<&'static str>,
    extract_cmd: Vec<&'static str>,
}

lazy_static! {
    static ref ARCHIVE_TYPES : Vec<ArchiveType> = vec![
        ArchiveType {
            extensions: vec![
                ".tgz",
                ".tar.gz",
                ".tar.Z",
                ".tbz2",
                ".tar.bz2",
                ".tlz",
                ".tar.lz",
                ".tar.lzma",
                ".tar.lzo",
                ".tar.xz",
            ],
            extract_cmd: vec!["tar", "-xf"],
        },
        ArchiveType {
            extensions: vec![
                ".zip",
            ],
            extract_cmd: vec!["unzip", "--"],
        },
        ArchiveType {
            extensions: vec![
                ".rar",
            ],
            extract_cmd: vec!["unrar", "x", "--"],
        },
    ];
}

fn main() {
    let matches = clap_app!(myapp =>
        (@arg ARCHIVE: +required ... "Archives to extract")
    ).get_matches();

    let mut found = false;

    for arch in matches.values_of_os("ARCHIVE").unwrap() {
        let arch = Path::new(arch);
        println!("Extract {:?}", arch);
        for at in ARCHIVE_TYPES.iter() {
            for &ext in &at.extensions {
                if !arch.as_os_str().as_bytes().ends_with(ext.as_bytes()) {
                    continue;
                }
                found = true;

                let noext : &OsStr = OsStrExt::from_bytes(&arch.as_os_str().as_bytes()[0..arch.as_os_str().as_bytes().len()-ext.as_bytes().len()]);
                let stripped =
                    if let Some(noext) = Path::new(noext).file_name() {
                        noext
                    } else {
                        OsStr::new("extracted")
                    };
                let cmd = at.extract_cmd[0];
                let tmpdir = tempdir::TempDir::new_in(
                    env::current_dir().expect("Unable to get the current directory"),
                    stripped.to_string_lossy().to_mut().as_str()).expect("Unable to create temporary directory");
                let status = Command::new(cmd)
                    .args(&at.extract_cmd[1..])
                    .arg(&arch)
                    .current_dir(&tmpdir)
                    .before_exec(setup_seccomp)
                    .status()
                    .expect(format!("Failed to launch {}", cmd).as_str());
                assert!(status.success(), format!("{} wasn't successful", cmd));
                let extracted_path = tmpdir.into_path();
                let mut dir_list = fs::read_dir(&extracted_path).expect("Unable to read the extraction directory");
                if let Some(path) = dir_list.next() {
                    let path = path.expect("Unable to iterate the extraction directory");
                    if let Some(path1) = dir_list.next() {
                        path1.expect("Unable to iterate the extraction directory");
                        let newname = rename_or_suffix(&extracted_path, Path::new(stripped)).expect("Failed to rename");
                        println!("Extracted to {:?}", newname);
                    } else {
                        let newname = rename_or_suffix(&path.path(), Path::new(stripped)).expect("Failed to rename");
                        println!("Extracted to {:?}", newname);
                        fs::remove_dir(&extracted_path).expect("Failed to remove empty extraction directory");
                    }
                } else {
                    println!("No files were extracted");
                }
                break;
            }
        }
        if !found {
            println!("Can't handle {:?}", arch);
        }
    }
}

<commit_msg>Factor in path_to_cstring<commit_after>#[macro_use]
extern crate clap;
#[macro_use]
extern crate lazy_static;

extern crate libc;
extern crate scmp;
extern crate tempdir;

use std::fs;
use std::env;
use std::ffi::OsStr;
use std::ffi::CString;
use std::io::Error;
use std::os::raw::c_void;
use std::os::unix::process::CommandExt;
use std::os::unix::ffi::OsStrExt;
use std::path::Path;
use std::path::PathBuf;
use std::process::Command;
use scmp::*;

unsafe fn allow_syscall(ctx : *mut c_void, nr : u32) {
    assert!(seccomp_rule_add_array(ctx, SCMP_ACT_ALLOW, nr as i32, 0, std::ptr::null()) == 0);
}

fn setup_seccomp() -> Result<(), Error> {
    unsafe {
        let ctx0 = seccomp_init(SCMP_ACT_TRAP);
        assert!(!ctx0.is_null());
        allow_syscall(ctx0, __NR_execve);
        allow_syscall(ctx0, __NR_brk);
        allow_syscall(ctx0, __NR_access);
        allow_syscall(ctx0, __NR_mmap);
        allow_syscall(ctx0, __NR_open);
        allow_syscall(ctx0, __NR_read);
        allow_syscall(ctx0, __NR_write);
        allow_syscall(ctx0, __NR_stat);
        allow_syscall(ctx0, __NR_fstat);
        allow_syscall(ctx0, __NR_close);
        allow_syscall(ctx0, __NR_mprotect);
        allow_syscall(ctx0, __NR_arch_prctl);
        allow_syscall(ctx0, __NR_munmap);
        allow_syscall(ctx0, __NR_set_tid_address);
        allow_syscall(ctx0, __NR_set_robust_list);
        allow_syscall(ctx0, __NR_rt_sigaction);
        allow_syscall(ctx0, __NR_rt_sigprocmask);
        allow_syscall(ctx0, __NR_getrlimit);
        allow_syscall(ctx0, __NR_statfs);
        allow_syscall(ctx0, __NR_fcntl);
        allow_syscall(ctx0, __NR_geteuid);
        allow_syscall(ctx0, __NR_umask);
        allow_syscall(ctx0, __NR_pipe);
        allow_syscall(ctx0, __NR_clone);
        allow_syscall(ctx0, __NR_wait4);
        allow_syscall(ctx0, __NR_exit_group);
        allow_syscall(ctx0, __NR_dup);
        allow_syscall(ctx0, __NR_ioctl);
        allow_syscall(ctx0, __NR_getpid);
        allow_syscall(ctx0, __NR_gettid);
        allow_syscall(ctx0, __NR_tgkill);
        allow_syscall(ctx0, __NR_rt_sigreturn);
        allow_syscall(ctx0, __NR_socket);
        allow_syscall(ctx0, __NR_connect);
        allow_syscall(ctx0, __NR_lseek);
        allow_syscall(ctx0, __NR_openat);
        allow_syscall(ctx0, __NR_unlinkat);
        allow_syscall(ctx0, __NR_utimensat);
        allow_syscall(ctx0, __NR_lstat);
        allow_syscall(ctx0, __NR_fchmod);
        allow_syscall(ctx0, __NR_unlink);
        allow_syscall(ctx0, __NR_utime);
        allow_syscall(ctx0, __NR_futex);
        allow_syscall(ctx0, __NR_chmod);
        assert!(seccomp_load(ctx0) == 0);
    }
    return Ok(());
}

const RENAME_NOREPLACE : u64 = 1;

fn path_to_cstring(path : &Path) -> CString {
    return CString::new(path.as_os_str().as_bytes()).unwrap();
}

fn rename_noreplace(path0 : &Path, path1 : &Path) -> Result<(), Error> {
    unsafe {
        if libc::syscall(__NR_renameat2 as i64, libc::AT_FDCWD, path_to_cstring(path0).as_ptr(), libc::AT_FDCWD, path_to_cstring(path1).as_ptr(), RENAME_NOREPLACE, 0) == 0 {
            return Ok(());
        } else {
            return Err(Error::last_os_error());
        }
    }
}

fn rename_or_suffix(path0 : &Path, path1 : &Path) -> Result<PathBuf, Error> {
    let rv = rename_noreplace(path0, path1);
    if let Err(err) = rv {
        if err.raw_os_error().expect("Error fails to wrap errno") != libc::EEXIST {
            return Err(err);
        }
        for suffix in 1.. {
            let mut builder = path1.as_os_str().to_os_string();
            builder.push(format!(".{}", suffix));
            let rv = rename_noreplace(path0, Path::new(&builder));
            if let Err(err) = rv {
                if err.raw_os_error().expect("Error fails to wrap errno") != libc::EEXIST {
                    return Err(err);
                }
                continue;
            }
            return Ok(Path::new(&builder).to_path_buf());
        }
        return Ok(PathBuf::new()); // XXX Unreachable
    } else {
        return Ok(path1.to_path_buf());
    }
}

struct ArchiveType {
    extensions: Vec<&'static str>,
    extract_cmd: Vec<&'static str>,
}

lazy_static! {
    static ref ARCHIVE_TYPES : Vec<ArchiveType> = vec![
        ArchiveType {
            extensions: vec![
                ".tgz",
                ".tar.gz",
                ".tar.Z",
                ".tbz2",
                ".tar.bz2",
                ".tlz",
                ".tar.lz",
                ".tar.lzma",
                ".tar.lzo",
                ".tar.xz",
            ],
            extract_cmd: vec!["tar", "-xf"],
        },
        ArchiveType {
            extensions: vec![
                ".zip",
            ],
            extract_cmd: vec!["unzip", "--"],
        },
        ArchiveType {
            extensions: vec![
                ".rar",
            ],
            extract_cmd: vec!["unrar", "x", "--"],
        },
    ];
}

fn main() {
    let matches = clap_app!(myapp =>
        (@arg ARCHIVE: +required ... "Archives to extract")
    ).get_matches();

    let mut found = false;

    for arch in matches.values_of_os("ARCHIVE").unwrap() {
        let arch = Path::new(arch);
        println!("Extract {:?}", arch);
        for at in ARCHIVE_TYPES.iter() {
            for &ext in &at.extensions {
                if !arch.as_os_str().as_bytes().ends_with(ext.as_bytes()) {
                    continue;
                }
                found = true;

                let noext : &OsStr = OsStrExt::from_bytes(&arch.as_os_str().as_bytes()[0..arch.as_os_str().as_bytes().len()-ext.as_bytes().len()]);
                let stripped =
                    if let Some(noext) = Path::new(noext).file_name() {
                        noext
                    } else {
                        OsStr::new("extracted")
                    };
                let cmd = at.extract_cmd[0];
                let tmpdir = tempdir::TempDir::new_in(
                    env::current_dir().expect("Unable to get the current directory"),
                    stripped.to_string_lossy().to_mut().as_str()).expect("Unable to create temporary directory");
                let status = Command::new(cmd)
                    .args(&at.extract_cmd[1..])
                    .arg(&arch)
                    .current_dir(&tmpdir)
                    .before_exec(setup_seccomp)
                    .status()
                    .expect(format!("Failed to launch {}", cmd).as_str());
                assert!(status.success(), format!("{} wasn't successful", cmd));
                let extracted_path = tmpdir.into_path();
                let mut dir_list = fs::read_dir(&extracted_path).expect("Unable to read the extraction directory");
                if let Some(path) = dir_list.next() {
                    let path = path.expect("Unable to iterate the extraction directory");
                    if let Some(path1) = dir_list.next() {
                        path1.expect("Unable to iterate the extraction directory");
                        let newname = rename_or_suffix(&extracted_path, Path::new(stripped)).expect("Failed to rename");
                        println!("Extracted to {:?}", newname);
                    } else {
                        let newname = rename_or_suffix(&path.path(), Path::new(stripped)).expect("Failed to rename");
                        println!("Extracted to {:?}", newname);
                        fs::remove_dir(&extracted_path).expect("Failed to remove empty extraction directory");
                    }
                } else {
                    println!("No files were extracted");
                }
                break;
            }
        }
        if !found {
            println!("Can't handle {:?}", arch);
        }
    }
}

<|endoftext|>


<commit_before>// Copyright (c) 2015 - 2017 Markus Kohlhase <mail@markus-kohlhase.de>

#![feature(plugin,custom_derive)]
#![plugin(rocket_codegen)]
#![allow(unmanaged_state)]

#[macro_use]
extern crate log;
extern crate slog_envlogger;
#[macro_use]
extern crate quick_error;
extern crate clap;
#[macro_use]
extern crate rusted_cypher;
extern crate r2d2;
extern crate r2d2_cypher;
extern crate uuid;
extern crate fast_chemail;
extern crate url;
extern crate rocket;
extern crate rocket_contrib;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
extern crate chrono;
#[macro_use]
extern crate lazy_static;
extern crate regex;
extern crate pwhash;

mod entities;
mod business;
mod adapters;
mod infrastructure;

fn main() {
    let _guard = slog_envlogger::init().unwrap();
    infrastructure::cli::run();
}
<commit_msg>fix(logging): defer proper logging<commit_after>// Copyright (c) 2015 - 2017 Markus Kohlhase <mail@markus-kohlhase.de>

#![feature(plugin,custom_derive)]
#![plugin(rocket_codegen)]
#![allow(unmanaged_state)]

#[macro_use]
extern crate log;
extern crate slog_envlogger;
#[macro_use]
extern crate quick_error;
extern crate clap;
#[macro_use]
extern crate rusted_cypher;
extern crate r2d2;
extern crate r2d2_cypher;
extern crate uuid;
extern crate fast_chemail;
extern crate url;
extern crate rocket;
extern crate rocket_contrib;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
extern crate chrono;
#[macro_use]
extern crate lazy_static;
extern crate regex;
extern crate pwhash;

mod entities;
mod business;
mod adapters;
mod infrastructure;

fn main() {
    // TODO: setup proper logging with rocket!
    // let _guard = slog_envlogger::init().unwrap();
    infrastructure::cli::run();
}
<|endoftext|>


<commit_before>#![crate_name="acronymy"]
#![crate_type = "bin"]

#![feature(rt, start, str_char)]

extern crate libc;
extern crate time;
extern crate url;
extern crate capnp;
extern crate capnp_rpc;
extern crate fdstream;
extern crate sqlite3;

pub mod grain_capnp {
  include!(concat!(env!("OUT_DIR"), "/grain_capnp.rs"));
}

pub mod util_capnp {
  include!(concat!(env!("OUT_DIR"), "/util_capnp.rs"));
}

pub mod web_session_capnp {
  include!(concat!(env!("OUT_DIR"), "/web_session_capnp.rs"));
}

pub mod server;

/// We write our own entry point because Rust's default lang_start() does some
/// fancy memory protection that fails if procfs is not mounted.
#[start]
fn start(argc: isize, argv: *const *const u8) -> isize {

    // We need to do this to get ::std::env:args() to work.
    unsafe { ::std::rt::args::init(argc, argv); }

    match server::main() {
        Ok(()) => {return 0;}
        Err(e) => {
            println!("error: {}", e);
            return 1;
        }
    }
}
<commit_msg>We no longer need to define our own entry point, thanks to https://github.com/rust-lang/rust/pull/27073<commit_after>#![crate_name="acronymy"]
#![crate_type = "bin"]

#![feature(str_char)]

extern crate libc;
extern crate time;
extern crate url;
extern crate capnp;
extern crate capnp_rpc;
extern crate fdstream;
extern crate sqlite3;

pub mod grain_capnp {
  include!(concat!(env!("OUT_DIR"), "/grain_capnp.rs"));
}

pub mod util_capnp {
  include!(concat!(env!("OUT_DIR"), "/util_capnp.rs"));
}

pub mod web_session_capnp {
  include!(concat!(env!("OUT_DIR"), "/web_session_capnp.rs"));
}

pub mod server;

fn main() {
    match server::main() {
        Ok(()) => {return;}
        Err(e) => {
            panic!("error: {}", e);
        }
    }
}
<|endoftext|>