<commit_before>// Copyright 2014 Alexis Mousset. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! ESMTP features

use std::str::FromStr;
use std::fmt::{Display, Formatter, Result};
use std::result::Result as RResult;

use common::CRLF;
use response::Response;
use self::Extension::{EightBitMime, SmtpUtfEight, StartTls, Size};

/// Supported ESMTP keywords
#[derive(PartialEq,Eq,Copy,Clone,Debug)]
pub enum Extension {
    /// 8BITMIME keyword
    ///
    /// RFC 6152 : https://tools.ietf.org/html/rfc6152
    EightBitMime,
    /// SMTPUTF8 keyword
    ///
    /// RFC 6531 : https://tools.ietf.org/html/rfc6531
    SmtpUtfEight,
    /// STARTTLS keyword
    ///
    /// RFC 2487 : http://tools.ietf.org/html/rfc2487
    StartTls,
    /// SIZE keyword
    ///
    /// RFC 1427 : https://tools.ietf.org/html/rfc1427
    Size(usize),
}

impl Display for Extension {
    fn fmt(&self, f: &mut Formatter) -> Result {
        write! (f, "{}",
            match self {
                &EightBitMime => "8BITMIME".to_string(),
                &SmtpUtfEight => "SMTPUTF8".to_string(),
                &StartTls => "STARTTLS".to_string(),
                &Size(ref size) => format!("SIZE={}", size)
            }
        )
    }
}

impl FromStr for Extension {
    // TODO: check RFC
    type Err = &'static str;
    fn from_str(s: &str) -> RResult<Extension, &'static str> {
        let splitted : Vec<&str> = s.splitn(1, ' ').collect();
        match splitted.len() {
            1 => match splitted[0] {
                     "8BITMIME" => Ok(EightBitMime),
                     "SMTPUTF8" => Ok(SmtpUtfEight),
                     "STARTTLS" => Ok(StartTls),
                     _ => Err("Unknown extension"),
                 },
            2 => match (splitted[0], splitted[1].parse::<usize>()) {
                     ("SIZE", Ok(size)) => Ok(Size(size)),
                     _ => Err("Can't parse size"),
                 },
            _ => Err("Empty extension?"),
        }
    }
}

impl Extension {
    /// Checks if the ESMTP keyword is the same
    pub fn same_extension_as(&self, other: &Extension) -> bool {
        if self == other {
            return true;
        }
        match (self, other) {
            (&Size(_), &Size(_)) => true,
            _ => false,
        }
    }

    /// Parses supported ESMTP features
    pub fn parse_esmtp_response(message: &str) -> Option<Vec<Extension>> {
        let mut esmtp_features = Vec::new();
        for line in message.split(CRLF) {
            if let Ok(Response{code: 250, message}) = line.parse::<Response>() {
                if let Ok(keyword) = message.unwrap().as_slice().parse::<Extension>() {
                    esmtp_features.push(keyword);
                };
            }
        }
        Some(esmtp_features)
    }

    /// Returns the string to add to the mail command
    pub fn client_mail_option(&self) -> Option<&str> {
        match *self {
            EightBitMime => Some("BODY=8BITMIME"),
            _ => None,
        }
    }
}

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

    #[test]
    fn test_fmt() {
        assert_eq!(format!("{}", Extension::EightBitMime), "8BITMIME".to_string());
        assert_eq!(format!("{}", Extension::Size(42)), "SIZE=42".to_string());
    }

    #[test]
    fn test_from_str() {
        assert_eq!("8BITMIME".parse::<Extension>(), Ok(Extension::EightBitMime));
        assert_eq!("SIZE 42".parse::<Extension>(), Ok(Extension::Size(42)));
        assert!("SIZ 42".parse::<Extension>().is_err());
        assert!("SIZE 4a2".parse::<Extension>().is_err());
        // TODO: accept trailing spaces ?
        assert!("SIZE 42 ".parse::<Extension>().is_err());
    }

    #[test]
    fn test_same_extension_as() {
        assert_eq!(Extension::EightBitMime.same_extension_as(&Extension::EightBitMime), true);
        assert_eq!(Extension::Size(42).same_extension_as(&Extension::Size(42)), true);
        assert_eq!(Extension::Size(42).same_extension_as(&Extension::Size(43)), true);
        assert_eq!(Extension::Size(42).same_extension_as(&Extension::EightBitMime), false);
        assert_eq!(Extension::EightBitMime.same_extension_as(&Extension::SmtpUtfEight), false);
    }

    #[test]
    fn test_parse_esmtp_response() {
        assert_eq!(Extension::parse_esmtp_response("me\r\n250-8BITMIME\r\n250 SIZE 42"),
            Some(vec![Extension::EightBitMime, Extension::Size(42)]));
        assert_eq!(Extension::parse_esmtp_response("me\r\n250-8BITMIME\r\n250 UNKNON 42"),
            Some(vec![Extension::EightBitMime]));
        assert_eq!(Extension::parse_esmtp_response("me\r\n250-9BITMIME\r\n250 SIZE a"),
            Some(vec![]));
        assert_eq!(Extension::parse_esmtp_response("me\r\n250-SIZE 42\r\n250 SIZE 43"),
            Some(vec![Extension::Size(42), Extension::Size(43)]));
        assert_eq!(Extension::parse_esmtp_response(""),
            Some(vec![]));
    }
}
<commit_msg>esmtp_features does not need to be an Option<commit_after>// Copyright 2014 Alexis Mousset. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! ESMTP features

use std::str::FromStr;
use std::fmt::{Display, Formatter, Result};
use std::result::Result as RResult;

use common::CRLF;
use response::Response;
use self::Extension::{EightBitMime, SmtpUtfEight, StartTls, Size};

/// Supported ESMTP keywords
#[derive(PartialEq,Eq,Copy,Clone,Debug)]
pub enum Extension {
    /// 8BITMIME keyword
    ///
    /// RFC 6152 : https://tools.ietf.org/html/rfc6152
    EightBitMime,
    /// SMTPUTF8 keyword
    ///
    /// RFC 6531 : https://tools.ietf.org/html/rfc6531
    SmtpUtfEight,
    /// STARTTLS keyword
    ///
    /// RFC 2487 : http://tools.ietf.org/html/rfc2487
    StartTls,
    /// SIZE keyword
    ///
    /// RFC 1427 : https://tools.ietf.org/html/rfc1427
    Size(usize),
}

impl Display for Extension {
    fn fmt(&self, f: &mut Formatter) -> Result {
        write! (f, "{}",
            match self {
                &EightBitMime => "8BITMIME".to_string(),
                &SmtpUtfEight => "SMTPUTF8".to_string(),
                &StartTls => "STARTTLS".to_string(),
                &Size(ref size) => format!("SIZE={}", size),
            }
        )
    }
}

impl FromStr for Extension {
    // TODO: check RFC
    type Err = &'static str;
    fn from_str(s: &str) -> RResult<Extension, &'static str> {
        let splitted : Vec<&str> = s.splitn(1, ' ').collect();
        match splitted.len() {
            1 => match splitted[0] {
                     "8BITMIME" => Ok(EightBitMime),
                     "SMTPUTF8" => Ok(SmtpUtfEight),
                     "STARTTLS" => Ok(StartTls),
                     _ => Err("Unknown extension"),
                 },
            2 => match (splitted[0], splitted[1].parse::<usize>()) {
                     ("SIZE", Ok(size)) => Ok(Size(size)),
                     _ => Err("Can't parse size"),
                 },
            _ => Err("Empty extension?"),
        }
    }
}

impl Extension {
    /// Checks if the ESMTP keyword is the same
    pub fn same_extension_as(&self, other: &Extension) -> bool {
        if self == other {
            return true;
        }
        match (self, other) {
            (&Size(_), &Size(_)) => true,
            _ => false,
        }
    }

    /// Parses supported ESMTP features
    pub fn parse_esmtp_response(message: &str) -> Vec<Extension> {
        let mut esmtp_features = Vec::new();
        for line in message.split(CRLF) {
            if let Ok(Response{code: 250, message}) = line.parse::<Response>() {
                if let Ok(keyword) = message.unwrap().as_slice().parse::<Extension>() {
                    esmtp_features.push(keyword);
                };
            }
        }
        esmtp_features
    }

    /// Returns the string to add to the mail command
    pub fn client_mail_option(&self) -> Option<&str> {
        match *self {
            EightBitMime => Some("BODY=8BITMIME"),
            _ => None,
        }
    }
}

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

    #[test]
    fn test_fmt() {
        assert_eq!(format!("{}", Extension::EightBitMime), "8BITMIME".to_string());
        assert_eq!(format!("{}", Extension::Size(42)), "SIZE=42".to_string());
    }

    #[test]
    fn test_from_str() {
        assert_eq!("8BITMIME".parse::<Extension>(), Ok(Extension::EightBitMime));
        assert_eq!("SIZE 42".parse::<Extension>(), Ok(Extension::Size(42)));
        assert!("SIZ 42".parse::<Extension>().is_err());
        assert!("SIZE 4a2".parse::<Extension>().is_err());
        // TODO: accept trailing spaces ?
        assert!("SIZE 42 ".parse::<Extension>().is_err());
    }

    #[test]
    fn test_same_extension_as() {
        assert_eq!(Extension::EightBitMime.same_extension_as(&Extension::EightBitMime), true);
        assert_eq!(Extension::Size(42).same_extension_as(&Extension::Size(42)), true);
        assert_eq!(Extension::Size(42).same_extension_as(&Extension::Size(43)), true);
        assert_eq!(Extension::Size(42).same_extension_as(&Extension::EightBitMime), false);
        assert_eq!(Extension::EightBitMime.same_extension_as(&Extension::SmtpUtfEight), false);
    }

    #[test]
    fn test_parse_esmtp_response() {
        assert_eq!(Extension::parse_esmtp_response("me\r\n250-8BITMIME\r\n250 SIZE 42"),
            vec!(Extension::EightBitMime, Extension::Size(42)));
        assert_eq!(Extension::parse_esmtp_response("me\r\n250-8BITMIME\r\n250 UNKNON 42"),
            vec!(Extension::EightBitMime));
        assert_eq!(Extension::parse_esmtp_response("me\r\n250-9BITMIME\r\n250 SIZE a"),
            vec!());
        assert_eq!(Extension::parse_esmtp_response("me\r\n250-SIZE 42\r\n250 SIZE 43"),
            vec!(Extension::Size(42), Extension::Size(43)));
        assert_eq!(Extension::parse_esmtp_response(""),
            vec!());
    }
}
<|endoftext|>


<commit_before>use correlation::Correlator;
use correlation::message::MessageBuilder;
use correlation::test_utils::correlator::MessageEventHandler;

use env_logger;

use std::cell::RefCell;
use std::rc::Rc;

#[test]
fn test_given_correlator_when_messages_are_received_then_they_are_grouped_into_a_context_by_a_context_id() {
    let _ = env_logger::init();
    let contexts_file = "tests/correlator/contexts.json";
    let mut correlator = Correlator::from_path(contexts_file).ok().expect("Failed to load contexts from a valid contexts_file");
    let login_message = MessageBuilder::new("6d2cba0c-e241-464a-89c3-8035cac8f73e", "message")
                                        .name(Some("LOGIN"))
                                        .pair("user_name", "linus")
                                        .build();
    let read_message = MessageBuilder::new("60dd1233-5fa6-4e3b-993f-e04ef9b4c164", "message")
                                        .name(Some("MAIL_READ"))
                                        .pair("user_name", "linus")
                                        .build();
    let logout_message = MessageBuilder::new("91ea534a-4880-4853-aec2-7b2a2df9a8c9", "message")
                                        .name(Some("LOGOUT"))
                                        .pair("user_name", "linus")
                                        .build();
    let responses = Rc::new(RefCell::new(Vec::new()));
    let message_event_handler = Box::new(MessageEventHandler{responses: responses.clone()});
    correlator.register_handler(message_event_handler);
    let _ = correlator.push_message(login_message);
    let _ = correlator.push_message(read_message);
    let _ = correlator.push_message(logout_message);
    correlator.handle_events();
    correlator.stop();
    assert_eq!(1, responses.borrow().len());
}
<commit_msg>tests: ignore return value of Correlator.stop()<commit_after>use correlation::Correlator;
use correlation::message::MessageBuilder;
use correlation::test_utils::correlator::MessageEventHandler;

use env_logger;

use std::cell::RefCell;
use std::rc::Rc;

#[test]
fn test_given_correlator_when_messages_are_received_then_they_are_grouped_into_a_context_by_a_context_id() {
    let _ = env_logger::init();
    let contexts_file = "tests/correlator/contexts.json";
    let mut correlator = Correlator::from_path(contexts_file).ok().expect("Failed to load contexts from a valid contexts_file");
    let login_message = MessageBuilder::new("6d2cba0c-e241-464a-89c3-8035cac8f73e", "message")
                                        .name(Some("LOGIN"))
                                        .pair("user_name", "linus")
                                        .build();
    let read_message = MessageBuilder::new("60dd1233-5fa6-4e3b-993f-e04ef9b4c164", "message")
                                        .name(Some("MAIL_READ"))
                                        .pair("user_name", "linus")
                                        .build();
    let logout_message = MessageBuilder::new("91ea534a-4880-4853-aec2-7b2a2df9a8c9", "message")
                                        .name(Some("LOGOUT"))
                                        .pair("user_name", "linus")
                                        .build();
    let responses = Rc::new(RefCell::new(Vec::new()));
    let message_event_handler = Box::new(MessageEventHandler{responses: responses.clone()});
    correlator.register_handler(message_event_handler);
    let _ = correlator.push_message(login_message);
    let _ = correlator.push_message(read_message);
    let _ = correlator.push_message(logout_message);
    correlator.handle_events();
    let _ = correlator.stop();
    assert_eq!(1, responses.borrow().len());
}
<|endoftext|>


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

use std::path::Path;
use std::time::Duration;

use hab_core::env;
use hab_core::util::sys;
use hyper::client::pool::{Config, Pool};
use hyper::client::{Client as HyperClient, IntoUrl, RequestBuilder};
use hyper::header::UserAgent;
use hyper::http::h1::Http11Protocol;
use hyper::net::HttpsConnector;
use hyper_openssl::OpensslClient;
use openssl::ssl::{
    SSL_OP_NO_SSLV2, SSL_OP_NO_SSLV3, SslConnector, SslConnectorBuilder, SslMethod, SslOption,
    SSL_OP_NO_COMPRESSION, SSL_VERIFY_NONE,
};
use url::Url;

use error::{Error, Result};
use net::ProxyHttpsConnector;
use proxy::{proxy_unless_domain_exempted, ProxyInfo};
use ssl;

// Read and write TCP socket timeout for Hyper/HTTP client calls.
const CLIENT_SOCKET_RW_TIMEOUT_SEC: u64 = 120;

header! { (ProxyAuthorization, "Proxy-Authorization") => [String] }

/// A generic wrapper around a Hyper HTTP client intended for API-like usage.
///
/// When an `ApiClient` is created, it has a constant URL base which is assumed to be some API
/// endpoint. This allows the underlying Hyper client to load and use any relevant HTTP proxy
/// support and to provide reasonable User-Agent HTTP headers, etc.
#[derive(Debug)]
pub struct ApiClient {
    /// The base URL for the client.
    endpoint: Url,
    /// An instance of a `hyper::Client` which is configured with an SSL context and optionally
    /// using an HTTP proxy.
    inner: HyperClient,
    /// Proxy information, if a proxy is being used.
    proxy: Option<ProxyInfo>,
    /// The URL scheme of the endpoint.
    target_scheme: String,
    /// The `User-Agent` header string to use for HTTP calls.
    user_agent_header: UserAgent,
}

impl ApiClient {
    /// Creates and returns a new `ApiClient` instance.
    ///
    /// # Errors
    ///
    /// * If the underlying Hyper client cannot be created
    /// * If a suitable SSL context cannot be established
    /// * If an HTTP proxy cannot be correctly setup
    /// * If a `User-Agent` HTTP header string cannot be constructed
    pub fn new<T>(
        endpoint: T,
        product: &str,
        version: &str,
        fs_root_path: Option<&Path>,
    ) -> Result<Self>
    where
        T: IntoUrl,
    {
        let endpoint = endpoint.into_url().map_err(Error::UrlParseError)?;
        Ok(ApiClient {
            inner: new_hyper_client(&endpoint, fs_root_path)?,
            proxy: proxy_unless_domain_exempted(Some(&endpoint))?,
            target_scheme: endpoint.scheme().to_string(),
            endpoint: endpoint,
            user_agent_header: user_agent(product, version)?,
        })
    }

    /// Builds an HTTP GET request for a given path.
    pub fn get(&self, path: &str) -> RequestBuilder {
        self.get_with_custom_url(path, |_| {})
    }

    /// Builds an HTTP GET request for a given path with the ability to customize the target URL.
    pub fn get_with_custom_url<F>(&self, path: &str, mut customize_url: F) -> RequestBuilder
    where
        F: FnMut(&mut Url),
    {
        let mut url = self.url_for(path);
        customize_url(&mut url);
        debug!("GET {} with {:?}", &url, &self);
        self.add_headers(self.inner.get(url))
    }

    /// Builds an HTTP HEAD request for a given path.
    pub fn head(&self, path: &str) -> RequestBuilder {
        self.head_with_custom_url(path, |_| {})
    }

    /// Builds an HTTP HEAD request for a given path with the ability to customize the target URL.
    pub fn head_with_custom_url<F>(&self, path: &str, mut customize_url: F) -> RequestBuilder
    where
        F: FnMut(&mut Url),
    {
        let mut url = self.url_for(path);
        customize_url(&mut url);
        debug!("HEAD {} with {:?}", &url, &self);
        self.add_headers(self.inner.head(url))
    }

    /// Builds an HTTP PATCH request for a given path.
    pub fn patch(&self, path: &str) -> RequestBuilder {
        self.patch_with_custom_url(path, |_| {})
    }

    /// Builds an HTTP PATCH request for a given path with the ability to customize the target URL.
    pub fn patch_with_custom_url<F>(&self, path: &str, mut customize_url: F) -> RequestBuilder
    where
        F: FnMut(&mut Url),
    {
        let mut url = self.url_for(path);
        customize_url(&mut url);
        debug!("PATH {} with {:?}", &url, &self);
        self.add_headers(self.inner.patch(url))
    }

    /// Builds an HTTP POST request for a given path.
    pub fn post(&self, path: &str) -> RequestBuilder {
        self.post_with_custom_url(path, |_| {})
    }

    /// Builds an HTTP POST request for a given path with the ability to customize the target URL.
    pub fn post_with_custom_url<F>(&self, path: &str, mut customize_url: F) -> RequestBuilder
    where
        F: FnMut(&mut Url),
    {
        let mut url = self.url_for(path);
        customize_url(&mut url);
        debug!("POST {} with {:?}", &url, &self);
        self.add_headers(self.inner.post(url))
    }

    /// Builds an HTTP PUT request for a given path.
    pub fn put(&self, path: &str) -> RequestBuilder {
        self.put_with_custom_url(path, |_| {})
    }

    /// Builds an HTTP PUT request for a given path with the ability to customize the target URL.
    pub fn put_with_custom_url<F>(&self, path: &str, mut customize_url: F) -> RequestBuilder
    where
        F: FnMut(&mut Url),
    {
        let mut url = self.url_for(path);
        customize_url(&mut url);
        debug!("PUT {} with {:?}", &url, &self);
        self.add_headers(self.inner.put(url))
    }

    /// Builds an HTTP DELETE request for a given path.
    pub fn delete(&self, path: &str) -> RequestBuilder {
        self.delete_with_custom_url(path, |_| {})
    }

    /// Builds an HTTP DELETE request for a given path with the ability to customize the target URL.
    pub fn delete_with_custom_url<F>(&self, path: &str, mut customize_url: F) -> RequestBuilder
    where
        F: FnMut(&mut Url),
    {
        let mut url = self.url_for(path);
        customize_url(&mut url);
        debug!("DELETE {} with {:?}", &url, &self);
        self.add_headers(self.inner.delete(url))
    }

    fn add_headers<'a>(&'a self, rb: RequestBuilder<'a>) -> RequestBuilder {
        let mut rb = rb.header(self.user_agent_header.clone());
        // If the target URL is an `"http"` scheme and we're using a proxy server, then add the
        // proxy authorization header if appropriate. Note that for `"https"` targets, the proxy
        // server will be operating in TCP tunneling mode and will be authenticated on connection to
        // the proxy server which is why we should not add an additional header in this latter
        // case.
        if self.target_scheme == "http" {
            if let Some(ref info) = self.proxy {
                if let Some(header_value) = info.authorization_header_value() {
                    rb = rb.header(ProxyAuthorization(header_value));
                }
            }
        }
        rb
    }

    fn url_for(&self, path: &str) -> Url {
        let mut url = self.endpoint.clone();

        if path.is_empty() {
            return url;
        }

        if url.path().ends_with("/") || path.starts_with("/") {
            url.set_path(&format!("{}{}", self.endpoint.path(), path));
        } else {
            url.set_path(&format!("{}/{}", self.endpoint.path(), path));
        }

        url
    }
}

/// Builds a new hyper HTTP client with appropriate SSL configuration and HTTP/HTTPS proxy support.
///
/// ## Linux Platforms
///
/// We need a set of root certificates when connected to SSL/TLS web endpoints and this usually
/// boiled down to using an all-in-one certificate file (such as a `cacert.pem` file) or a directory
/// of files which are certificates. The strategy to location or use a reasonable set of
/// certificates is as follows:
///
/// 1. If the `SSL_CERT_FILE` environment variable is set, then use its value for the certificates.
///    Internally this is triggering default OpenSSL behavior for this environment variable.
/// 2. If the `SSL_CERT_DIR` environment variable is set, then use its value for the directory
///    containing certificates. Like the `SSL_CERT_FILE` case above, this triggers default OpenSSL
///    behavior for this environment variable.
/// 3. If the `core/cacerts` Habitat package is installed locally, then use the latest release's
///    `cacert.pem` file.
/// 4. If none of the conditions above are met, then a `cacert.pem` will be written in an SSL cache
///    directory (by default `/hab/cache/ssl` for a root user and `$HOME/.hab/cache/ssl` for a
///    non-root user) and this will be used. The contents of this file will be inlined in this
///    crate at build time as a fallback insurance policy, meaning that if the a program using this
///    code is operating in a minimal environment which may not contain system certificates, it can
///    still operate. Once a `core/cacerts` Habitat package is present, the logic would fall back
///    to preferring the package version to the cached/inline file version.
///
/// ## Mac Platforms
///
/// The Mac platform uses a Security Framework to store and find root certificates and the hyper
/// library will default to using this on the Mac. Therefore the behavior on the Mac remains
/// unchanged and will use the system's certificates.
///
fn new_hyper_client(url: &Url, fs_root_path: Option<&Path>) -> Result<HyperClient> {
    let connector = ssl_connector(fs_root_path)?;
    let ssl_client = OpensslClient::from(connector);

    let timeout_in_secs = match env::var("HAB_CLIENT_SOCKET_TIMEOUT") {
        Ok(t) => match t.parse::<u64>() {
            Ok(n) => n,
            Err(_) => CLIENT_SOCKET_RW_TIMEOUT_SEC,
        },
        Err(_) => CLIENT_SOCKET_RW_TIMEOUT_SEC,
    };
    debug!("Client socket timeout: {} secs", timeout_in_secs);

    let timeout = Some(Duration::from_secs(timeout_in_secs));

    match proxy_unless_domain_exempted(Some(url))? {
        Some(proxy) => {
            debug!("Using proxy {}:{}...", proxy.host(), proxy.port());
            let connector = ProxyHttpsConnector::new(proxy, ssl_client)?;
            let pool = Pool::with_connector(Config::default(), connector);
            let mut client = HyperClient::with_protocol(Http11Protocol::with_connector(pool));
            client.set_read_timeout(timeout);
            client.set_write_timeout(timeout);
            Ok(client)
        }
        None => {
            let connector = HttpsConnector::new(ssl_client);
            let pool = Pool::with_connector(Config::default(), connector);
            let mut client = HyperClient::with_protocol(Http11Protocol::with_connector(pool));
            client.set_read_timeout(timeout);
            client.set_write_timeout(timeout);
            Ok(client)
        }
    }
}

/// Returns an HTTP User-Agent string type for use by Hyper when making HTTP requests.
///
/// The general form for Habitat-related clients are of the following form:
///
/// ```text
/// <PRODUCT>/<VERSION> (<TARGET>; <KERNEL_RELEASE>)
/// ```
///
/// where:
///
/// * `<PRODUCT>`: is the provided product name
/// * `<VERSION>`: is the provided version string which may also include a release number
/// * `<TARGET>`: is the machine architecture and the kernel separated by a dash in lower case
/// * `<KERNEL_RELEASE>`: is the kernel release string from `uname`
///
/// For example:
///
/// ```text
/// hab/0.6.0/20160606153031 (x86_64-darwin; 14.5.0)
/// ```
///
/// # Errors
///
/// * If system information cannot be obtained via `uname`
fn user_agent(product: &str, version: &str) -> Result<UserAgent> {
    let uname = sys::uname()?;
    let ua = format!(
        "{}/{} ({}-{}; {})",
        product.trim(),
        version.trim(),
        uname.machine.trim().to_lowercase(),
        uname.sys_name.trim().to_lowercase(),
        uname.release.trim().to_lowercase()
    );
    debug!("User-Agent: {}", &ua);
    Ok(UserAgent(ua))
}

fn ssl_connector(fs_root_path: Option<&Path>) -> Result<SslConnector> {
    let mut conn = SslConnectorBuilder::new(SslMethod::tls())?;
    let mut options = SslOption::empty();
    options.toggle(SSL_OP_NO_SSLV2);
    options.toggle(SSL_OP_NO_SSLV3);
    options.toggle(SSL_OP_NO_COMPRESSION);
    ssl::set_ca(&mut conn, fs_root_path)?;
    conn.set_options(options);
    conn.set_cipher_list("ALL!EXPORT!EXPORT40!EXPORT56!aNULL!LOW!RC4@STRENGTH")?;

    if env::var("HAB_SSL_CERT_VERIFY_NONE").is_ok() {
        conn.set_verify(SSL_VERIFY_NONE);
    }

    Ok(conn.build())
}
<commit_msg>[api-client] Use `PackageTarget::active_target()` in user agent string.<commit_after>// Copyright (c) 2016-2017 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::path::Path;
use std::time::Duration;

use hab_core::env;
use hab_core::package::PackageTarget;
use hab_core::util::sys;
use hyper::client::pool::{Config, Pool};
use hyper::client::{Client as HyperClient, IntoUrl, RequestBuilder};
use hyper::header::UserAgent;
use hyper::http::h1::Http11Protocol;
use hyper::net::HttpsConnector;
use hyper_openssl::OpensslClient;
use openssl::ssl::{
    SSL_OP_NO_SSLV2, SSL_OP_NO_SSLV3, SslConnector, SslConnectorBuilder, SslMethod, SslOption,
    SSL_OP_NO_COMPRESSION, SSL_VERIFY_NONE,
};
use url::Url;

use error::{Error, Result};
use net::ProxyHttpsConnector;
use proxy::{proxy_unless_domain_exempted, ProxyInfo};
use ssl;

// Read and write TCP socket timeout for Hyper/HTTP client calls.
const CLIENT_SOCKET_RW_TIMEOUT_SEC: u64 = 120;

header! { (ProxyAuthorization, "Proxy-Authorization") => [String] }

/// A generic wrapper around a Hyper HTTP client intended for API-like usage.
///
/// When an `ApiClient` is created, it has a constant URL base which is assumed to be some API
/// endpoint. This allows the underlying Hyper client to load and use any relevant HTTP proxy
/// support and to provide reasonable User-Agent HTTP headers, etc.
#[derive(Debug)]
pub struct ApiClient {
    /// The base URL for the client.
    endpoint: Url,
    /// An instance of a `hyper::Client` which is configured with an SSL context and optionally
    /// using an HTTP proxy.
    inner: HyperClient,
    /// Proxy information, if a proxy is being used.
    proxy: Option<ProxyInfo>,
    /// The URL scheme of the endpoint.
    target_scheme: String,
    /// The `User-Agent` header string to use for HTTP calls.
    user_agent_header: UserAgent,
}

impl ApiClient {
    /// Creates and returns a new `ApiClient` instance.
    ///
    /// # Errors
    ///
    /// * If the underlying Hyper client cannot be created
    /// * If a suitable SSL context cannot be established
    /// * If an HTTP proxy cannot be correctly setup
    /// * If a `User-Agent` HTTP header string cannot be constructed
    pub fn new<T>(
        endpoint: T,
        product: &str,
        version: &str,
        fs_root_path: Option<&Path>,
    ) -> Result<Self>
    where
        T: IntoUrl,
    {
        let endpoint = endpoint.into_url().map_err(Error::UrlParseError)?;
        Ok(ApiClient {
            inner: new_hyper_client(&endpoint, fs_root_path)?,
            proxy: proxy_unless_domain_exempted(Some(&endpoint))?,
            target_scheme: endpoint.scheme().to_string(),
            endpoint: endpoint,
            user_agent_header: user_agent(product, version)?,
        })
    }

    /// Builds an HTTP GET request for a given path.
    pub fn get(&self, path: &str) -> RequestBuilder {
        self.get_with_custom_url(path, |_| {})
    }

    /// Builds an HTTP GET request for a given path with the ability to customize the target URL.
    pub fn get_with_custom_url<F>(&self, path: &str, mut customize_url: F) -> RequestBuilder
    where
        F: FnMut(&mut Url),
    {
        let mut url = self.url_for(path);
        customize_url(&mut url);
        debug!("GET {} with {:?}", &url, &self);
        self.add_headers(self.inner.get(url))
    }

    /// Builds an HTTP HEAD request for a given path.
    pub fn head(&self, path: &str) -> RequestBuilder {
        self.head_with_custom_url(path, |_| {})
    }

    /// Builds an HTTP HEAD request for a given path with the ability to customize the target URL.
    pub fn head_with_custom_url<F>(&self, path: &str, mut customize_url: F) -> RequestBuilder
    where
        F: FnMut(&mut Url),
    {
        let mut url = self.url_for(path);
        customize_url(&mut url);
        debug!("HEAD {} with {:?}", &url, &self);
        self.add_headers(self.inner.head(url))
    }

    /// Builds an HTTP PATCH request for a given path.
    pub fn patch(&self, path: &str) -> RequestBuilder {
        self.patch_with_custom_url(path, |_| {})
    }

    /// Builds an HTTP PATCH request for a given path with the ability to customize the target URL.
    pub fn patch_with_custom_url<F>(&self, path: &str, mut customize_url: F) -> RequestBuilder
    where
        F: FnMut(&mut Url),
    {
        let mut url = self.url_for(path);
        customize_url(&mut url);
        debug!("PATH {} with {:?}", &url, &self);
        self.add_headers(self.inner.patch(url))
    }

    /// Builds an HTTP POST request for a given path.
    pub fn post(&self, path: &str) -> RequestBuilder {
        self.post_with_custom_url(path, |_| {})
    }

    /// Builds an HTTP POST request for a given path with the ability to customize the target URL.
    pub fn post_with_custom_url<F>(&self, path: &str, mut customize_url: F) -> RequestBuilder
    where
        F: FnMut(&mut Url),
    {
        let mut url = self.url_for(path);
        customize_url(&mut url);
        debug!("POST {} with {:?}", &url, &self);
        self.add_headers(self.inner.post(url))
    }

    /// Builds an HTTP PUT request for a given path.
    pub fn put(&self, path: &str) -> RequestBuilder {
        self.put_with_custom_url(path, |_| {})
    }

    /// Builds an HTTP PUT request for a given path with the ability to customize the target URL.
    pub fn put_with_custom_url<F>(&self, path: &str, mut customize_url: F) -> RequestBuilder
    where
        F: FnMut(&mut Url),
    {
        let mut url = self.url_for(path);
        customize_url(&mut url);
        debug!("PUT {} with {:?}", &url, &self);
        self.add_headers(self.inner.put(url))
    }

    /// Builds an HTTP DELETE request for a given path.
    pub fn delete(&self, path: &str) -> RequestBuilder {
        self.delete_with_custom_url(path, |_| {})
    }

    /// Builds an HTTP DELETE request for a given path with the ability to customize the target URL.
    pub fn delete_with_custom_url<F>(&self, path: &str, mut customize_url: F) -> RequestBuilder
    where
        F: FnMut(&mut Url),
    {
        let mut url = self.url_for(path);
        customize_url(&mut url);
        debug!("DELETE {} with {:?}", &url, &self);
        self.add_headers(self.inner.delete(url))
    }

    fn add_headers<'a>(&'a self, rb: RequestBuilder<'a>) -> RequestBuilder {
        let mut rb = rb.header(self.user_agent_header.clone());
        // If the target URL is an `"http"` scheme and we're using a proxy server, then add the
        // proxy authorization header if appropriate. Note that for `"https"` targets, the proxy
        // server will be operating in TCP tunneling mode and will be authenticated on connection to
        // the proxy server which is why we should not add an additional header in this latter
        // case.
        if self.target_scheme == "http" {
            if let Some(ref info) = self.proxy {
                if let Some(header_value) = info.authorization_header_value() {
                    rb = rb.header(ProxyAuthorization(header_value));
                }
            }
        }
        rb
    }

    fn url_for(&self, path: &str) -> Url {
        let mut url = self.endpoint.clone();

        if path.is_empty() {
            return url;
        }

        if url.path().ends_with("/") || path.starts_with("/") {
            url.set_path(&format!("{}{}", self.endpoint.path(), path));
        } else {
            url.set_path(&format!("{}/{}", self.endpoint.path(), path));
        }

        url
    }
}

/// Builds a new hyper HTTP client with appropriate SSL configuration and HTTP/HTTPS proxy support.
///
/// ## Linux Platforms
///
/// We need a set of root certificates when connected to SSL/TLS web endpoints and this usually
/// boiled down to using an all-in-one certificate file (such as a `cacert.pem` file) or a directory
/// of files which are certificates. The strategy to location or use a reasonable set of
/// certificates is as follows:
///
/// 1. If the `SSL_CERT_FILE` environment variable is set, then use its value for the certificates.
///    Internally this is triggering default OpenSSL behavior for this environment variable.
/// 2. If the `SSL_CERT_DIR` environment variable is set, then use its value for the directory
///    containing certificates. Like the `SSL_CERT_FILE` case above, this triggers default OpenSSL
///    behavior for this environment variable.
/// 3. If the `core/cacerts` Habitat package is installed locally, then use the latest release's
///    `cacert.pem` file.
/// 4. If none of the conditions above are met, then a `cacert.pem` will be written in an SSL cache
///    directory (by default `/hab/cache/ssl` for a root user and `$HOME/.hab/cache/ssl` for a
///    non-root user) and this will be used. The contents of this file will be inlined in this
///    crate at build time as a fallback insurance policy, meaning that if the a program using this
///    code is operating in a minimal environment which may not contain system certificates, it can
///    still operate. Once a `core/cacerts` Habitat package is present, the logic would fall back
///    to preferring the package version to the cached/inline file version.
///
/// ## Mac Platforms
///
/// The Mac platform uses a Security Framework to store and find root certificates and the hyper
/// library will default to using this on the Mac. Therefore the behavior on the Mac remains
/// unchanged and will use the system's certificates.
///
fn new_hyper_client(url: &Url, fs_root_path: Option<&Path>) -> Result<HyperClient> {
    let connector = ssl_connector(fs_root_path)?;
    let ssl_client = OpensslClient::from(connector);

    let timeout_in_secs = match env::var("HAB_CLIENT_SOCKET_TIMEOUT") {
        Ok(t) => match t.parse::<u64>() {
            Ok(n) => n,
            Err(_) => CLIENT_SOCKET_RW_TIMEOUT_SEC,
        },
        Err(_) => CLIENT_SOCKET_RW_TIMEOUT_SEC,
    };
    debug!("Client socket timeout: {} secs", timeout_in_secs);

    let timeout = Some(Duration::from_secs(timeout_in_secs));

    match proxy_unless_domain_exempted(Some(url))? {
        Some(proxy) => {
            debug!("Using proxy {}:{}...", proxy.host(), proxy.port());
            let connector = ProxyHttpsConnector::new(proxy, ssl_client)?;
            let pool = Pool::with_connector(Config::default(), connector);
            let mut client = HyperClient::with_protocol(Http11Protocol::with_connector(pool));
            client.set_read_timeout(timeout);
            client.set_write_timeout(timeout);
            Ok(client)
        }
        None => {
            let connector = HttpsConnector::new(ssl_client);
            let pool = Pool::with_connector(Config::default(), connector);
            let mut client = HyperClient::with_protocol(Http11Protocol::with_connector(pool));
            client.set_read_timeout(timeout);
            client.set_write_timeout(timeout);
            Ok(client)
        }
    }
}

/// Returns an HTTP User-Agent string type for use by Hyper when making HTTP requests.
///
/// The general form for Habitat-related clients are of the following form:
///
/// ```text
/// <PRODUCT>/<VERSION> (<TARGET>; <KERNEL_RELEASE>)
/// ```
///
/// where:
///
/// * `<PRODUCT>`: is the provided product name
/// * `<VERSION>`: is the provided version string which may also include a release number
/// * `<TARGET>`: is the machine architecture and the kernel separated by a dash in lower case
/// * `<KERNEL_RELEASE>`: is the kernel release string from `uname`
///
/// For example:
///
/// ```text
/// hab/0.6.0/20160606153031 (x86_64-darwin; 14.5.0)
/// ```
///
/// # Errors
///
/// * If system information cannot be obtained via `uname`
fn user_agent(product: &str, version: &str) -> Result<UserAgent> {
    let uname = sys::uname()?;
    let ua = format!(
        "{}/{} ({}; {})",
        product.trim(),
        version.trim(),
        PackageTarget::active_target(),
        uname.release.trim().to_lowercase()
    );
    debug!("User-Agent: {}", &ua);
    Ok(UserAgent(ua))
}

fn ssl_connector(fs_root_path: Option<&Path>) -> Result<SslConnector> {
    let mut conn = SslConnectorBuilder::new(SslMethod::tls())?;
    let mut options = SslOption::empty();
    options.toggle(SSL_OP_NO_SSLV2);
    options.toggle(SSL_OP_NO_SSLV3);
    options.toggle(SSL_OP_NO_COMPRESSION);
    ssl::set_ca(&mut conn, fs_root_path)?;
    conn.set_options(options);
    conn.set_cipher_list("ALL!EXPORT!EXPORT40!EXPORT56!aNULL!LOW!RC4@STRENGTH")?;

    if env::var("HAB_SSL_CERT_VERIFY_NONE").is_ok() {
        conn.set_verify(SSL_VERIFY_NONE);
    }

    Ok(conn.build())
}
<|endoftext|>


<commit_before>
#![crate_name = "geoip"]
#![crate_type = "rlib"]

#![warn(non_camel_case_types,
        non_upper_case_globals,
        unused_qualifications)]
#![feature(libc, path, std_misc, io)]

extern crate libc;
extern crate "rustc-serialize" as rustc_serialize;
extern crate "geoip-sys" as geoip_sys;

use libc::{c_char, c_int, c_ulong};
use std::ffi;
use std::fmt;
use std::old_io::net::ip::{IpAddr,Ipv4Addr,Ipv6Addr};

#[cfg(test)]
use std::str::FromStr;

enum Charset {
    UTF8 = 1
}

pub enum Options {
    Standard = 0,
    MemoryCache = 1,
    CheckCache = 2,
    IndexCache = 4,
    MmapCache = 8
}

impl Copy for Options { }

pub enum DBType {
    CountryEdition = 1,
    RegionEditionRev0 = 7,
    CityEditionRev0 = 6,
    ORGEdition = 5,
    ISPEdition = 4,
    CityEditionRev1 = 2,
    RegionEditionRev1 = 3,
    ProxyEdition = 8,
    ASNUMEdition = 9,
    NetSpeedEdition = 10,
    DomainEdition = 11,
    CountryEditionV6 = 12,
    LocationAEdition = 13,
    AccuracyRadiusEdition = 14,
    LargeCountryEdition = 17,
    LargeCountryEditionV6 = 18,
    ASNumEditionV6 = 21,
    ISPEditionV6 = 22,
    ORGEditionV6 = 23,
    DomainEditionV6 = 24,
    LoctionAEditionV6 = 25,
    RegistrarEdition = 26,
    RegistrarEditionV6 = 27,
    UserTypeEdition = 28,
    UserTypeEditionV6 = 29,
    CityEditionRev1V6 = 30,
    CityEditionRev0V6 = 31,
    NetSpeedEditionRev1 = 32,
    NetSpeedEditionRev1V6 = 33,
    CountryConfEdition = 34,
    CityConfEdition = 35,
    RegionConfEdition = 36,
    PostalConfEdition = 37,
    AccuracyRadiusEditionV6 = 38
}

impl Copy for DBType { }

pub struct GeoIp {
    db: geoip_sys::RawGeoIp
}

#[derive(RustcDecodable, RustcEncodable)]
pub struct ASInfo {
    pub asn: u32,
    pub name: String,
    pub netmask: u32
}

#[derive(RustcDecodable, RustcEncodable)]
pub struct CityInfo {
    pub country_code: Option<String>,
    pub country_code3: Option<String>,
    pub country_name: Option<String>,
    pub region: Option<String>,
    pub city: Option<String>,
    pub postal_code: Option<String>,
    pub latitude: f32,
    pub longitude: f32,
    pub dma_code: u32,
    pub area_code: u32,
    pub charset: u32,
    pub continent_code: Option<String>,
    pub netmask: u32
}

fn maybe_string(c_str: *const c_char) -> Option<String> {
    if c_str.is_null() {
        None
    } else {
        String::from_utf8(unsafe { ffi::c_str_to_bytes(&c_str) }.to_vec()).ok()
    }
}

impl CityInfo {
    unsafe fn from_geoiprecord(res: &geoip_sys::GeoIpRecord) -> CityInfo {
        CityInfo {
            country_code: maybe_string(res.country_code),
            country_code3: maybe_string(res.country_code3),
            country_name: maybe_string(res.country_name),
            region: maybe_string(res.region),
            city: maybe_string(res.city),
            postal_code: maybe_string(res.postal_code),
            latitude: res.latitude as f32,
            longitude: res.longitude as f32,
            dma_code: res.dma_code as u32,
            area_code: res.area_code as u32,
            charset: res.charset as u32,
            continent_code: maybe_string(res.continent_code),
            netmask: res.netmask as u32
        }
    }
}

impl fmt::Debug for ASInfo {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}\t{}", self.asn, self.name)
    }
}

enum CNetworkIp {
    V4(c_ulong),
    V6(geoip_sys::In6Addr)
}

impl CNetworkIp {
    fn new(ip: IpAddr) -> CNetworkIp {
        match ip {
            Ipv4Addr(a, b, c, d) => {
                CNetworkIp::V4(((a as c_ulong) << 24) | ((b as c_ulong) << 16) |
                               ((c as c_ulong) << 8)  | ((d as c_ulong)))
            },
            Ipv6Addr(a, b, c, d, e, f, g, h) => {
                CNetworkIp::V6([(a >> 8) as u8, a as u8,
                                (b >> 8) as u8, b as u8,
                                (c >> 8) as u8, c as u8,
                                (d >> 8) as u8, d as u8,
                                (e >> 8) as u8, e as u8,
                                (f >> 8) as u8, f as u8,
                                (g >> 8) as u8, g as u8,
                                (h >> 8) as u8, h as u8])
            }
        }
    }
}

impl GeoIp {
    pub fn open(path: &Path, options: Options) -> Result<GeoIp, String> {
        let file = match path.as_str() {
            None => return Err(format!("Invalid path {}", path.display())),
            Some(file) => file
        };
        let db = unsafe {
            geoip_sys::GeoIP_open(ffi::CString::from_slice(file.as_bytes()).as_ptr(),
                                  options as c_int)
        };
        if db.is_null() {
            return Err(format!("Can't open {}", file));
        }
        if unsafe { geoip_sys::GeoIP_set_charset(db, Charset::UTF8 as c_int)
        } != 0 {
            return Err("Can't set charset to UTF8".to_string());
        }
        Ok(GeoIp { db: db })
    }

    pub fn city_info_by_ip(&self, ip: IpAddr) -> Option<CityInfo> {
        let cres = match CNetworkIp::new(ip) {
            CNetworkIp::V4(ip) => unsafe {
                geoip_sys::GeoIP_record_by_ipnum(self.db, ip) },
            CNetworkIp::V6(ip) => unsafe {
                geoip_sys::GeoIP_record_by_ipnum_v6(self.db, ip) }
        };

        if cres.is_null() { return None; }

        unsafe {
            let city_info = CityInfo::from_geoiprecord(&*cres);
            geoip_sys::GeoIPRecord_delete(cres);
            std::mem::forget(cres);
            Some(city_info)
        }
    }

    pub fn as_info_by_ip(&self, ip: IpAddr) -> Option<ASInfo> {
        let mut gl = geoip_sys::GeoIpLookup::new();
        let cres = match CNetworkIp::new(ip) {
            CNetworkIp::V4(ip) => unsafe {
                geoip_sys::GeoIP_name_by_ipnum_gl(self.db, ip, &mut gl) },
            CNetworkIp::V6(ip) => unsafe {
                geoip_sys::GeoIP_name_by_ipnum_v6_gl(self.db, ip, &mut gl) }
        };

        if cres.is_null() {
            return None;
        }
        let description = match maybe_string(cres) {
            None => return None,
            Some(description) => description
        };
        let mut di = description.splitn(1, ' ');
        let asn = match di.next() {
            None => return None,
            Some(asn) => {
                if ! asn.starts_with("AS") {
                    return None
                } else {
                    asn[2..].parse::<u32>().unwrap()
                }
            }
        };
        let name = di.next().unwrap_or("(none)");
        let as_info = ASInfo {
            asn: asn,
            name: name.to_string(),
            netmask: gl.netmask as u32
        };
        Some(as_info)
    }
}

impl Drop for GeoIp {
    fn drop(&mut self) {
        unsafe {
            geoip_sys::GeoIP_delete(self.db);
        }
    }
}

#[test]
fn geoip_test_basic() {
    let geoip = match GeoIp::open(&Path::new("/opt/geoip/GeoIPASNum.dat"), Options::MemoryCache) {
        Err(err) => panic!(err),
        Ok(geoip) => geoip
    };
    let ip = FromStr::from_str("91.203.184.192").unwrap();
    let res = geoip.as_info_by_ip(ip).unwrap();
    assert!(res.asn == 41064);
    assert!(res.name.contains("Telefun"));
    assert!(res.netmask == 22);
}

#[test]
fn geoip_test_city() {
    let geoip = match GeoIp::open(&Path::new("/opt/geoip/GeoLiteCity.dat"), Options::MemoryCache) {
        Err(err) => panic!(err),
        Ok(geoip) => geoip
    };
    let ip = FromStr::from_str("8.8.8.8").unwrap();
    let res = geoip.city_info_by_ip(ip).unwrap();
    assert!(res.city.unwrap() == "Mountain View");
}
<commit_msg>Switch to the new io<commit_after>
#![crate_name = "geoip"]
#![crate_type = "rlib"]

#![warn(non_camel_case_types,
        non_upper_case_globals,
        unused_qualifications)]
#![feature(libc, path, std_misc, net)]

extern crate libc;
extern crate "rustc-serialize" as rustc_serialize;
extern crate "geoip-sys" as geoip_sys;

use libc::{c_char, c_int, c_ulong};
use std::ffi;
use std::fmt;
use std::net::IpAddr;

#[cfg(test)]
use std::str::FromStr;

enum Charset {
    UTF8 = 1
}

pub enum Options {
    Standard = 0,
    MemoryCache = 1,
    CheckCache = 2,
    IndexCache = 4,
    MmapCache = 8
}

impl Copy for Options { }

pub enum DBType {
    CountryEdition = 1,
    RegionEditionRev0 = 7,
    CityEditionRev0 = 6,
    ORGEdition = 5,
    ISPEdition = 4,
    CityEditionRev1 = 2,
    RegionEditionRev1 = 3,
    ProxyEdition = 8,
    ASNUMEdition = 9,
    NetSpeedEdition = 10,
    DomainEdition = 11,
    CountryEditionV6 = 12,
    LocationAEdition = 13,
    AccuracyRadiusEdition = 14,
    LargeCountryEdition = 17,
    LargeCountryEditionV6 = 18,
    ASNumEditionV6 = 21,
    ISPEditionV6 = 22,
    ORGEditionV6 = 23,
    DomainEditionV6 = 24,
    LoctionAEditionV6 = 25,
    RegistrarEdition = 26,
    RegistrarEditionV6 = 27,
    UserTypeEdition = 28,
    UserTypeEditionV6 = 29,
    CityEditionRev1V6 = 30,
    CityEditionRev0V6 = 31,
    NetSpeedEditionRev1 = 32,
    NetSpeedEditionRev1V6 = 33,
    CountryConfEdition = 34,
    CityConfEdition = 35,
    RegionConfEdition = 36,
    PostalConfEdition = 37,
    AccuracyRadiusEditionV6 = 38
}

impl Copy for DBType { }

pub struct GeoIp {
    db: geoip_sys::RawGeoIp
}

#[derive(RustcDecodable, RustcEncodable)]
pub struct ASInfo {
    pub asn: u32,
    pub name: String,
    pub netmask: u32
}

#[derive(RustcDecodable, RustcEncodable)]
pub struct CityInfo {
    pub country_code: Option<String>,
    pub country_code3: Option<String>,
    pub country_name: Option<String>,
    pub region: Option<String>,
    pub city: Option<String>,
    pub postal_code: Option<String>,
    pub latitude: f32,
    pub longitude: f32,
    pub dma_code: u32,
    pub area_code: u32,
    pub charset: u32,
    pub continent_code: Option<String>,
    pub netmask: u32
}

fn maybe_string(c_str: *const c_char) -> Option<String> {
    if c_str.is_null() {
        None
    } else {
        String::from_utf8(unsafe { ffi::c_str_to_bytes(&c_str) }.to_vec()).ok()
    }
}

impl CityInfo {
    unsafe fn from_geoiprecord(res: &geoip_sys::GeoIpRecord) -> CityInfo {
        CityInfo {
            country_code: maybe_string(res.country_code),
            country_code3: maybe_string(res.country_code3),
            country_name: maybe_string(res.country_name),
            region: maybe_string(res.region),
            city: maybe_string(res.city),
            postal_code: maybe_string(res.postal_code),
            latitude: res.latitude as f32,
            longitude: res.longitude as f32,
            dma_code: res.dma_code as u32,
            area_code: res.area_code as u32,
            charset: res.charset as u32,
            continent_code: maybe_string(res.continent_code),
            netmask: res.netmask as u32
        }
    }
}

impl fmt::Debug for ASInfo {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}\t{}", self.asn, self.name)
    }
}

enum CNetworkIp {
    V4(c_ulong),
    V6(geoip_sys::In6Addr)
}

impl CNetworkIp {
    fn new(ip: IpAddr) -> CNetworkIp {
        match ip {
            IpAddr::V4(addr) => {
                let b = addr.octets();
                CNetworkIp::V4(((b[0] as c_ulong) << 24) |
                               ((b[1] as c_ulong) << 16) |
                               ((b[2] as c_ulong) << 8)  |
                               ((b[3] as c_ulong)))
            },
            IpAddr::V6(addr) => {
                let b = addr.segments();
                CNetworkIp::V6([(b[0] >> 8) as u8, b[0] as u8,
                                (b[1] >> 8) as u8, b[1] as u8,
                                (b[2] >> 8) as u8, b[2] as u8,
                                (b[3] >> 8) as u8, b[3] as u8,
                                (b[4] >> 8) as u8, b[4] as u8,
                                (b[5] >> 8) as u8, b[5] as u8,
                                (b[6] >> 8) as u8, b[6] as u8,
                                (b[7] >> 8) as u8, b[7] as u8])
            }
        }
    }
}

impl GeoIp {
    pub fn open(path: &Path, options: Options) -> Result<GeoIp, String> {
        let file = match path.as_str() {
            None => return Err(format!("Invalid path {}", path.display())),
            Some(file) => file
        };
        let db = unsafe {
            geoip_sys::GeoIP_open(ffi::CString::from_slice(file.as_bytes()).as_ptr(),
                                  options as c_int)
        };
        if db.is_null() {
            return Err(format!("Can't open {}", file));
        }
        if unsafe { geoip_sys::GeoIP_set_charset(db, Charset::UTF8 as c_int)
        } != 0 {
            return Err("Can't set charset to UTF8".to_string());
        }
        Ok(GeoIp { db: db })
    }

    pub fn city_info_by_ip(&self, ip: IpAddr) -> Option<CityInfo> {
        let cres = match CNetworkIp::new(ip) {
            CNetworkIp::V4(ip) => unsafe {
                geoip_sys::GeoIP_record_by_ipnum(self.db, ip) },
            CNetworkIp::V6(ip) => unsafe {
                geoip_sys::GeoIP_record_by_ipnum_v6(self.db, ip) }
        };

        if cres.is_null() { return None; }

        unsafe {
            let city_info = CityInfo::from_geoiprecord(&*cres);
            geoip_sys::GeoIPRecord_delete(cres);
            std::mem::forget(cres);
            Some(city_info)
        }
    }

    pub fn as_info_by_ip(&self, ip: IpAddr) -> Option<ASInfo> {
        let mut gl = geoip_sys::GeoIpLookup::new();
        let cres = match CNetworkIp::new(ip) {
            CNetworkIp::V4(ip) => unsafe {
                geoip_sys::GeoIP_name_by_ipnum_gl(self.db, ip, &mut gl) },
            CNetworkIp::V6(ip) => unsafe {
                geoip_sys::GeoIP_name_by_ipnum_v6_gl(self.db, ip, &mut gl) }
        };

        if cres.is_null() {
            return None;
        }
        let description = match maybe_string(cres) {
            None => return None,
            Some(description) => description
        };
        let mut di = description.splitn(1, ' ');
        let asn = match di.next() {
            None => return None,
            Some(asn) => {
                if ! asn.starts_with("AS") {
                    return None
                } else {
                    asn[2..].parse::<u32>().unwrap()
                }
            }
        };
        let name = di.next().unwrap_or("(none)");
        let as_info = ASInfo {
            asn: asn,
            name: name.to_string(),
            netmask: gl.netmask as u32
        };
        Some(as_info)
    }
}

impl Drop for GeoIp {
    fn drop(&mut self) {
        unsafe {
            geoip_sys::GeoIP_delete(self.db);
        }
    }
}

#[test]
fn geoip_test_basic() {
    let geoip = match GeoIp::open(&Path::new("/opt/geoip/GeoIPASNum.dat"), Options::MemoryCache) {
        Err(err) => panic!(err),
        Ok(geoip) => geoip
    };
    let ip = FromStr::from_str("91.203.184.192").unwrap();
    let res = geoip.as_info_by_ip(ip).unwrap();
    assert!(res.asn == 41064);
    assert!(res.name.contains("Telefun"));
    assert!(res.netmask == 22);
}

#[test]
fn geoip_test_city() {
    let geoip = match GeoIp::open(&Path::new("/opt/geoip/GeoLiteCity.dat"), Options::MemoryCache) {
        Err(err) => panic!(err),
        Ok(geoip) => geoip
    };
    let ip = FromStr::from_str("8.8.8.8").unwrap();
    let res = geoip.city_info_by_ip(ip).unwrap();
    assert!(res.city.unwrap() == "Mountain View");
}
<|endoftext|>


<commit_before>use super::point::Point;
use super::number::NumberOps;

#[inline]
pub fn h_down_intersect(y: i64, p1: Point, p2: Point, points: &mut Vec<Point>) {
   if p2.y > y {
      if p1.y < y {
         points.push(Point::new(h_intersect(y, p1, p2), y));
      }
   } else {
      if p1.y > y && p2.y < y {
         points.push(Point::new(h_intersect(y, p1, p2), y));
      }
      points.push(Point::new(p2.x, p2.y));
   }
}

#[inline]
pub fn h_up_intersect(y: i64, p1: Point, p2: Point, points: &mut Vec<Point>) {
   if p2.y < y {
      if p1.y > y {
         points.push(Point::new(h_intersect(y, p1, p2), y));
      }
   } else {
      if p1.y < y && p2.y > y {
         points.push(Point::new(h_intersect(y, p1, p2), y));
      }
      points.push(Point::new(p2.x, p2.y));
   }
}

#[inline]
pub fn v_left_intersect(x: i64, p1: Point, p2: Point, points: &mut Vec<Point>) {
   if p2.x > x {
      if p1.x < x {
         points.push(Point::new(x, v_intersect(x, p1, p2)));
      }
   } else {
      if p1.x > x && p2.x < x {
         points.push(Point::new(x, v_intersect(x, p1, p2)));
      }
      points.push(Point::new(p2.x, p2.y));
   }
}

#[inline]
pub fn v_right_intersect(x: i64, p1: Point, p2: Point, points: &mut Vec<Point>) {
   if p2.x < x {
      if p1.x > x {
         points.push(Point::new(x, v_intersect(x, p1, p2)));
      }
   } else {
      if p1.x < x && p2.x > x {
         points.push(Point::new(x, v_intersect(x, p1, p2)));
      }
      points.push(Point::new(p2.x, p2.y));
   }
}

#[inline]
pub fn h_intersect(y: i64, p1: Point, p2: Point) -> i64 {
   p1.x + ((p2.x - p1.x) * (y - p1.y)).rounding_div(p2.y - p1.y)
}

#[inline]
pub fn v_intersect(x: i64, p1: Point, p2: Point) -> i64 {
   p1.y + ((p2.y - p1.y) * (x - p1.x)).rounding_div(p2.x - p1.x)
}

pub fn hv_clip<F>(intersect: F, y: i64, points: &mut Vec<Point>, start: usize) where
   F: Fn(i64, Point, Point, &mut Vec<Point>),
{
   let end = points.len();

   if end - start < 2 {
      return;
   }

   let mut p1 = points[end-1];

   for i in start..end {
      let p2 = points[i];
      intersect(y, p1, p2, points);

      p1 = p2;
   }
}

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

   use super::super::point::Point;

   use super::*;

   #[bench]
   fn bench_clip(b: &mut Bencher) {
      b.iter(|| {
         for _ in 0..1000 {
            let mut points = vec![
               Point::new(50, 50),
               Point::new(100, 200),
               Point::new(150, 100),
            ];
            points.reserve(30);

            let start = 0;
            let end = points.len();
            hv_clip(h_down_intersect, 150, &mut points, start);

            let start = end;
            let end = points.len();
            hv_clip(h_up_intersect, 80, &mut points, start);

            let start = end;
            let end = points.len();
            hv_clip(v_right_intersect, 70, &mut points, start);

            let start = end;
            hv_clip(v_left_intersect, 140, &mut points, start);
         }
      });
   }

   #[test]
   fn test_clip() {
      let mut points = vec![
         Point::new(50, 50),
         Point::new(100, 200),
         Point::new(150, 100),
      ];

      let start = 0;
      let end = points.len();
      hv_clip(h_down_intersect, 150, &mut points, start);

      let start = end;
      let end = points.len();
      hv_clip(h_up_intersect, 80, &mut points, start);

      let start = end;
      let end = points.len();
      hv_clip(v_right_intersect, 70, &mut points, start);

      let start = end;
      let end = points.len();
      hv_clip(v_left_intersect, 140, &mut points, start);

      assert_eq!(points.len(), 25);

      let (_, result) = points.split_at(end);
      assert_eq!(result, [
         Point::new(140, 95),
         Point::new(110, 80),
         Point::new(70, 80),
         Point::new(70, 110),
         Point::new(83, 150),
         Point::new(125, 150),
         Point::new(140, 120),
      ]);
   }
}
<commit_msg>Horizontal and vertical splitting of polygons<commit_after>use super::point::Point;
use super::number::NumberOps;

#[inline]
pub fn h_down_intersect(y: i64, p1: Point, p2: Point, points: &mut Vec<Point>) {
   if p2.y > y {
      if p1.y < y {
         points.push(Point::new(h_intersect(y, p1, p2), y));
      }
   } else {
      if p1.y > y && p2.y < y {
         points.push(Point::new(h_intersect(y, p1, p2), y));
      }
      points.push(p2);
   }
}

#[inline]
pub fn h_up_intersect(y: i64, p1: Point, p2: Point, points: &mut Vec<Point>) {
   if p2.y < y {
      if p1.y > y {
         points.push(Point::new(h_intersect(y, p1, p2), y));
      }
   } else {
      if p1.y < y && p2.y > y {
         points.push(Point::new(h_intersect(y, p1, p2), y));
      }
      points.push(p2);
   }
}

#[inline]
pub fn v_left_intersect(x: i64, p1: Point, p2: Point, points: &mut Vec<Point>) {
   if p2.x > x {
      if p1.x < x {
         points.push(Point::new(x, v_intersect(x, p1, p2)));
      }
   } else {
      if p1.x > x && p2.x < x {
         points.push(Point::new(x, v_intersect(x, p1, p2)));
      }
      points.push(p2);
   }
}

#[inline]
pub fn v_right_intersect(x: i64, p1: Point, p2: Point, points: &mut Vec<Point>) {
   if p2.x < x {
      if p1.x > x {
         points.push(Point::new(x, v_intersect(x, p1, p2)));
      }
   } else {
      if p1.x < x && p2.x > x {
         points.push(Point::new(x, v_intersect(x, p1, p2)));
      }
      points.push(p2);
   }
}

#[inline]
pub fn h_intersect(y: i64, p1: Point, p2: Point) -> i64 {
   p1.x + ((p2.x - p1.x) * (y - p1.y)).rounding_div(p2.y - p1.y)
}

#[inline]
pub fn v_intersect(x: i64, p1: Point, p2: Point) -> i64 {
   p1.y + ((p2.y - p1.y) * (x - p1.x)).rounding_div(p2.x - p1.x)
}

pub fn hv_clip<F>(intersect: F, y: i64, points: &mut Vec<Point>, start: usize) where
   F: Fn(i64, Point, Point, &mut Vec<Point>),
{
   let end = points.len();

   if end - start < 2 {
      return;
   }

   let mut p1 = points[end-1];

   for i in start..end {
      let p2 = points[i];
      intersect(y, p1, p2, points);

      p1 = p2;
   }
}

pub fn h_split(y: i64, start: usize, down: &mut Vec<Point>, up: &mut Vec<Point>) {
   let end = up.len();

   assert!(end - start > 2);

   let mut p1 = up[end-1];

   for i in start..end {
      let p2 = up[i];

      h_split_edge(y, p1, p2, down, up);

      p1 = p2;
   }
}

pub fn v_split(x: i64, start: usize, left: &mut Vec<Point>, right: &mut Vec<Point>) {
   let end = right.len();

   assert!(end - start > 2);

   let mut p1 = right[end-1];

   for i in start..end {
      let p2 = right[i];

      v_split_edge(x, p1, p2, left, right);

      p1 = p2;
   }
}

#[inline]
pub fn h_split_edge(y: i64, p1: Point, p2: Point, down: &mut Vec<Point>, up: &mut Vec<Point>) {
   if p2.y > y {
      if p1.y < y {
         let intersection = Point::new(h_intersect(y, p1, p2), y);
         down.push(intersection);
         up.push(intersection);
      }
      up.push(p2);
   } else if p2.y < y {
      if p1.y > y {
         let intersection = Point::new(h_intersect(y, p1, p2), y);
         down.push(intersection);
         up.push(intersection);
      }
      down.push(p2);
   } else {
      down.push(p2);
      up.push(p2);
   }
}

#[inline]
pub fn v_split_edge(x: i64, p1: Point, p2: Point, left: &mut Vec<Point>, right: &mut Vec<Point>) {
   if p2.x > x {
      if p1.x < x {
         let intersection = Point::new(x, v_intersect(x, p1, p2));
         left.push(intersection);
         right.push(intersection);
      }
      right.push(p2);
   } else if p2.x < x {
      if p1.x > x {
         let intersection = Point::new(x, v_intersect(x, p1, p2));
         left.push(intersection);
         right.push(intersection);
      }
      left.push(p2);
   } else {
      left.push(p2);
      right.push(p2);
   }
}

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

   use super::super::point::Point;

   use super::*;

   #[bench]
   fn bench_clip(b: &mut Bencher) {
      b.iter(|| {
         for _ in 0..1000 {
            let mut points = vec![
               Point::new(50, 50),
               Point::new(100, 200),
               Point::new(150, 100),
            ];
            points.reserve(30);

            let start = 0;
            let end = points.len();
            hv_clip(h_down_intersect, 150, &mut points, start);

            let start = end;
            let end = points.len();
            hv_clip(h_up_intersect, 80, &mut points, start);

            let start = end;
            let end = points.len();
            hv_clip(v_right_intersect, 70, &mut points, start);

            let start = end;
            hv_clip(v_left_intersect, 140, &mut points, start);
         }
      });
   }

   #[test]
   fn test_clip() {
      let mut points = vec![
         Point::new(50, 50),
         Point::new(100, 200),
         Point::new(150, 100),
      ];

      let start = 0;
      let end = points.len();
      hv_clip(h_down_intersect, 150, &mut points, start);

      let start = end;
      let end = points.len();
      hv_clip(h_up_intersect, 80, &mut points, start);

      let start = end;
      let end = points.len();
      hv_clip(v_right_intersect, 70, &mut points, start);

      let start = end;
      let end = points.len();
      hv_clip(v_left_intersect, 140, &mut points, start);

      assert_eq!(points.len(), 25);

      let (_, result) = points.split_at(end);
      assert_eq!(result, [
         Point::new(140, 95),
         Point::new(110, 80),
         Point::new(70, 80),
         Point::new(70, 110),
         Point::new(83, 150),
         Point::new(125, 150),
         Point::new(140, 120),
      ]);
   }
}
<|endoftext|>


<commit_before>extern crate rand;
extern crate nadezhda;

use nadezhda::grammar::Program;
use nadezhda::environment::Environment;
use nadezhda::population::Population;
use nadezhda::evaluate::Evaluator;
use nadezhda::darwin::evolve::succesion;

fn survivors(population: Population, environment: Environment) -> Vec<Program> {
    let Population(generation) = population;
    let survivors: Vec<Program> = generation
        .iter()
        .map(|program| (program.clone(), program.evaluate_on(environment.clone())))
        .filter(|&(_, ref env)| env.cockroach_location - env.food_location == 0)
        .map(|(program, _)| program)
        .collect();

    survivors
}

fn main() {
    let mut rng = rand::thread_rng();
    let environment: Environment = Environment::new(6);
    let mut last_population: Population = Population::new(10);

    let mut generation_count = 0;
    while generation_count < 100 {
        let next_population = succesion(&mut rng, environment.clone(), last_population.clone());

        let survivors: Vec<Program> = survivors(next_population.clone(), environment.clone());
        last_population = next_population;

        if survivors.len() > 0 { break; }
        generation_count += 1;
    }

    let survivors: Vec<Program> = survivors(last_population.clone(), environment.clone());
    if survivors.len() > 0 {
        println!("{} {} {:?}", generation_count, survivors.len(), survivors[0]);
    } else {
        println!("{} {:?}", generation_count, last_population);
    }
}
<commit_msg>Let binary accept arguments<commit_after>extern crate rand;
extern crate nadezhda;

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

use nadezhda::grammar::Program;
use nadezhda::environment::Environment;
use nadezhda::population::Population;
use nadezhda::evaluate::Evaluator;
use nadezhda::darwin::evolve::succesion;

fn survivors(population: Population, environment: Environment) -> Vec<Program> {
    let Population(generation) = population;
    let survivors: Vec<Program> = generation
        .iter()
        .map(|program| (program.clone(), program.evaluate_on(environment.clone())))
        .filter(|&(_, ref env)| env.cockroach_location - env.food_location == 0)
        .map(|(program, _)| program)
        .collect();

    survivors
}

fn main() {
    if env::args().len() < 3 {
        println!("Usage: evolve FOOD POPULATION");
        process::exit(1);
    }

    let food_string: String = env::args().nth(1).unwrap();
    let food: i32 = match i32::from_str_radix(&food_string, 10) {
        Ok(value) => value,
        Err(_) => {
            println!("FOOD argument is not an integer");
            process::exit(2);
        }
    };

    let population_count_string: String = env::args().nth(2).unwrap();
    let population_count: i32 = match i32::from_str_radix(&population_count_string, 10) {
        Ok(value) => value,
        Err(_) => {
            println!("POPULATION argument is not an integer");
            process::exit(3);
        }
    };

    let mut rng = rand::thread_rng();
    let environment: Environment = Environment::new(food);
    let mut last_population: Population = Population::new(population_count);

    let mut generation_count = 0;
    while generation_count < 100 {
        let next_population = succesion(&mut rng, environment.clone(), last_population.clone());

        let survivors: Vec<Program> = survivors(next_population.clone(), environment.clone());
        last_population = next_population;

        if survivors.len() > 0 { break; }
        generation_count += 1;
    }

    let survivors: Vec<Program> = survivors(last_population.clone(), environment.clone());
    if survivors.len() > 0 {
        println!("{} {} {:?}", generation_count, survivors.len(), survivors[0]);
    } else {
        println!("{} {:?}", generation_count, last_population);
    }
}
<|endoftext|>


<commit_before>/// Represents an ASN.1 `BIT STRING`.
#[derive(Debug, PartialEq, Clone)]
pub struct BitString<'a> {
    data: &'a [u8],
    padding_bits: u8,
}

impl<'a> BitString<'a> {
    pub(crate) fn new(data: &'a [u8], padding_bits: u8) -> Option<BitString<'a>> {
        if padding_bits > 7 || (data.is_empty() && padding_bits != 0) {
            return None;
        }
        if padding_bits > 0 && data[data.len() - 1] & ((1 << padding_bits) - 1) != 0 {
            return None;
        }

        Some(BitString { data, padding_bits })
    }

    /// Returns a sequence of bytes representing the data in the `BIT STRING`. Padding bits will
    /// always be 0.
    pub fn as_bytes(&self) -> &'a [u8] {
        self.data
    }

    /// Returns the number of padding bits. Will always be in [0, 8).
    pub fn padding_bits(&self) -> u8 {
        self.padding_bits
    }

    /// Returns whether the requested bit is set. Padding bits will always return false and
    /// asking for bits that exceed the length of the bit string will also return false.
    pub fn has_bit_set(&self, n: usize) -> bool {
        let idx = n / 8;
        let v = 1 << (7 - (n & 0x07));
        if self.data.len() < (idx + 1) {
            false
        } else {
            self.data[idx] & v != 0
        }
    }
}

#[cfg(test)]
mod tests {
    use crate::BitString;

    #[test]
    fn test_bitstring_new() {
        assert_eq!(BitString::new(b"abc", 8), None);
        assert_eq!(BitString::new(b"", 2), None);
        assert_eq!(BitString::new(b"\xff", 1), None);

        assert!(BitString::new(b"\xff", 0).is_some());
        assert!(BitString::new(b"\xfe", 1).is_some());
    }

    #[test]
    fn test_bitstring_as_bytes() {
        let bs = BitString::new(b"\xfe", 1).unwrap();
        assert_eq!(bs.as_bytes(), b"\xfe");
    }

    #[test]
    fn test_bitstring_padding_bits() {
        let bs = BitString::new(b"\xfe", 1).unwrap();
        assert_eq!(bs.padding_bits(), 1);
        let bs = BitString::new(b"\xe0", 5).unwrap();
        assert_eq!(bs.padding_bits(), 5);
    }

    #[test]
    fn test_bitstring_has_bit_set() {
        let bs = BitString::new(b"\x80", 0).unwrap();
        assert_eq!(bs.has_bit_set(0), true);
        assert_eq!(bs.has_bit_set(1), false);
        assert_eq!(bs.has_bit_set(7), false);
        // An arbitrary bit much bigger than the underlying size of the bitfield
        assert_eq!(bs.has_bit_set(50), false);
        let bs = BitString::new(b"\xc0", 4).unwrap();
        // padding bits should always return false when asking if the bit is set
        assert_eq!(bs.has_bit_set(0), true);
        assert_eq!(bs.has_bit_set(1), true);
        assert_eq!(bs.has_bit_set(2), false);
        assert_eq!(bs.has_bit_set(3), false);
        assert_eq!(bs.has_bit_set(4), false);
        assert_eq!(bs.has_bit_set(5), false);
        assert_eq!(bs.has_bit_set(6), false);
        assert_eq!(bs.has_bit_set(7), false);
    }
}
<commit_msg>make clippy happy<commit_after>/// Represents an ASN.1 `BIT STRING`.
#[derive(Debug, PartialEq, Clone)]
pub struct BitString<'a> {
    data: &'a [u8],
    padding_bits: u8,
}

impl<'a> BitString<'a> {
    pub(crate) fn new(data: &'a [u8], padding_bits: u8) -> Option<BitString<'a>> {
        if padding_bits > 7 || (data.is_empty() && padding_bits != 0) {
            return None;
        }
        if padding_bits > 0 && data[data.len() - 1] & ((1 << padding_bits) - 1) != 0 {
            return None;
        }

        Some(BitString { data, padding_bits })
    }

    /// Returns a sequence of bytes representing the data in the `BIT STRING`. Padding bits will
    /// always be 0.
    pub fn as_bytes(&self) -> &'a [u8] {
        self.data
    }

    /// Returns the number of padding bits. Will always be in [0, 8).
    pub fn padding_bits(&self) -> u8 {
        self.padding_bits
    }

    /// Returns whether the requested bit is set. Padding bits will always return false and
    /// asking for bits that exceed the length of the bit string will also return false.
    pub fn has_bit_set(&self, n: usize) -> bool {
        let idx = n / 8;
        let v = 1 << (7 - (n & 0x07));
        if self.data.len() < (idx + 1) {
            false
        } else {
            self.data[idx] & v != 0
        }
    }
}

#[cfg(test)]
mod tests {
    use crate::BitString;

    #[test]
    fn test_bitstring_new() {
        assert_eq!(BitString::new(b"abc", 8), None);
        assert_eq!(BitString::new(b"", 2), None);
        assert_eq!(BitString::new(b"\xff", 1), None);

        assert!(BitString::new(b"\xff", 0).is_some());
        assert!(BitString::new(b"\xfe", 1).is_some());
    }

    #[test]
    fn test_bitstring_as_bytes() {
        let bs = BitString::new(b"\xfe", 1).unwrap();
        assert_eq!(bs.as_bytes(), b"\xfe");
    }

    #[test]
    fn test_bitstring_padding_bits() {
        let bs = BitString::new(b"\xfe", 1).unwrap();
        assert_eq!(bs.padding_bits(), 1);
        let bs = BitString::new(b"\xe0", 5).unwrap();
        assert_eq!(bs.padding_bits(), 5);
    }

    #[test]
    fn test_bitstring_has_bit_set() {
        let bs = BitString::new(b"\x80", 0).unwrap();
        assert!(bs.has_bit_set(0));
        assert!(!bs.has_bit_set(1));
        assert!(!bs.has_bit_set(7));
        // An arbitrary bit much bigger than the underlying size of the bitfield
        assert!(!bs.has_bit_set(50));
        let bs = BitString::new(b"\xc0", 4).unwrap();
        // padding bits should always return false when asking if the bit is set
        assert!(bs.has_bit_set(0));
        assert!(bs.has_bit_set(1));
        assert!(!bs.has_bit_set(2));
        assert!(!bs.has_bit_set(3));
        assert!(!bs.has_bit_set(4));
        assert!(!bs.has_bit_set(5));
        assert!(!bs.has_bit_set(6));
        assert!(!bs.has_bit_set(7));
    }
}
<|endoftext|>


<commit_before>extern crate tempdir;

use self::tempdir::TempDir;
use super::mpd;
use std::fs::{File, create_dir};
use std::io::Write;
use std::os::unix::net::UnixStream;
use std::path::{Path, PathBuf};
use std::process::{Command, Child};

struct MpdConfig {
    db_file: PathBuf,
    music_directory: PathBuf,
    playlist_directory: PathBuf,
    config_path: PathBuf,
    sock_path: PathBuf,
}

impl MpdConfig {
    pub fn new<P>(base: P) -> MpdConfig
        where P: AsRef<Path>
    {
        let base = base.as_ref();
        MpdConfig {
            db_file: base.join("db"),
            music_directory: base.join("music"),
            playlist_directory: base.join("playlists"),
            config_path: base.join("config"),
            sock_path: base.join("sock"),
        }
    }

    fn config_text(&self) -> String {
        format!(r#"
db_file "{db_file}"
log_file "/dev/null"
music_directory "{music_directory}"
playlist_directory "{playlist_directory}"
bind_to_address "{sock_path}"
audio_output {{
    type "null"
    name "null"
}}
"#,
            db_file=self.db_file.display(),
            music_directory=self.music_directory.display(),
            playlist_directory=self.playlist_directory.display(),
            sock_path=self.sock_path.display(),
        )
    }

    fn generate(&self) {
        create_dir(&self.music_directory).expect("Could not create music directory.");
        create_dir(&self.playlist_directory).expect("Could not create playlist directory.");
        let mut file = File::create(&self.config_path).expect("Could not create config file.");
        file.write_all(self.config_text().as_bytes()).expect("Could not write config file.");
    }
}

pub struct Daemon {
    // Saved here so it gets dropped when this does.
    _temp_dir: TempDir,
    config: MpdConfig,
    process: Child,
}

impl Drop for Daemon {
    fn drop(&mut self) {
        self.process.kill().expect("Could not kill mpd daemon.");
        self.process.wait().expect("Could not wait for mpd daemon to shutdown.");
    }
}

fn sleep() {
    use std::{thread, time};
    let ten_millis = time::Duration::from_millis(10);
    thread::sleep(ten_millis);
}

impl Daemon {
    pub fn start() -> Daemon {
        let temp_dir = TempDir::new("mpd-test").unwrap();
        let config = MpdConfig::new(&temp_dir);
        config.generate();
        let process = Command::new("mpd")
            .arg("--no-daemon")
            .arg(&config.config_path)
            .spawn()
            .expect("Could not create mpd daemon.");

        let daemon = Daemon {
            _temp_dir: temp_dir,
            config: config,
            process: process,
        };

        // Wait until we can connect to the daemon
        while let Err(_) = daemon.maybe_connect() {
            sleep()
        }

        daemon
    }

    fn maybe_connect(&self) -> Result<mpd::Client<UnixStream>, mpd::error::Error> {
        let stream = UnixStream::connect(&self.config.sock_path)?;
        mpd::Client::new(stream)
    }

    pub fn connect(&self) -> mpd::Client<UnixStream> {
        self.maybe_connect().expect("Could not connect to daemon.")
    }
}
<commit_msg>Wait for db to finish updating before handing off to tests.<commit_after>extern crate tempdir;

use self::tempdir::TempDir;
use super::mpd;
use std::fs::{File, create_dir};
use std::io::Write;
use std::os::unix::net::UnixStream;
use std::path::{Path, PathBuf};
use std::process::{Command, Child};

struct MpdConfig {
    db_file: PathBuf,
    music_directory: PathBuf,
    playlist_directory: PathBuf,
    config_path: PathBuf,
    sock_path: PathBuf,
}

impl MpdConfig {
    pub fn new<P>(base: P) -> MpdConfig
        where P: AsRef<Path>
    {
        let base = base.as_ref();
        MpdConfig {
            db_file: base.join("db"),
            music_directory: base.join("music"),
            playlist_directory: base.join("playlists"),
            config_path: base.join("config"),
            sock_path: base.join("sock"),
        }
    }

    fn config_text(&self) -> String {
        format!(r#"
db_file "{db_file}"
log_file "/dev/null"
music_directory "{music_directory}"
playlist_directory "{playlist_directory}"
bind_to_address "{sock_path}"
audio_output {{
    type "null"
    name "null"
}}
"#,
            db_file=self.db_file.display(),
            music_directory=self.music_directory.display(),
            playlist_directory=self.playlist_directory.display(),
            sock_path=self.sock_path.display(),
        )
    }

    fn generate(&self) {
        create_dir(&self.music_directory).expect("Could not create music directory.");
        create_dir(&self.playlist_directory).expect("Could not create playlist directory.");
        let mut file = File::create(&self.config_path).expect("Could not create config file.");
        file.write_all(self.config_text().as_bytes()).expect("Could not write config file.");
    }
}

pub struct Daemon {
    // Saved here so it gets dropped when this does.
    _temp_dir: TempDir,
    config: MpdConfig,
    process: Child,
}

impl Drop for Daemon {
    fn drop(&mut self) {
        self.process.kill().expect("Could not kill mpd daemon.");
        self.process.wait().expect("Could not wait for mpd daemon to shutdown.");
    }
}

fn sleep() {
    use std::{thread, time};
    let ten_millis = time::Duration::from_millis(10);
    thread::sleep(ten_millis);
}

impl Daemon {
    pub fn start() -> Daemon {
        let temp_dir = TempDir::new("mpd-test").unwrap();
        let config = MpdConfig::new(&temp_dir);
        config.generate();
        let process = Command::new("mpd")
            .arg("--no-daemon")
            .arg(&config.config_path)
            .spawn()
            .expect("Could not create mpd daemon.");

        let daemon = Daemon {
            _temp_dir: temp_dir,
            config: config,
            process: process,
        };

        // Wait until we can connect to the daemon
        let mut client;
        loop {
            if let Ok(c) = daemon.maybe_connect() {
                client = c;
                break;
            }
            sleep()
        }
        while let Some(_) = client.status().expect("Couldn't get status.").updating_db {
            sleep()
        }

        daemon
    }

    fn maybe_connect(&self) -> Result<mpd::Client<UnixStream>, mpd::error::Error> {
        let stream = UnixStream::connect(&self.config.sock_path)?;
        mpd::Client::new(stream)
    }

    pub fn connect(&self) -> mpd::Client<UnixStream> {
        self.maybe_connect().expect("Could not connect to daemon.")
    }
}
<|endoftext|>


<commit_before>pub struct Block {
    pub type_map: u16,
    pub type_map_ext: u8,

    pub west: u8,
    pub east: u8,
    pub north: u8,
    pub south: u8,
    pub lid: u8
}

impl Block {
    pub fn get_block_type(&self) -> BlockType {
        let v1: u8 = if self.type_map & 16 != 0 { 1 } else { 0 };
        let v2: u8 = if self.type_map & 32 != 0 { 2 } else { 0 };
        let v3: u8 = if self.type_map & 64 != 0 { 4 } else { 0 };

        match v1 + v2 + v3 {
            0 => Air,
            1 => Water,
            2 => Road,
            3 => Pavement,
            4 => Field,
            5 => Building,
            _ => Unused
        }
    }

    pub fn is_flat(&self) -> bool {
        self.type_map & 128 != 0
    }
}

impl Block {
    pub fn is_traffic_light(&self) -> bool {
        self.type_map_ext & 1 != 0
    }

    pub fn is_railway_end_turn(&self) -> bool {
        self.type_map_ext & 4 != 0
    }

    pub fn is_railway_start_turn(&self) -> bool {
        self.type_map_ext & 4 != 0 && self.type_map_ext & 1 != 0
    }

    pub fn is_railway_station(&self) -> bool {
        self.type_map_ext & 4 != 0 && self.type_map_ext & 2 != 0
    }

    pub fn is_railway_train(&self) -> bool {
        self.type_map_ext & 4 != 0 && self.type_map_ext & 2 != 0 &&
            self.type_map_ext & 1 != 0
    }

    pub fn get_remap_index(&self) -> bool {
        let first_cond: u8 = if self.type_map_ext & 8 != 0 { 1 } else { 0 };
        let second_cond: u8 = if self.type_map_ext & 16 != 0 { 2 } else { 0 };
        first_cond + second_cond != 0
    }

    pub fn should_flip_north_south(&self) -> bool {
        self.type_map_ext & 32 != 0
    }

    pub fn should_flip_west_east(&self) -> bool {
        self.type_map_ext & 64 != 0
    }

    pub fn is_railway(&self) -> bool {
        self.type_map_ext & 128 != 0
    }
}

pub enum BlockType {
    Air,
    Water,
    Road,
    Pavement,
    Field,
    Building,
    Unused
}
<commit_msg>Added more methods for Block.<commit_after>pub struct Block {
    pub type_map: u16,
    pub type_map_ext: u8,

    pub west: u8,
    pub east: u8,
    pub north: u8,
    pub south: u8,
    pub lid: u8
}

impl Block {
    pub fn is_north_direction_allowed(&self) -> bool {
        self.type_map & 1 != 0
    }

    pub fn is_south_direction_allowed(&self) -> bool {
        self.type_map & 2 != 0
    }

    pub fn is_west_direction_allowed(&self) -> bool {
        self.type_map & 4 != 0
    }

    pub fn is_east_direction_allowed(&self) -> bool {
        self.type_map & 8 != 0
    }

    pub fn get_block_type(&self) -> BlockType {
        let v1: u8 = if self.type_map & 16 != 0 { 1 } else { 0 };
        let v2: u8 = if self.type_map & 32 != 0 { 2 } else { 0 };
        let v3: u8 = if self.type_map & 64 != 0 { 4 } else { 0 };

        match v1 + v2 + v3 {
            0 => Air,
            1 => Water,
            2 => Road,
            3 => Pavement,
            4 => Field,
            5 => Building,
            _ => Unused
        }
    }

    pub fn get_slope_type(&self) -> u8 {
        let v1: u8 = if self.type_map & 256 != 0 { 1 } else { 0 };
        let v2: u8 = if self.type_map & 512 != 0 { 2 } else { 0 };
        let v3: u8 = if self.type_map & 1024 != 0 { 4 } else { 0 };
        let v4: u8 = if self.type_map & 2048 != 0 { 8 } else { 0 };
        let v5: u8 = if self.type_map & 4096 != 0 { 16 } else { 0 };
        let v6: u8 = if self.type_map & 8192 != 0 { 32 } else { 0 };

        v1 + v2 + v3 + v4 + v5 + v6
    }

    pub fn is_flat(&self) -> bool {
        self.type_map & 128 != 0
    }
}

impl Block {
    pub fn is_traffic_light(&self) -> bool {
        self.type_map_ext & 1 != 0
    }

    pub fn is_railway_end_turn(&self) -> bool {
        self.type_map_ext & 4 != 0
    }

    pub fn is_railway_start_turn(&self) -> bool {
        self.type_map_ext & 4 != 0 && self.type_map_ext & 1 != 0
    }

    pub fn is_railway_station(&self) -> bool {
        self.type_map_ext & 4 != 0 && self.type_map_ext & 2 != 0
    }

    pub fn is_railway_train(&self) -> bool {
        self.type_map_ext & 4 != 0 && self.type_map_ext & 2 != 0 &&
            self.type_map_ext & 1 != 0
    }

    pub fn get_remap_index(&self) -> bool {
        let first_cond: u8 = if self.type_map_ext & 8 != 0 { 1 } else { 0 };
        let second_cond: u8 = if self.type_map_ext & 16 != 0 { 2 } else { 0 };
        first_cond + second_cond != 0
    }

    pub fn should_flip_north_south(&self) -> bool {
        self.type_map_ext & 32 != 0
    }

    pub fn should_flip_east_west(&self) -> bool {
        self.type_map_ext & 64 != 0
    }

    pub fn is_railway(&self) -> bool {
        self.type_map_ext & 128 != 0
    }
}

pub enum BlockType {
    Air,
    Water,
    Road,
    Pavement,
    Field,
    Building,
    Unused
}
<|endoftext|>