<commit_before>// Copyright 2017 Mozilla Foundation
// Copyright 2017 Google Inc.
//
// 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::cell::RefCell;
use std::fmt;
use std::io;
use std::rc::Rc;
use std::time;

use cache::{
    Cache,
    CacheRead,
    CacheWrite,
    Storage,
};
use chrono;
use futures::future::Shared;
use futures::{future, Async, Future, Stream};
use hyper;
use hyper::header::{Authorization, Bearer, ContentType, ContentLength};
use hyper::Method;
use hyper::client::{Client, HttpConnector, Request};
use hyper_tls::HttpsConnector;
use jwt;
use openssl;
use serde_json;
use tokio_core::reactor::Handle;
use url::form_urlencoded;
use url::percent_encoding::{percent_encode, PATH_SEGMENT_ENCODE_SET, QUERY_ENCODE_SET};

use errors::*;

type HyperClient = Client<HttpsConnector<HttpConnector>>;

/// GCS bucket
struct Bucket {
    name: String,
    client: HyperClient,
}

impl fmt::Display for Bucket {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Bucket(name={})", self.name)
    }
}

impl Bucket {
    pub fn new(name: String, handle: &Handle) -> Result<Bucket> {
        let client = Client::configure()
                        .connector(HttpsConnector::new(1, handle)?)
                        .build(handle);

        Ok(Bucket { name, client })
    }

    fn get(&self, key: &str, cred_provider: &Option<GCSCredentialProvider>) -> SFuture<Vec<u8>> {
        let url = format!("https://www.googleapis.com/download/storage/v1/b/{}/o/{}?alt=media",
                    percent_encode(self.name.as_bytes(), PATH_SEGMENT_ENCODE_SET),
                    percent_encode(key.as_bytes(), PATH_SEGMENT_ENCODE_SET));

        let client = self.client.clone();

        let creds_opt_future = if let &Some(ref cred_provider) = cred_provider {
            future::Either::A(cred_provider.credentials(&self.client).map(Some))
        } else {
            future::Either::B(future::ok(None))
        };

        Box::new(creds_opt_future.and_then(move |creds_opt| {
            let mut request = Request::new(Method::Get, url.parse().unwrap());
            if let Some(creds) = creds_opt {
                request.headers_mut()
                    .set(Authorization(Bearer { token: creds.token }));
            }
            client.request(request).chain_err(move || {
                format!("failed GET: {}", url)
            }).and_then(|res| {
                if res.status().is_success() {
                    Ok(res.body())
                } else {
                    Err(ErrorKind::BadHTTPStatus(res.status().clone()).into())
                }
            }).and_then(|body| {
                body.fold(Vec::new(), |mut body, chunk| {
                    body.extend_from_slice(&chunk);
                    Ok::<_, hyper::Error>(body)
                }).chain_err(|| {
                    "failed to read HTTP body"
                })
            })
        }))
    }

    fn put(&self, key: &str, content: Vec<u8>, cred_provider: &Option<GCSCredentialProvider>) -> SFuture<()> {
        let url = format!("https://www.googleapis.com/upload/storage/v1/b/{}/o?name={}&uploadType=media",
                    percent_encode(self.name.as_bytes(), PATH_SEGMENT_ENCODE_SET),
                    percent_encode(key.as_bytes(), QUERY_ENCODE_SET));

        let client = self.client.clone();

        let creds_opt_future = if let &Some(ref cred_provider) = cred_provider {
            future::Either::A(cred_provider.credentials(&self.client).map(Some))
        } else {
            future::Either::B(future::ok(None))
        };

        Box::new(creds_opt_future.and_then(move |creds_opt| {
            let mut request = Request::new(Method::Post, url.parse().unwrap());
            {
                let headers = request.headers_mut();
                if let Some(creds) = creds_opt {
                    headers.set(Authorization(Bearer { token: creds.token }));
                }
                headers.set(ContentType::octet_stream());
                headers.set(ContentLength(content.len() as u64));
            }
            request.set_body(content);

            client.request(request).then(|result| {
                match result {
                    Ok(res) => {
                        if res.status().is_success() {
                            trace!("PUT succeeded");
                            Ok(())
                        } else {
                            trace!("PUT failed with HTTP status: {}", res.status());
                            Err(ErrorKind::BadHTTPStatus(res.status().clone()).into())
                        }
                    }
                    Err(e) => {
                        trace!("PUT failed with error: {:?}", e);
                        Err(e.into())
                    }
                }
            })
        }))
    }
}

/// GCSCredentialProvider provides GCS OAUTH tokens.
///
/// It uses service account credentials to request tokens, and caches the result so that successive
/// calls to GCS APIs don't need to request new tokens.
pub struct GCSCredentialProvider {
    rw_mode: RWMode,
    sa_key: ServiceAccountKey,
    cached_credentials: RefCell<Option<Shared<SFuture<GCSCredential>>>>,
}

/// ServiceAccountKey is a subset of the information in the JSON service account credentials.
///
/// Note: by default, serde ignores extra fields when deserializing. This allows us to keep this
/// structure minimal and not list all the fields present in a service account credential file.
#[derive(Debug, Deserialize)]
pub struct ServiceAccountKey {
    private_key: String,
    client_email: String,
}

/// JwtClaims are the required claims that must be present in the OAUTH token request JWT.
#[derive(Serialize)]
struct JwtClaims {
    #[serde(rename = "iss")]
    issuer: String,
    scope: String,
    #[serde(rename = "aud")]
    audience: String,
    #[serde(rename = "exp")]
    expiration: i64,
    #[serde(rename = "iat")]
    issued_at: i64,
}

/// TokenMsg is a subset of the information provided by GCS in response to an OAUTH token request.
///
/// Note: by default, serde ignores extra fields when deserializing. This allows us to keep this
/// structure minimal and not list all the fields present in the response.
#[derive(Deserialize)]
struct TokenMsg {
    access_token: String,
}

/// RWMode describes whether or not to attempt cache writes.
#[derive(Copy, Clone)]
pub enum RWMode {
    ReadOnly,
    ReadWrite,
}

/// GCSCredential is a GCS OAUTH token paired with an expiration time.
#[derive(Clone)]
pub struct GCSCredential {
    token: String,
    expiration_time: chrono::DateTime<chrono::UTC>,
}

impl GCSCredentialProvider {
    pub fn new(rw_mode: RWMode, sa_key: ServiceAccountKey) -> Self {
        GCSCredentialProvider {
            rw_mode,
            sa_key,
            cached_credentials: RefCell::new(None),
        }
    }

    fn auth_request_jwt(&self, expire_at: &chrono::DateTime<chrono::UTC>) -> Result<String> {
        let scope = (match self.rw_mode {
            RWMode::ReadOnly => "https://www.googleapis.com/auth/devstorage.readonly",
            RWMode::ReadWrite => "https://www.googleapis.com/auth/devstorage.read_write",
        }).to_owned();

        let jwt_claims = JwtClaims {
            issuer: self.sa_key.client_email.clone(),
            scope: scope,
            audience: "https://www.googleapis.com/oauth2/v4/token".to_owned(),
            expiration: expire_at.timestamp(),
            issued_at: chrono::UTC::now().timestamp(),
        };

        let binary_key = openssl::rsa::Rsa::private_key_from_pem(
            self.sa_key.private_key.as_bytes()
        )?.private_key_to_der()?;

        let auth_request_jwt = jwt::encode(
            &jwt::Header::new(jwt::Algorithm::RS256),
            &jwt_claims,
            &binary_key,
        )?;

        Ok(auth_request_jwt)
    }

    fn request_new_token(&self, client: &HyperClient) -> SFuture<GCSCredential> {
        let client = client.clone();
        let expires_at = chrono::UTC::now() + chrono::Duration::minutes(59);
        let auth_jwt = self.auth_request_jwt(&expires_at);

        // Request credentials
        Box::new(future::result(auth_jwt).and_then(move |auth_jwt| {
            let url = "https://www.googleapis.com/oauth2/v4/token";
            let params = form_urlencoded::Serializer::new(String::new())
                .append_pair("grant_type", "urn:ietf:params:oauth:grant-type:jwt-bearer")
                .append_pair("assertion", &auth_jwt)
                .finish();

            let mut request = Request::new(Method::Post, url.parse().unwrap());
            {
                let headers = request.headers_mut();
                headers.set(ContentType::form_url_encoded());
                headers.set(ContentLength(params.len() as u64));
            }
            request.set_body(params);

            client.request(request).map_err(Into::into)
        }).and_then(move |res| {
            if res.status().is_success() {
                Ok(res.body())
            } else {
                Err(ErrorKind::BadHTTPStatus(res.status().clone()).into())
            }
        }).and_then(move |body| {
            // Concatenate body chunks into a single Vec<u8>
            body.fold(Vec::new(), |mut body, chunk| {
                body.extend_from_slice(&chunk);
                Ok::<_, hyper::Error>(body)
            }).chain_err(|| {
                "failed to read HTTP body"
            })
        }).and_then(move |body| {
            // Convert body to string and parse the token out of the response
            let body_str = String::from_utf8(body)?;
            let token_msg: TokenMsg = serde_json::from_str(&body_str)?;
            Ok(GCSCredential {
                token: token_msg.access_token,
                expiration_time: expires_at,
            })
        }))
    }

    pub fn credentials(&self, client: &HyperClient) -> SFuture<GCSCredential> {
        let mut future_opt = self.cached_credentials.borrow_mut();

        let needs_refresh = match Option::as_mut(&mut future_opt).map(|f| f.poll()) {
            None => true,
            Some(Ok(Async::Ready(ref creds))) => creds.expiration_time < chrono::UTC::now(),
            _ => false
        };

        if needs_refresh {
            let credentials = self.request_new_token(client);
            *future_opt = Some(credentials.shared());
        };

        Box::new(Option::as_mut(&mut future_opt).unwrap().clone().then(|result| {
            match result {
                Ok(e) => Ok((*e).clone()),
                Err(e) => Err(e.to_string().into()),
            }
        }))
    }
}

/// A cache that stores entries in Google Cloud Storage
pub struct GCSCache {
    /// The GCS bucket
    bucket: Rc<Bucket>,
    /// Credential provider for GCS
    credential_provider: Option<GCSCredentialProvider>,
    /// Read-only or not
    rw_mode: RWMode,
}

impl GCSCache {
    /// Create a new `GCSCache` storing data in `bucket`
    pub fn new(bucket: String,
               credential_provider: Option<GCSCredentialProvider>,
               rw_mode: RWMode,
               handle: &Handle) -> Result<GCSCache>
    {
        Ok(GCSCache {
            bucket: Rc::new(Bucket::new(bucket, handle)?),
            rw_mode: rw_mode,
            credential_provider: credential_provider,
        })
    }
}

impl Storage for GCSCache {
    fn get(&self, key: &str) -> SFuture<Cache> {
        Box::new(self.bucket.get(&key, &self.credential_provider).then(|result| {
            match result {
                Ok(data) => {
                    let hit = CacheRead::from(io::Cursor::new(data))?;
                    Ok(Cache::Hit(hit))
                }
                Err(e) => {
                    warn!("Got GCS error: {:?}", e);
                    Ok(Cache::Miss)
                }
            }
        }))
    }

    fn put(&self, key: &str, entry: CacheWrite) -> SFuture<time::Duration> {
        if let RWMode::ReadOnly = self.rw_mode {
            return Box::new(future::ok(time::Duration::new(0, 0)));
        }

        let start = time::Instant::now();
        let data = match entry.finish() {
            Ok(data) => data,
            Err(e) => return Box::new(future::err(e.into())),
        };
        let bucket = self.bucket.clone();
        let response = bucket.put(&key, data, &self.credential_provider).chain_err(|| {
            "failed to put cache entry in GCS"
        });

        Box::new(response.map(move |_| start.elapsed()))
    }

    fn location(&self) -> String {
        format!("GCS, bucket: {}", self.bucket)
    }

    fn current_size(&self) -> Option<usize> { None }
    fn max_size(&self) -> Option<usize> { None }
}
<commit_msg>Fix GCS backend from https://github.com/mozilla/sccache/pull/212<commit_after>// Copyright 2017 Mozilla Foundation
// Copyright 2017 Google Inc.
//
// 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::cell::RefCell;
use std::fmt;
use std::io;
use std::rc::Rc;
use std::time;

use cache::{
    Cache,
    CacheRead,
    CacheWrite,
    Storage,
};
use chrono;
use futures::future::Shared;
use futures::{future, Async, Future, Stream};
use hyper;
use hyper::header::{Authorization, Bearer, ContentType, ContentLength};
use hyper::Method;
use hyper::client::{Client, HttpConnector, Request};
use hyper_tls::HttpsConnector;
use jwt;
use openssl;
use serde_json;
use tokio_core::reactor::Handle;
use url::form_urlencoded;
use url::percent_encoding::{percent_encode, PATH_SEGMENT_ENCODE_SET, QUERY_ENCODE_SET};

use errors::*;

type HyperClient = Client<HttpsConnector<HttpConnector>>;

/// GCS bucket
struct Bucket {
    name: String,
    client: HyperClient,
}

impl fmt::Display for Bucket {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Bucket(name={})", self.name)
    }
}

impl Bucket {
    pub fn new(name: String, handle: &Handle) -> Result<Bucket> {
        let client = Client::configure()
                        .connector(HttpsConnector::new(1, handle)?)
                        .build(handle);

        Ok(Bucket { name, client })
    }

    fn get(&self, key: &str, cred_provider: &Option<GCSCredentialProvider>) -> SFuture<Vec<u8>> {
        let url = format!("https://www.googleapis.com/download/storage/v1/b/{}/o/{}?alt=media",
                    percent_encode(self.name.as_bytes(), PATH_SEGMENT_ENCODE_SET),
                    percent_encode(key.as_bytes(), PATH_SEGMENT_ENCODE_SET));

        let client = self.client.clone();

        let creds_opt_future = if let &Some(ref cred_provider) = cred_provider {
            future::Either::A(cred_provider.credentials(&self.client).map(Some))
        } else {
            future::Either::B(future::ok(None))
        };

        Box::new(creds_opt_future.and_then(move |creds_opt| {
            let mut request = Request::new(Method::Get, url.parse().unwrap());
            if let Some(creds) = creds_opt {
                request.headers_mut()
                    .set(Authorization(Bearer { token: creds.token }));
            }
            client.request(request).chain_err(move || {
                format!("failed GET: {}", url)
            }).and_then(|res| {
                if res.status().is_success() {
                    Ok(res.body())
                } else {
                    Err(ErrorKind::BadHTTPStatus(res.status().clone()).into())
                }
            }).and_then(|body| {
                body.fold(Vec::new(), |mut body, chunk| {
                    body.extend_from_slice(&chunk);
                    Ok::<_, hyper::Error>(body)
                }).chain_err(|| {
                    "failed to read HTTP body"
                })
            })
        }))
    }

    fn put(&self, key: &str, content: Vec<u8>, cred_provider: &Option<GCSCredentialProvider>) -> SFuture<()> {
        let url = format!("https://www.googleapis.com/upload/storage/v1/b/{}/o?name={}&uploadType=media",
                    percent_encode(self.name.as_bytes(), PATH_SEGMENT_ENCODE_SET),
                    percent_encode(key.as_bytes(), QUERY_ENCODE_SET));

        let client = self.client.clone();

        let creds_opt_future = if let &Some(ref cred_provider) = cred_provider {
            future::Either::A(cred_provider.credentials(&self.client).map(Some))
        } else {
            future::Either::B(future::ok(None))
        };

        Box::new(creds_opt_future.and_then(move |creds_opt| {
            let mut request = Request::new(Method::Post, url.parse().unwrap());
            {
                let headers = request.headers_mut();
                if let Some(creds) = creds_opt {
                    headers.set(Authorization(Bearer { token: creds.token }));
                }
                headers.set(ContentType::octet_stream());
                headers.set(ContentLength(content.len() as u64));
            }
            request.set_body(content);

            client.request(request).then(|result| {
                match result {
                    Ok(res) => {
                        if res.status().is_success() {
                            trace!("PUT succeeded");
                            Ok(())
                        } else {
                            trace!("PUT failed with HTTP status: {}", res.status());
                            Err(ErrorKind::BadHTTPStatus(res.status().clone()).into())
                        }
                    }
                    Err(e) => {
                        trace!("PUT failed with error: {:?}", e);
                        Err(e.into())
                    }
                }
            })
        }))
    }
}

/// GCSCredentialProvider provides GCS OAUTH tokens.
///
/// It uses service account credentials to request tokens, and caches the result so that successive
/// calls to GCS APIs don't need to request new tokens.
pub struct GCSCredentialProvider {
    rw_mode: RWMode,
    sa_key: ServiceAccountKey,
    cached_credentials: RefCell<Option<Shared<SFuture<GCSCredential>>>>,
}

/// ServiceAccountKey is a subset of the information in the JSON service account credentials.
///
/// Note: by default, serde ignores extra fields when deserializing. This allows us to keep this
/// structure minimal and not list all the fields present in a service account credential file.
#[derive(Debug, Deserialize)]
pub struct ServiceAccountKey {
    private_key: String,
    client_email: String,
}

/// JwtClaims are the required claims that must be present in the OAUTH token request JWT.
#[derive(Serialize)]
struct JwtClaims {
    #[serde(rename = "iss")]
    issuer: String,
    scope: String,
    #[serde(rename = "aud")]
    audience: String,
    #[serde(rename = "exp")]
    expiration: i64,
    #[serde(rename = "iat")]
    issued_at: i64,
}

/// TokenMsg is a subset of the information provided by GCS in response to an OAUTH token request.
///
/// Note: by default, serde ignores extra fields when deserializing. This allows us to keep this
/// structure minimal and not list all the fields present in the response.
#[derive(Deserialize)]
struct TokenMsg {
    access_token: String,
}

/// RWMode describes whether or not to attempt cache writes.
#[derive(Copy, Clone)]
pub enum RWMode {
    ReadOnly,
    ReadWrite,
}

/// GCSCredential is a GCS OAUTH token paired with an expiration time.
#[derive(Clone)]
pub struct GCSCredential {
    token: String,
    expiration_time: chrono::DateTime<chrono::UTC>,
}

impl GCSCredentialProvider {
    pub fn new(rw_mode: RWMode, sa_key: ServiceAccountKey) -> Self {
        GCSCredentialProvider {
            rw_mode,
            sa_key,
            cached_credentials: RefCell::new(None),
        }
    }

    fn auth_request_jwt(&self, expire_at: &chrono::DateTime<chrono::UTC>) -> Result<String> {
        let scope = (match self.rw_mode {
            RWMode::ReadOnly => "https://www.googleapis.com/auth/devstorage.readonly",
            RWMode::ReadWrite => "https://www.googleapis.com/auth/devstorage.read_write",
        }).to_owned();

        let jwt_claims = JwtClaims {
            issuer: self.sa_key.client_email.clone(),
            scope: scope,
            audience: "https://www.googleapis.com/oauth2/v4/token".to_owned(),
            expiration: expire_at.timestamp(),
            issued_at: chrono::UTC::now().timestamp(),
        };

        let binary_key = openssl::rsa::Rsa::private_key_from_pem(
            self.sa_key.private_key.as_bytes()
        )?.private_key_to_der()?;

        let auth_request_jwt = jwt::encode(
            &jwt::Header::new(jwt::Algorithm::RS256),
            &jwt_claims,
            &binary_key,
        )?;

        Ok(auth_request_jwt)
    }

    fn request_new_token(&self, client: &HyperClient) -> SFuture<GCSCredential> {
        let client = client.clone();
        let expires_at = chrono::UTC::now() + chrono::Duration::minutes(59);
        let auth_jwt = self.auth_request_jwt(&expires_at);

        // Request credentials
        Box::new(future::result(auth_jwt).and_then(move |auth_jwt| {
            let url = "https://www.googleapis.com/oauth2/v4/token";
            let params = form_urlencoded::Serializer::new(String::new())
                .append_pair("grant_type", "urn:ietf:params:oauth:grant-type:jwt-bearer")
                .append_pair("assertion", &auth_jwt)
                .finish();

            let mut request = Request::new(Method::Post, url.parse().unwrap());
            {
                let headers = request.headers_mut();
                headers.set(ContentType::form_url_encoded());
                headers.set(ContentLength(params.len() as u64));
            }
            request.set_body(params);

            client.request(request).map_err(Into::into)
        }).and_then(move |res| {
            if res.status().is_success() {
                Ok(res.body())
            } else {
                Err(ErrorKind::BadHTTPStatus(res.status().clone()).into())
            }
        }).and_then(move |body| {
            // Concatenate body chunks into a single Vec<u8>
            body.fold(Vec::new(), |mut body, chunk| {
                body.extend_from_slice(&chunk);
                Ok::<_, hyper::Error>(body)
            }).chain_err(|| {
                "failed to read HTTP body"
            })
        }).and_then(move |body| {
            // Convert body to string and parse the token out of the response
            let body_str = String::from_utf8(body)?;
            let token_msg: TokenMsg = serde_json::from_str(&body_str)?;
            Ok(GCSCredential {
                token: token_msg.access_token,
                expiration_time: expires_at,
            })
        }))
    }

    pub fn credentials(&self, client: &HyperClient) -> SFuture<GCSCredential> {
        let mut future_opt = self.cached_credentials.borrow_mut();

        let needs_refresh = match Option::as_mut(&mut future_opt).map(|f| f.poll()) {
            None => true,
            Some(Ok(Async::Ready(ref creds))) => creds.expiration_time < chrono::UTC::now(),
            _ => false
        };

        if needs_refresh {
            let credentials = self.request_new_token(client);
            *future_opt = Some(credentials.shared());
        };

        Box::new(Option::as_mut(&mut future_opt).unwrap().clone().then(|result| {
            match result {
                Ok(e) => Ok((*e).clone()),
                Err(e) => Err(e.to_string().into()),
            }
        }))
    }
}

/// A cache that stores entries in Google Cloud Storage
pub struct GCSCache {
    /// The GCS bucket
    bucket: Rc<Bucket>,
    /// Credential provider for GCS
    credential_provider: Option<GCSCredentialProvider>,
    /// Read-only or not
    rw_mode: RWMode,
}

impl GCSCache {
    /// Create a new `GCSCache` storing data in `bucket`
    pub fn new(bucket: String,
               credential_provider: Option<GCSCredentialProvider>,
               rw_mode: RWMode,
               handle: &Handle) -> Result<GCSCache>
    {
        Ok(GCSCache {
            bucket: Rc::new(Bucket::new(bucket, handle)?),
            rw_mode: rw_mode,
            credential_provider: credential_provider,
        })
    }
}

impl Storage for GCSCache {
    fn get(&self, key: &str) -> SFuture<Cache> {
        Box::new(self.bucket.get(&key, &self.credential_provider).then(|result| {
            match result {
                Ok(data) => {
                    let hit = CacheRead::from(io::Cursor::new(data))?;
                    Ok(Cache::Hit(hit))
                }
                Err(e) => {
                    warn!("Got GCS error: {:?}", e);
                    Ok(Cache::Miss)
                }
            }
        }))
    }

    fn put(&self, key: &str, entry: CacheWrite) -> SFuture<time::Duration> {
        if let RWMode::ReadOnly = self.rw_mode {
            return Box::new(future::ok(time::Duration::new(0, 0)));
        }

        let start = time::Instant::now();
        let data = match entry.finish() {
            Ok(data) => data,
            Err(e) => return Box::new(future::err(e.into())),
        };
        let bucket = self.bucket.clone();
        let response = bucket.put(&key, data, &self.credential_provider).chain_err(|| {
            "failed to put cache entry in GCS"
        });

        Box::new(response.map(move |_| start.elapsed()))
    }

    fn location(&self) -> String {
        format!("GCS, bucket: {}", self.bucket)
    }

    fn current_size(&self) -> Option<u64> { None }
    fn max_size(&self) -> Option<u64> { None }
}
<|endoftext|>


<commit_before>#![deny(unused)]
#![cfg_attr(test, deny(warnings))]

#[cfg(test)] extern crate hamcrest;
#[macro_use] extern crate log;
extern crate crates_io as registry;
extern crate crossbeam;
extern crate curl;
extern crate docopt;
extern crate filetime;
extern crate flate2;
extern crate fs2;
extern crate git2;
extern crate glob;
extern crate libc;
extern crate libgit2_sys;
extern crate num_cpus;
extern crate regex;
extern crate rustc_serialize;
extern crate semver;
extern crate tar;
extern crate tempdir;
extern crate term;
extern crate time;
extern crate toml;
extern crate url;

use std::env;
use std::io::prelude::*;
use std::io;
use rustc_serialize::{Decodable, Encodable};
use rustc_serialize::json::{self, Json};
use docopt::Docopt;

use core::{Shell, MultiShell, ShellConfig, Verbosity, ColorConfig};
use core::shell::Verbosity::{Verbose};
use core::shell::ColorConfig::{Auto};
use term::color::{BLACK, RED};

pub use util::{CargoError, CliError, CliResult, human, Config, ChainError};

macro_rules! bail {
    ($($fmt:tt)*) => (
        return Err(::util::human(&format_args!($($fmt)*)))
    )
}

pub mod core;
pub mod ops;
pub mod sources;
pub mod util;

pub fn execute_main<T, U, V>(
                        exec: fn(T, U, &Config) -> CliResult<Option<V>>,
                        options_first: bool,
                        usage: &str)
    where V: Encodable, T: Decodable, U: Decodable
{
    process::<V, _>(|rest, shell| {
        call_main(exec, shell, usage, rest, options_first)
    });
}

pub fn call_main<T, U, V>(
            exec: fn(T, U, &Config) -> CliResult<Option<V>>,
            shell: &Config,
            usage: &str,
            args: &[String],
            options_first: bool) -> CliResult<Option<V>>
    where V: Encodable, T: Decodable, U: Decodable
{
    let flags = try!(flags_from_args::<T>(usage, args, options_first));
    let json = try!(json_from_stdin::<U>());

    exec(flags, json, shell)
}

pub fn execute_main_without_stdin<T, V>(
                                      exec: fn(T, &Config) -> CliResult<Option<V>>,
                                      options_first: bool,
                                      usage: &str)
    where V: Encodable, T: Decodable
{
    process::<V, _>(|rest, shell| {
        call_main_without_stdin(exec, shell, usage, rest, options_first)
    });
}

pub fn call_main_without_stdin<T, V>(
            exec: fn(T, &Config) -> CliResult<Option<V>>,
            shell: &Config,
            usage: &str,
            args: &[String],
            options_first: bool) -> CliResult<Option<V>>
    where V: Encodable, T: Decodable
{
    let flags = try!(flags_from_args::<T>(usage, args, options_first));
    exec(flags, shell)
}

fn process<V, F>(mut callback: F)
    where F: FnMut(&[String], &Config) -> CliResult<Option<V>>,
          V: Encodable
{
    let mut config = None;
    let result = (|| {
        config = Some(try!(Config::default()));
        let args: Vec<_> = try!(env::args_os().map(|s| {
            s.into_string().map_err(|s| {
                human(format!("invalid unicode in argument: {:?}", s))
            })
        }).collect());
        callback(&args, config.as_ref().unwrap())
    })();
    let mut verbose_shell = shell(Verbose, Auto);
    let mut shell = config.as_ref().map(|s| s.shell());
    let shell = shell.as_mut().map(|s| &mut **s).unwrap_or(&mut verbose_shell);
    process_executed(result, shell)
}

pub fn process_executed<T>(result: CliResult<Option<T>>, shell: &mut MultiShell)
    where T: Encodable
{
    match result {
        Err(e) => handle_error(e, shell),
        Ok(Some(encodable)) => {
            let encoded = json::encode(&encodable).unwrap();
            println!("{}", encoded);
        }
        Ok(None) => {}
    }
}

pub fn shell(verbosity: Verbosity, color_config: ColorConfig) -> MultiShell {
    enum Output {
        Stdout,
        Stderr,
    }

    let tty = isatty(Output::Stderr);
    let stderr = Box::new(io::stderr());

    let config = ShellConfig { color_config: color_config, tty: tty };
    let err = Shell::create(stderr, config);

    let tty = isatty(Output::Stdout);
    let stdout = Box::new(io::stdout());

    let config = ShellConfig { color_config: color_config, tty: tty };
    let out = Shell::create(stdout, config);

    return MultiShell::new(out, err, verbosity);

    #[cfg(unix)]
    fn isatty(output: Output) -> bool {
        let fd = match output {
            Output::Stdout => libc::STDOUT_FILENO,
            Output::Stderr => libc::STDERR_FILENO,
        };

        unsafe { libc::isatty(fd) != 0 }
    }
    #[cfg(windows)]
    fn isatty(output: Output) -> bool {
        extern crate kernel32;
        extern crate winapi;

        let handle = match output {
            Output::Stdout => winapi::winbase::STD_OUTPUT_HANDLE,
            Output::Stderr => winapi::winbase::STD_ERROR_HANDLE,
        };

        unsafe {
            let handle = kernel32::GetStdHandle(handle);
            let mut out = 0;
            kernel32::GetConsoleMode(handle, &mut out) != 0
        }
    }
}

// `output` print variant error strings to either stderr or stdout.
// For fatal errors, print to stderr;
// and for others, e.g. docopt version info, print to stdout.
fn output(err: String, shell: &mut MultiShell, fatal: bool) {
    let (std_shell, color, message) = if fatal {
        (shell.err(), RED, Some("error:"))
    } else {
        (shell.out(), BLACK, None)
    };
    let _ = match message{
        Some(text) => std_shell.say_status(text, err.to_string(), color, false),
        None => std_shell.say(err, color)
    };
}

pub fn handle_error(err: CliError, shell: &mut MultiShell) {
    debug!("handle_error; err={:?}", err);

    let CliError { error, exit_code, unknown } = err;
    let fatal = exit_code != 0; // exit_code == 0 is non-fatal error

    let hide = unknown && shell.get_verbose() != Verbose;
    if hide {
        let _ = shell.err().say_status("error:", "An unknown error occurred",
                                       RED, false);
    } else {
        output(error.to_string(), shell, fatal);
    }
    if !handle_cause(&error, shell) || hide {
        let _ = shell.err().say("\nTo learn more, run the command again \
                                 with --verbose.".to_string(), BLACK);
    }

    std::process::exit(exit_code);
}

fn handle_cause(mut cargo_err: &CargoError, shell: &mut MultiShell) -> bool {
    let verbose = shell.get_verbose();
    let mut err;
    loop {
        cargo_err = match cargo_err.cargo_cause() {
            Some(cause) => cause,
            None => { err = cargo_err.cause(); break }
        };
        if verbose != Verbose && !cargo_err.is_human() { return false }
        print(cargo_err.to_string(), shell);
    }
    loop {
        let cause = match err { Some(err) => err, None => return true };
        if verbose != Verbose { return false }
        print(cause.to_string(), shell);
        err = cause.cause();
    }

    fn print(error: String, shell: &mut MultiShell) {
        let _ = shell.err().say("\nCaused by:", BLACK);
        let _ = shell.err().say(format!("  {}", error), BLACK);
    }
}

pub fn version() -> String {
    format!("cargo {}", match option_env!("CFG_VERSION") {
        Some(s) => s.to_string(),
        None => format!("{}.{}.{}{}",
                        env!("CARGO_PKG_VERSION_MAJOR"),
                        env!("CARGO_PKG_VERSION_MINOR"),
                        env!("CARGO_PKG_VERSION_PATCH"),
                        option_env!("CARGO_PKG_VERSION_PRE").unwrap_or(""))
    })
}

fn flags_from_args<T>(usage: &str, args: &[String],
                          options_first: bool) -> CliResult<T>
    where T: Decodable
{
    let docopt = Docopt::new(usage).unwrap()
                                   .options_first(options_first)
                                   .argv(args.iter().map(|s| &s[..]))
                                   .help(true);
    docopt.decode().map_err(|e| {
        let code = if e.fatal() {1} else {0};
        CliError::from_error(human(e.to_string()), code)
    })
}

fn json_from_stdin<T: Decodable>() -> CliResult<T> {
    let mut reader = io::stdin();
    let mut input = String::new();
    try!(reader.read_to_string(&mut input).map_err(|_| {
        CliError::new("Standard in did not exist or was not UTF-8", 1)
    }));

    let json = try!(Json::from_str(&input).map_err(|_| {
        CliError::new("Could not parse standard in as JSON", 1)
    }));
    let mut decoder = json::Decoder::new(json);

    Decodable::decode(&mut decoder).map_err(|_| {
        CliError::new("Could not process standard in as input", 1)
    })
}
<commit_msg>Simplify error printing<commit_after>#![deny(unused)]
#![cfg_attr(test, deny(warnings))]

#[cfg(test)] extern crate hamcrest;
#[macro_use] extern crate log;
extern crate crates_io as registry;
extern crate crossbeam;
extern crate curl;
extern crate docopt;
extern crate filetime;
extern crate flate2;
extern crate fs2;
extern crate git2;
extern crate glob;
extern crate libc;
extern crate libgit2_sys;
extern crate num_cpus;
extern crate regex;
extern crate rustc_serialize;
extern crate semver;
extern crate tar;
extern crate tempdir;
extern crate term;
extern crate time;
extern crate toml;
extern crate url;

use std::env;
use std::io::prelude::*;
use std::io;
use rustc_serialize::{Decodable, Encodable};
use rustc_serialize::json::{self, Json};
use docopt::Docopt;

use core::{Shell, MultiShell, ShellConfig, Verbosity, ColorConfig};
use core::shell::Verbosity::{Verbose};
use core::shell::ColorConfig::{Auto};
use term::color::{BLACK};

pub use util::{CargoError, CliError, CliResult, human, Config, ChainError};

macro_rules! bail {
    ($($fmt:tt)*) => (
        return Err(::util::human(&format_args!($($fmt)*)))
    )
}

pub mod core;
pub mod ops;
pub mod sources;
pub mod util;

pub fn execute_main<T, U, V>(
                        exec: fn(T, U, &Config) -> CliResult<Option<V>>,
                        options_first: bool,
                        usage: &str)
    where V: Encodable, T: Decodable, U: Decodable
{
    process::<V, _>(|rest, shell| {
        call_main(exec, shell, usage, rest, options_first)
    });
}

pub fn call_main<T, U, V>(
            exec: fn(T, U, &Config) -> CliResult<Option<V>>,
            shell: &Config,
            usage: &str,
            args: &[String],
            options_first: bool) -> CliResult<Option<V>>
    where V: Encodable, T: Decodable, U: Decodable
{
    let flags = try!(flags_from_args::<T>(usage, args, options_first));
    let json = try!(json_from_stdin::<U>());

    exec(flags, json, shell)
}

pub fn execute_main_without_stdin<T, V>(
                                      exec: fn(T, &Config) -> CliResult<Option<V>>,
                                      options_first: bool,
                                      usage: &str)
    where V: Encodable, T: Decodable
{
    process::<V, _>(|rest, shell| {
        call_main_without_stdin(exec, shell, usage, rest, options_first)
    });
}

pub fn call_main_without_stdin<T, V>(
            exec: fn(T, &Config) -> CliResult<Option<V>>,
            shell: &Config,
            usage: &str,
            args: &[String],
            options_first: bool) -> CliResult<Option<V>>
    where V: Encodable, T: Decodable
{
    let flags = try!(flags_from_args::<T>(usage, args, options_first));
    exec(flags, shell)
}

fn process<V, F>(mut callback: F)
    where F: FnMut(&[String], &Config) -> CliResult<Option<V>>,
          V: Encodable
{
    let mut config = None;
    let result = (|| {
        config = Some(try!(Config::default()));
        let args: Vec<_> = try!(env::args_os().map(|s| {
            s.into_string().map_err(|s| {
                human(format!("invalid unicode in argument: {:?}", s))
            })
        }).collect());
        callback(&args, config.as_ref().unwrap())
    })();
    let mut verbose_shell = shell(Verbose, Auto);
    let mut shell = config.as_ref().map(|s| s.shell());
    let shell = shell.as_mut().map(|s| &mut **s).unwrap_or(&mut verbose_shell);
    process_executed(result, shell)
}

pub fn process_executed<T>(result: CliResult<Option<T>>, shell: &mut MultiShell)
    where T: Encodable
{
    match result {
        Err(e) => handle_error(e, shell),
        Ok(Some(encodable)) => {
            let encoded = json::encode(&encodable).unwrap();
            println!("{}", encoded);
        }
        Ok(None) => {}
    }
}

pub fn shell(verbosity: Verbosity, color_config: ColorConfig) -> MultiShell {
    enum Output {
        Stdout,
        Stderr,
    }

    let tty = isatty(Output::Stderr);
    let stderr = Box::new(io::stderr());

    let config = ShellConfig { color_config: color_config, tty: tty };
    let err = Shell::create(stderr, config);

    let tty = isatty(Output::Stdout);
    let stdout = Box::new(io::stdout());

    let config = ShellConfig { color_config: color_config, tty: tty };
    let out = Shell::create(stdout, config);

    return MultiShell::new(out, err, verbosity);

    #[cfg(unix)]
    fn isatty(output: Output) -> bool {
        let fd = match output {
            Output::Stdout => libc::STDOUT_FILENO,
            Output::Stderr => libc::STDERR_FILENO,
        };

        unsafe { libc::isatty(fd) != 0 }
    }
    #[cfg(windows)]
    fn isatty(output: Output) -> bool {
        extern crate kernel32;
        extern crate winapi;

        let handle = match output {
            Output::Stdout => winapi::winbase::STD_OUTPUT_HANDLE,
            Output::Stderr => winapi::winbase::STD_ERROR_HANDLE,
        };

        unsafe {
            let handle = kernel32::GetStdHandle(handle);
            let mut out = 0;
            kernel32::GetConsoleMode(handle, &mut out) != 0
        }
    }
}

pub fn handle_error(err: CliError, shell: &mut MultiShell) {
    debug!("handle_error; err={:?}", err);

    let CliError { error, exit_code, unknown } = err;
    // exit_code == 0 is non-fatal error, e.g. docopt version info
    let fatal = exit_code != 0;

    let hide = unknown && shell.get_verbose() != Verbose;

    let _ignored_result = if hide {
        shell.error("An unknown error occurred")
    } else if fatal {
        shell.error(&error)
    } else {
        shell.say(&error, BLACK)
    };

    if !handle_cause(&error, shell) || hide {
        let _ = shell.err().say("\nTo learn more, run the command again \
                                 with --verbose.".to_string(), BLACK);
    }

    std::process::exit(exit_code);
}

fn handle_cause(mut cargo_err: &CargoError, shell: &mut MultiShell) -> bool {
    let verbose = shell.get_verbose();
    let mut err;
    loop {
        cargo_err = match cargo_err.cargo_cause() {
            Some(cause) => cause,
            None => { err = cargo_err.cause(); break }
        };
        if verbose != Verbose && !cargo_err.is_human() { return false }
        print(cargo_err.to_string(), shell);
    }
    loop {
        let cause = match err { Some(err) => err, None => return true };
        if verbose != Verbose { return false }
        print(cause.to_string(), shell);
        err = cause.cause();
    }

    fn print(error: String, shell: &mut MultiShell) {
        let _ = shell.err().say("\nCaused by:", BLACK);
        let _ = shell.err().say(format!("  {}", error), BLACK);
    }
}

pub fn version() -> String {
    format!("cargo {}", match option_env!("CFG_VERSION") {
        Some(s) => s.to_string(),
        None => format!("{}.{}.{}{}",
                        env!("CARGO_PKG_VERSION_MAJOR"),
                        env!("CARGO_PKG_VERSION_MINOR"),
                        env!("CARGO_PKG_VERSION_PATCH"),
                        option_env!("CARGO_PKG_VERSION_PRE").unwrap_or(""))
    })
}

fn flags_from_args<T>(usage: &str, args: &[String],
                          options_first: bool) -> CliResult<T>
    where T: Decodable
{
    let docopt = Docopt::new(usage).unwrap()
                                   .options_first(options_first)
                                   .argv(args.iter().map(|s| &s[..]))
                                   .help(true);
    docopt.decode().map_err(|e| {
        let code = if e.fatal() {1} else {0};
        CliError::from_error(human(e.to_string()), code)
    })
}

fn json_from_stdin<T: Decodable>() -> CliResult<T> {
    let mut reader = io::stdin();
    let mut input = String::new();
    try!(reader.read_to_string(&mut input).map_err(|_| {
        CliError::new("Standard in did not exist or was not UTF-8", 1)
    }));

    let json = try!(Json::from_str(&input).map_err(|_| {
        CliError::new("Could not parse standard in as JSON", 1)
    }));
    let mut decoder = json::Decoder::new(json);

    Decodable::decode(&mut decoder).map_err(|_| {
        CliError::new("Could not process standard in as input", 1)
    })
}
<|endoftext|>


<commit_before>//! A text character

use types::{FontSize, Scalar};
use ImageSize;

/// Holds rendered character data.
#[derive(Clone)]
pub struct Character<'a, T: 'a + ImageSize> {
    /// The offset of character.
    pub offset: [Scalar; 2],
    /// The size of character, including space.
    pub size: [Scalar; 2],
    /// The texture of the character.
    pub texture: &'a T,
}

impl<'a, T: ImageSize> Character<'a, T> {
    /// The left offset.
    pub fn left(&self) -> Scalar {
        self.offset[0]
    }

    /// The top offset.
    pub fn top(&self) -> Scalar {
        self.offset[1]
    }

    /// Gets width of character, including space to the next one.
    pub fn width(&self) -> Scalar {
        self.size[0]
    }

    /// Sets height of character, including space to the next one.
    pub fn height(&self) -> Scalar {
        self.size[1]
    }
}

/// Stores characters in a buffer and loads them by demand.
pub trait CharacterCache {
    /// The textyre type associated with the character cache.
    type Texture: ImageSize;

    /// Get reference to character.
    fn character<'a>(&'a mut self,
                     font_size: FontSize,
                     ch: char)
                     -> Character<'a, <Self as CharacterCache>::Texture>;

    /// Return the width for some given text.
    fn width(&mut self, size: FontSize, text: &str) -> ::math::Scalar {
        text.chars().fold(0.0, |a, ch| {
            let character = self.character(size, ch);
            a + character.width()
        })
    }
}
<commit_msg>Fix typo<commit_after>//! A text character

use types::{FontSize, Scalar};
use ImageSize;

/// Holds rendered character data.
#[derive(Clone)]
pub struct Character<'a, T: 'a + ImageSize> {
    /// The offset of character.
    pub offset: [Scalar; 2],
    /// The size of character, including space.
    pub size: [Scalar; 2],
    /// The texture of the character.
    pub texture: &'a T,
}

impl<'a, T: ImageSize> Character<'a, T> {
    /// The left offset.
    pub fn left(&self) -> Scalar {
        self.offset[0]
    }

    /// The top offset.
    pub fn top(&self) -> Scalar {
        self.offset[1]
    }

    /// Gets width of character, including space to the next one.
    pub fn width(&self) -> Scalar {
        self.size[0]
    }

    /// Sets height of character, including space to the next one.
    pub fn height(&self) -> Scalar {
        self.size[1]
    }
}

/// Stores characters in a buffer and loads them by demand.
pub trait CharacterCache {
    /// The texture type associated with the character cache.
    type Texture: ImageSize;

    /// Get reference to character.
    fn character<'a>(&'a mut self,
                     font_size: FontSize,
                     ch: char)
                     -> Character<'a, <Self as CharacterCache>::Texture>;

    /// Return the width for some given text.
    fn width(&mut self, size: FontSize, text: &str) -> ::math::Scalar {
        text.chars().fold(0.0, |a, ch| {
            let character = self.character(size, ch);
            a + character.width()
        })
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#![feature(start)]

#[cfg(target_os="android")]
extern crate libc;

extern crate servo;
extern crate time;
extern crate util;
extern crate net;

#[cfg(not(test))]
extern crate "glutin_app" as app;

#[cfg(not(test))]
extern crate compositing;

#[cfg(target_os="android")]
#[macro_use]
extern crate android_glue;

#[cfg(target_os="android")]
use libc::c_int;

#[cfg(not(test))]
use util::opts;

#[cfg(not(test))]
use net::resource_task;

#[cfg(not(test))]
use servo::Browser;
#[cfg(not(test))]
use compositing::windowing::WindowEvent;

#[cfg(target_os="android")]
use std::borrow::ToOwned;

#[cfg(not(test))]
struct BrowserWrapper {
    browser: Browser,
}

#[cfg(target_os="android")]
android_start!(main);

#[cfg(target_os="android")]
fn get_args() -> Vec<String> {
    vec![
        "servo".to_owned(),
        "http://en.wikipedia.org/wiki/Rust".to_owned()
    ]
}

#[cfg(not(target_os="android"))]
fn get_args() -> Vec<String> {
    use std::env;
    env::args().collect()
}

#[cfg(target_os="android")]
struct FilePtr(*mut libc::types::common::c95::FILE);

#[cfg(target_os="android")]
unsafe impl Send for FilePtr {}

#[cfg(target_os="android")]
fn redirect_output(file_no: c_int) {
    use libc::funcs::posix88::unistd::{pipe, dup2};
    use libc::funcs::posix88::stdio::fdopen;
    use libc::funcs::c95::stdio::fgets;
    use util::task::spawn_named;
    use std::mem;
    use std::ffi::CString;
    use std::str::from_utf8;

    unsafe {
        let mut pipes: [c_int; 2] = [ 0, 0 ];
        pipe(pipes.as_mut_ptr());
        dup2(pipes[1], file_no);
        let mode = CString::from_slice("r".as_bytes());
        let input_file = FilePtr(fdopen(pipes[0], mode.as_ptr()));
        spawn_named("android-logger".to_owned(), move || {
            loop {
                let mut read_buffer: [u8; 1024] = mem::zeroed();
                let FilePtr(input_file) = input_file;
                fgets(read_buffer.as_mut_ptr() as *mut i8, read_buffer.len() as i32, input_file);
                let cs = CString::from_slice(&read_buffer);
                match from_utf8(cs.as_bytes()) {
                    Ok(s) => android_glue::write_log(s),
                    _ => {}
                }
            }
        });
    }
}

#[cfg(target_os="android")]
fn setup_logging() {
    use libc::consts::os::posix88::{STDERR_FILENO, STDOUT_FILENO};
    //os::setenv("RUST_LOG", "servo,gfx,msg,util,layers,js,std,rt,extra");
    redirect_output(STDERR_FILENO);
    redirect_output(STDOUT_FILENO);
}

#[cfg(not(target_os="android"))]
fn setup_logging() {
}

fn main() {
    if opts::from_cmdline_args(&*get_args()) {
        setup_logging();
        resource_task::global_init();

        let window = if opts::get().headless {
            None
        } else {
            Some(app::create_window())
        };

        let mut browser = BrowserWrapper {
            browser: Browser::new(window.clone()),
        };

        match window {
            None => {}
            Some(ref window) => {
                unsafe {
                    window.set_nested_event_loop_listener(&mut browser);
                }
            }
        }

        browser.browser.handle_event(WindowEvent::InitializeCompositing);

        loop {
            let should_continue = match window {
                None => browser.browser.handle_event(WindowEvent::Idle),
                Some(ref window) => {
                    let event = window.wait_events();
                    browser.browser.handle_event(event)
                }
            };
            if !should_continue {
                break
            }
        };

        match window {
            None => {}
            Some(ref window) => {
                unsafe {
                    window.remove_nested_event_loop_listener();
                }
            }
        }

        let BrowserWrapper {
            browser
        } = browser;
        browser.shutdown();
    }
}

impl app::NestedEventLoopListener for BrowserWrapper {
    fn handle_event_from_nested_event_loop(&mut self, event: WindowEvent) -> bool {
        let is_resize = match event {
            WindowEvent::Resize(..) => true,
            _ => false,
        };
        if !self.browser.handle_event(event) {
            return false
        }
        if is_resize {
            self.browser.repaint_synchronously()
        }
        true
    }
}

<commit_msg>auto merge of #5289 : Ms2ger/servo/android-redirect, r=metajack<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#![feature(start)]

#[cfg(target_os="android")]
extern crate libc;

extern crate servo;
extern crate time;
extern crate util;
extern crate net;

#[cfg(not(test))]
extern crate "glutin_app" as app;

#[cfg(not(test))]
extern crate compositing;

#[cfg(target_os="android")]
#[macro_use]
extern crate android_glue;

#[cfg(target_os="android")]
use libc::c_int;

#[cfg(not(test))]
use util::opts;

#[cfg(not(test))]
use net::resource_task;

#[cfg(not(test))]
use servo::Browser;
#[cfg(not(test))]
use compositing::windowing::WindowEvent;

#[cfg(target_os="android")]
use std::borrow::ToOwned;

#[cfg(not(test))]
struct BrowserWrapper {
    browser: Browser,
}

#[cfg(target_os="android")]
android_start!(main);

#[cfg(target_os="android")]
fn get_args() -> Vec<String> {
    vec![
        "servo".to_owned(),
        "http://en.wikipedia.org/wiki/Rust".to_owned()
    ]
}

#[cfg(not(target_os="android"))]
fn get_args() -> Vec<String> {
    use std::env;
    env::args().collect()
}

#[cfg(target_os="android")]
struct FilePtr(*mut libc::types::common::c95::FILE);

#[cfg(target_os="android")]
unsafe impl Send for FilePtr {}

#[cfg(target_os="android")]
fn redirect_output(file_no: c_int) {
    use libc::funcs::posix88::unistd::{pipe, dup2};
    use libc::funcs::posix88::stdio::fdopen;
    use libc::funcs::c95::stdio::fgets;
    use util::task::spawn_named;
    use std::mem;
    use std::ffi::CString;
    use std::str::from_utf8;

    unsafe {
        let mut pipes: [c_int; 2] = [ 0, 0 ];
        pipe(pipes.as_mut_ptr());
        dup2(pipes[1], file_no);
        let mode = CString::from_slice("r".as_bytes());
        let input_file = FilePtr(fdopen(pipes[0], mode.as_ptr()));
        spawn_named("android-logger".to_owned(), move || {
            loop {
                let mut read_buffer: [u8; 1024] = mem::zeroed();
                let FilePtr(input_file) = input_file;
                fgets(read_buffer.as_mut_ptr() as *mut i8, read_buffer.len() as i32, input_file);
                match from_utf8(&read_buffer) {
                    Ok(s) => android_glue::write_log(s.trim_right_matches('\0')),
                    _ => {}
                }
            }
        });
    }
}

#[cfg(target_os="android")]
fn setup_logging() {
    use libc::consts::os::posix88::{STDERR_FILENO, STDOUT_FILENO};
    //os::setenv("RUST_LOG", "servo,gfx,msg,util,layers,js,std,rt,extra");
    redirect_output(STDERR_FILENO);
    redirect_output(STDOUT_FILENO);
}

#[cfg(not(target_os="android"))]
fn setup_logging() {
}

fn main() {
    if opts::from_cmdline_args(&*get_args()) {
        setup_logging();
        resource_task::global_init();

        let window = if opts::get().headless {
            None
        } else {
            Some(app::create_window())
        };

        let mut browser = BrowserWrapper {
            browser: Browser::new(window.clone()),
        };

        match window {
            None => {}
            Some(ref window) => {
                unsafe {
                    window.set_nested_event_loop_listener(&mut browser);
                }
            }
        }

        browser.browser.handle_event(WindowEvent::InitializeCompositing);

        loop {
            let should_continue = match window {
                None => browser.browser.handle_event(WindowEvent::Idle),
                Some(ref window) => {
                    let event = window.wait_events();
                    browser.browser.handle_event(event)
                }
            };
            if !should_continue {
                break
            }
        };

        match window {
            None => {}
            Some(ref window) => {
                unsafe {
                    window.remove_nested_event_loop_listener();
                }
            }
        }

        let BrowserWrapper {
            browser
        } = browser;
        browser.shutdown();
    }
}

impl app::NestedEventLoopListener for BrowserWrapper {
    fn handle_event_from_nested_event_loop(&mut self, event: WindowEvent) -> bool {
        let is_resize = match event {
            WindowEvent::Resize(..) => true,
            _ => false,
        };
        if !self.browser.handle_event(event) {
            return false
        }
        if is_resize {
            self.browser.repaint_synchronously()
        }
        true
    }
}

<|endoftext|>


<commit_before>/* automatically generated by rust-bindgen */

#[repr(C)]
#[deriving(Copy)]
pub struct Struct_zmq_msg_t {
    pub unnamed_field1: [::libc::c_uchar, ..32u],
}
pub type zmq_msg_t = Struct_zmq_msg_t;
pub type zmq_free_fn = ::libc::c_void;
#[repr(C)]
#[deriving(Copy)]
pub struct Struct_Unnamed1 {
    pub event: uint16_t,
    pub value: int32_t,
}
pub type zmq_event_t = Struct_Unnamed1;
pub enum Struct_iovec { }
#[repr(C)]
#[deriving(Copy)]
pub struct Struct_Unnamed2 {
    pub socket: *mut ::libc::c_void,
    pub fd: ::libc::c_int,
    pub events: ::libc::c_short,
    pub revents: ::libc::c_short,
}
pub type zmq_pollitem_t = Struct_Unnamed2;
#[link(name = "zmq")]
extern "C" { }
#[link(name = "zmq")]
extern "C" {
    pub fn zmq_version(major: *mut ::libc::c_int, minor: *mut ::libc::c_int,
                       patch: *mut ::libc::c_int);
    pub fn zmq_errno() -> ::libc::c_int;
    pub fn zmq_strerror(errnum: ::libc::c_int) -> *const ::libc::c_char;
    pub fn zmq_ctx_new() -> *mut ::libc::c_void;
    pub fn zmq_ctx_term(context: *mut ::libc::c_void) -> ::libc::c_int;
    pub fn zmq_ctx_shutdown(ctx_: *mut ::libc::c_void) -> ::libc::c_int;
    pub fn zmq_ctx_set(context: *mut ::libc::c_void, option: ::libc::c_int,
                       optval: ::libc::c_int) -> ::libc::c_int;
    pub fn zmq_ctx_get(context: *mut ::libc::c_void, option: ::libc::c_int)
     -> ::libc::c_int;
    pub fn zmq_init(io_threads: ::libc::c_int) -> *mut ::libc::c_void;
    pub fn zmq_term(context: *mut ::libc::c_void) -> ::libc::c_int;
    pub fn zmq_ctx_destroy(context: *mut ::libc::c_void) -> ::libc::c_int;
    pub fn zmq_msg_init(msg: *mut zmq_msg_t) -> ::libc::c_int;
    pub fn zmq_msg_init_size(msg: *mut zmq_msg_t, size: size_t)
     -> ::libc::c_int;
    pub fn zmq_msg_init_data(msg: *mut zmq_msg_t, data: *mut ::libc::c_void,
                             size: size_t, ffn: *mut zmq_free_fn,
                             hint: *mut ::libc::c_void) -> ::libc::c_int;
    pub fn zmq_msg_send(msg: *mut zmq_msg_t, s: *mut ::libc::c_void,
                        flags: ::libc::c_int) -> ::libc::c_int;
    pub fn zmq_msg_recv(msg: *mut zmq_msg_t, s: *mut ::libc::c_void,
                        flags: ::libc::c_int) -> ::libc::c_int;
    pub fn zmq_msg_close(msg: *mut zmq_msg_t) -> ::libc::c_int;
    pub fn zmq_msg_move(dest: *mut zmq_msg_t, src: *mut zmq_msg_t)
     -> ::libc::c_int;
    pub fn zmq_msg_copy(dest: *mut zmq_msg_t, src: *mut zmq_msg_t)
     -> ::libc::c_int;
    pub fn zmq_msg_data(msg: *mut zmq_msg_t) -> *mut ::libc::c_void;
    pub fn zmq_msg_size(msg: *mut zmq_msg_t) -> size_t;
    pub fn zmq_msg_more(msg: *mut zmq_msg_t) -> ::libc::c_int;
    pub fn zmq_msg_get(msg: *mut zmq_msg_t, option: ::libc::c_int)
     -> ::libc::c_int;
    pub fn zmq_msg_set(msg: *mut zmq_msg_t, option: ::libc::c_int,
                       optval: ::libc::c_int) -> ::libc::c_int;
    pub fn zmq_socket(arg1: *mut ::libc::c_void, _type: ::libc::c_int)
     -> *mut ::libc::c_void;
    pub fn zmq_close(s: *mut ::libc::c_void) -> ::libc::c_int;
    pub fn zmq_setsockopt(s: *mut ::libc::c_void, option: ::libc::c_int,
                          optval: *const ::libc::c_void, optvallen: size_t)
     -> ::libc::c_int;
    pub fn zmq_getsockopt(s: *mut ::libc::c_void, option: ::libc::c_int,
                          optval: *mut ::libc::c_void, optvallen: *mut size_t)
     -> ::libc::c_int;
    pub fn zmq_bind(s: *mut ::libc::c_void, addr: *const ::libc::c_char)
     -> ::libc::c_int;
    pub fn zmq_connect(s: *mut ::libc::c_void, addr: *const ::libc::c_char)
     -> ::libc::c_int;
    pub fn zmq_unbind(s: *mut ::libc::c_void, addr: *const ::libc::c_char)
     -> ::libc::c_int;
    pub fn zmq_disconnect(s: *mut ::libc::c_void, addr: *const ::libc::c_char)
     -> ::libc::c_int;
    pub fn zmq_send(s: *mut ::libc::c_void, buf: *const ::libc::c_void,
                    len: size_t, flags: ::libc::c_int) -> ::libc::c_int;
    pub fn zmq_send_const(s: *mut ::libc::c_void, buf: *const ::libc::c_void,
                          len: size_t, flags: ::libc::c_int) -> ::libc::c_int;
    pub fn zmq_recv(s: *mut ::libc::c_void, buf: *mut ::libc::c_void,
                    len: size_t, flags: ::libc::c_int) -> ::libc::c_int;
    pub fn zmq_socket_monitor(s: *mut ::libc::c_void,
                              addr: *const ::libc::c_char,
                              events: ::libc::c_int) -> ::libc::c_int;
    pub fn zmq_sendmsg(s: *mut ::libc::c_void, msg: *mut zmq_msg_t,
                       flags: ::libc::c_int) -> ::libc::c_int;
    pub fn zmq_recvmsg(s: *mut ::libc::c_void, msg: *mut zmq_msg_t,
                       flags: ::libc::c_int) -> ::libc::c_int;
    pub fn zmq_sendiov(s: *mut ::libc::c_void, iov: *mut Struct_iovec,
                       count: size_t, flags: ::libc::c_int) -> ::libc::c_int;
    pub fn zmq_recviov(s: *mut ::libc::c_void, iov: *mut Struct_iovec,
                       count: *mut size_t, flags: ::libc::c_int)
     -> ::libc::c_int;
    pub fn zmq_poll(items: *mut zmq_pollitem_t, nitems: ::libc::c_int,
                    timeout: ::libc::c_long) -> ::libc::c_int;
    pub fn zmq_proxy(frontend: *mut ::libc::c_void,
                     backend: *mut ::libc::c_void,
                     capture: *mut ::libc::c_void) -> ::libc::c_int;
    pub fn zmq_proxy_steerable(frontend: *mut ::libc::c_void,
                               backend: *mut ::libc::c_void,
                               capture: *mut ::libc::c_void,
                               control: *mut ::libc::c_void) -> ::libc::c_int;
    pub fn zmq_z85_encode(dest: *mut ::libc::c_char, data: *mut uint8_t,
                          size: size_t) -> *mut ::libc::c_char;
    pub fn zmq_z85_decode(dest: *mut uint8_t, string: *mut ::libc::c_char)
     -> *mut uint8_t;
    pub fn zmq_device(_type: ::libc::c_int, frontend: *mut ::libc::c_void,
                      backend: *mut ::libc::c_void) -> ::libc::c_int;
}
<commit_msg>More update for rust-nightly<commit_after>/* automatically generated by rust-bindgen */

#[repr(C)]
#[derive(Copy)]
pub struct Struct_zmq_msg_t {
    pub unnamed_field1: [::libc::c_uchar; 32u],
}
pub type zmq_msg_t = Struct_zmq_msg_t;
pub type zmq_free_fn = ::libc::c_void;
#[repr(C)]
#[derive(Copy)]
pub struct Struct_Unnamed1 {
    pub event: uint16_t,
    pub value: int32_t,
}
pub type zmq_event_t = Struct_Unnamed1;
pub enum Struct_iovec { }
#[repr(C)]
#[derive(Copy)]
pub struct Struct_Unnamed2 {
    pub socket: *mut ::libc::c_void,
    pub fd: ::libc::c_int,
    pub events: ::libc::c_short,
    pub revents: ::libc::c_short,
}
pub type zmq_pollitem_t = Struct_Unnamed2;
#[link(name = "zmq")]
extern "C" { }
#[link(name = "zmq")]
extern "C" {
    pub fn zmq_version(major: *mut ::libc::c_int, minor: *mut ::libc::c_int,
                       patch: *mut ::libc::c_int);
    pub fn zmq_errno() -> ::libc::c_int;
    pub fn zmq_strerror(errnum: ::libc::c_int) -> *const ::libc::c_char;
    pub fn zmq_ctx_new() -> *mut ::libc::c_void;
    pub fn zmq_ctx_term(context: *mut ::libc::c_void) -> ::libc::c_int;
    pub fn zmq_ctx_shutdown(ctx_: *mut ::libc::c_void) -> ::libc::c_int;
    pub fn zmq_ctx_set(context: *mut ::libc::c_void, option: ::libc::c_int,
                       optval: ::libc::c_int) -> ::libc::c_int;
    pub fn zmq_ctx_get(context: *mut ::libc::c_void, option: ::libc::c_int)
     -> ::libc::c_int;
    pub fn zmq_init(io_threads: ::libc::c_int) -> *mut ::libc::c_void;
    pub fn zmq_term(context: *mut ::libc::c_void) -> ::libc::c_int;
    pub fn zmq_ctx_destroy(context: *mut ::libc::c_void) -> ::libc::c_int;
    pub fn zmq_msg_init(msg: *mut zmq_msg_t) -> ::libc::c_int;
    pub fn zmq_msg_init_size(msg: *mut zmq_msg_t, size: size_t)
     -> ::libc::c_int;
    pub fn zmq_msg_init_data(msg: *mut zmq_msg_t, data: *mut ::libc::c_void,
                             size: size_t, ffn: *mut zmq_free_fn,
                             hint: *mut ::libc::c_void) -> ::libc::c_int;
    pub fn zmq_msg_send(msg: *mut zmq_msg_t, s: *mut ::libc::c_void,
                        flags: ::libc::c_int) -> ::libc::c_int;
    pub fn zmq_msg_recv(msg: *mut zmq_msg_t, s: *mut ::libc::c_void,
                        flags: ::libc::c_int) -> ::libc::c_int;
    pub fn zmq_msg_close(msg: *mut zmq_msg_t) -> ::libc::c_int;
    pub fn zmq_msg_move(dest: *mut zmq_msg_t, src: *mut zmq_msg_t)
     -> ::libc::c_int;
    pub fn zmq_msg_copy(dest: *mut zmq_msg_t, src: *mut zmq_msg_t)
     -> ::libc::c_int;
    pub fn zmq_msg_data(msg: *mut zmq_msg_t) -> *mut ::libc::c_void;
    pub fn zmq_msg_size(msg: *mut zmq_msg_t) -> size_t;
    pub fn zmq_msg_more(msg: *mut zmq_msg_t) -> ::libc::c_int;
    pub fn zmq_msg_get(msg: *mut zmq_msg_t, option: ::libc::c_int)
     -> ::libc::c_int;
    pub fn zmq_msg_set(msg: *mut zmq_msg_t, option: ::libc::c_int,
                       optval: ::libc::c_int) -> ::libc::c_int;
    pub fn zmq_socket(arg1: *mut ::libc::c_void, _type: ::libc::c_int)
     -> *mut ::libc::c_void;
    pub fn zmq_close(s: *mut ::libc::c_void) -> ::libc::c_int;
    pub fn zmq_setsockopt(s: *mut ::libc::c_void, option: ::libc::c_int,
                          optval: *const ::libc::c_void, optvallen: size_t)
     -> ::libc::c_int;
    pub fn zmq_getsockopt(s: *mut ::libc::c_void, option: ::libc::c_int,
                          optval: *mut ::libc::c_void, optvallen: *mut size_t)
     -> ::libc::c_int;
    pub fn zmq_bind(s: *mut ::libc::c_void, addr: *const ::libc::c_char)
     -> ::libc::c_int;
    pub fn zmq_connect(s: *mut ::libc::c_void, addr: *const ::libc::c_char)
     -> ::libc::c_int;
    pub fn zmq_unbind(s: *mut ::libc::c_void, addr: *const ::libc::c_char)
     -> ::libc::c_int;
    pub fn zmq_disconnect(s: *mut ::libc::c_void, addr: *const ::libc::c_char)
     -> ::libc::c_int;
    pub fn zmq_send(s: *mut ::libc::c_void, buf: *const ::libc::c_void,
                    len: size_t, flags: ::libc::c_int) -> ::libc::c_int;
    pub fn zmq_send_const(s: *mut ::libc::c_void, buf: *const ::libc::c_void,
                          len: size_t, flags: ::libc::c_int) -> ::libc::c_int;
    pub fn zmq_recv(s: *mut ::libc::c_void, buf: *mut ::libc::c_void,
                    len: size_t, flags: ::libc::c_int) -> ::libc::c_int;
    pub fn zmq_socket_monitor(s: *mut ::libc::c_void,
                              addr: *const ::libc::c_char,
                              events: ::libc::c_int) -> ::libc::c_int;
    pub fn zmq_sendmsg(s: *mut ::libc::c_void, msg: *mut zmq_msg_t,
                       flags: ::libc::c_int) -> ::libc::c_int;
    pub fn zmq_recvmsg(s: *mut ::libc::c_void, msg: *mut zmq_msg_t,
                       flags: ::libc::c_int) -> ::libc::c_int;
    pub fn zmq_sendiov(s: *mut ::libc::c_void, iov: *mut Struct_iovec,
                       count: size_t, flags: ::libc::c_int) -> ::libc::c_int;
    pub fn zmq_recviov(s: *mut ::libc::c_void, iov: *mut Struct_iovec,
                       count: *mut size_t, flags: ::libc::c_int)
     -> ::libc::c_int;
    pub fn zmq_poll(items: *mut zmq_pollitem_t, nitems: ::libc::c_int,
                    timeout: ::libc::c_long) -> ::libc::c_int;
    pub fn zmq_proxy(frontend: *mut ::libc::c_void,
                     backend: *mut ::libc::c_void,
                     capture: *mut ::libc::c_void) -> ::libc::c_int;
    pub fn zmq_proxy_steerable(frontend: *mut ::libc::c_void,
                               backend: *mut ::libc::c_void,
                               capture: *mut ::libc::c_void,
                               control: *mut ::libc::c_void) -> ::libc::c_int;
    pub fn zmq_z85_encode(dest: *mut ::libc::c_char, data: *mut uint8_t,
                          size: size_t) -> *mut ::libc::c_char;
    pub fn zmq_z85_decode(dest: *mut uint8_t, string: *mut ::libc::c_char)
     -> *mut uint8_t;
    pub fn zmq_device(_type: ::libc::c_int, frontend: *mut ::libc::c_void,
                      backend: *mut ::libc::c_void) -> ::libc::c_int;
}
<|endoftext|>


<commit_before>use super::validator_record::ValidatorRecord;
use super::utils::types::Bitfield;
use super::utils::bls::{ AggregateSignature, PublicKey };
use super::crystallized_state::CrystallizedState;
use super::active_state::ActiveState;
use super::config::Config;
use super::shuffling::get_shuffling;

// For a given state set and skip_count, return a proposer and set
// of attestors.
pub fn get_attesters_and_proposer(
    cry_state: &CrystallizedState,
    act_state: &ActiveState,
    skip_count: &u64,
    config: &Config)
    -> (Vec<usize>, usize)
{
    let active_validator_count = cry_state.num_active_validators();
    assert!(active_validator_count >= 2, "must be >=2 active validators");
    let shuffled_validator_indicies = get_shuffling(
        &act_state.randao,
        &active_validator_count,
        config);
    let proposer_count: usize = 1;
    let ideal_validator_count: usize = (config.attester_count as usize)
        + (*skip_count as usize) + proposer_count;
    assert!(ideal_validator_count >= 2,
            "ideal_validator_count must be >=2");
    if ideal_validator_count > active_validator_count {
        return (
            shuffled_validator_indicies[0..active_validator_count - 1].to_vec(),
            shuffled_validator_indicies[active_validator_count - 1]);
    } else {
        return (
            shuffled_validator_indicies[0..ideal_validator_count - 1].to_vec(),
            shuffled_validator_indicies[ideal_validator_count - 1]);
    }
}

#[allow(unused_variables)]
pub fn process_attestations(
    validators: &Vec<ValidatorRecord>,
    attestation_indicies: &Vec<usize>,
    attestation_bitfield: &Bitfield,
    msg: &Vec<u8>,
    aggregate_sig: &AggregateSignature)
    -> Option<Vec<usize>>
{
    let mut key_msg_tuples: Vec<(&PublicKey, &[u8])> = vec![];
    let mut attesters: Vec<usize> = vec![];

    assert_eq!(attestation_indicies.len(), attestation_bitfield.len());
    for (bitfield_bit, validators_index) in attestation_indicies.iter().enumerate() {
        if attestation_bitfield.get_bit(&bitfield_bit) {
            key_msg_tuples.push(
                (&validators[*validators_index].pubkey,
                &msg)
                );
            attesters.push(*validators_index);
        }
    }
    // TODO: figure out why this assert exists in the Python impl.
    assert!(attesters.len() <= 128, "Max attesters is 128.");

    /*
    // TODO: ensure signature verification actually takes place.
    // It is completely bypassed here.
    match aggregate_sig.verify(&key_msg_tuples) {
        false => None,
        true => Some(attesters)
    }
    */
    Some(attesters)
}

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

    #[test]
    fn test_attester_and_proposer_selection() {
        let mut cry_state = CrystallizedState::zero();

        (0..10).for_each(
            |_| cry_state.active_validators.push(
                ValidatorRecord::zero_with_thread_rand_pub_key()));

        let act_state = ActiveState::zero();
        let (attestors, proposer) = get_attesters_and_proposer(
            &cry_state,
            &act_state,
            &0,
            &Config::standard());
        assert_eq!(attestors, [0, 9, 7, 6, 4, 1, 8, 5, 2]);
        assert_eq!(proposer, 3);
    }

    #[test]
    #[should_panic(expected = "must be >=2 active validators")]
    fn test_attester_and_proposer_selection_with_zero_active_validators() {
        let mut cry_state = CrystallizedState::zero();
        cry_state.active_validators = Vec::new();
        let act_state = ActiveState::zero();
        let (_attestors, _proposer) = get_attesters_and_proposer(
            &cry_state,
            &act_state,
            &0,
            &Config::standard());
    }

    #[test]
    fn test_attestation_processing() {
        let validator_count = 10;
        let mut validators: Vec<ValidatorRecord> = vec![];
        let mut attestation_indicies: Vec<usize> = vec![];
        let mut bitfield = Bitfield::new();
        let mut agg_sig = AggregateSignature::new();
        let msg = "Message that's longer than 16 chars".as_bytes();

        for i in 0..validator_count {
            let (v, keypair) =
                ValidatorRecord::zero_with_thread_rand_keypair();
            validators.push(v);
            attestation_indicies.push(i);
            bitfield.set_bit(&i, &true);
            let sig = keypair.sign(&msg);
            agg_sig.aggregate(&sig);
        }

        let result = process_attestations(
            &validators,
            &attestation_indicies,
            &bitfield,
            &msg.to_vec(),
            &agg_sig);

        match result {
            None => panic!("Verification failed."),
            Some(x) => println!("{:?}", x)
        };
    }
}

<commit_msg>Implement process_recent_attesters()<commit_after>use super::validator_record::ValidatorRecord;
use super::utils::types::Bitfield;
use super::utils::bls::{ AggregateSignature, PublicKey };
use super::crystallized_state::CrystallizedState;
use super::active_state::ActiveState;
use super::config::Config;
use super::shuffling::get_shuffling;

pub fn process_recent_attesters(
    cry_state: &CrystallizedState,
    recent_attesters: &Vec<usize>,
    config: &Config)
    -> Vec<i64>
{
    let mut deltas: Vec<i64> = vec![0; cry_state.num_active_validators()];
    for v in recent_attesters {
        deltas[*v] += config.attester_reward;
    }
    deltas
}

// For a given state set and skip_count, return a proposer and set
// of attestors.
pub fn get_attesters_and_proposer(
    cry_state: &CrystallizedState,
    act_state: &ActiveState,
    skip_count: &u64,
    config: &Config)
    -> (Vec<usize>, usize)
{
    let active_validator_count = cry_state.num_active_validators();
    assert!(active_validator_count >= 2, "must be >=2 active validators");
    let shuffled_validator_indicies = get_shuffling(
        &act_state.randao,
        &active_validator_count,
        config);
    let proposer_count: usize = 1;
    let ideal_validator_count: usize = (config.attester_count as usize)
        + (*skip_count as usize) + proposer_count;
    assert!(ideal_validator_count >= 2,
            "ideal_validator_count must be >=2");
    if ideal_validator_count > active_validator_count {
        return (
            shuffled_validator_indicies[0..active_validator_count - 1].to_vec(),
            shuffled_validator_indicies[active_validator_count - 1]);
    } else {
        return (
            shuffled_validator_indicies[0..ideal_validator_count - 1].to_vec(),
            shuffled_validator_indicies[ideal_validator_count - 1]);
    }
}

#[allow(unused_variables)]
pub fn process_attestations(
    validators: &Vec<ValidatorRecord>,
    attestation_indicies: &Vec<usize>,
    attestation_bitfield: &Bitfield,
    msg: &Vec<u8>,
    aggregate_sig: &AggregateSignature)
    -> Option<Vec<usize>>
{
    let mut key_msg_tuples: Vec<(&PublicKey, &[u8])> = vec![];
    let mut attesters: Vec<usize> = vec![];

    assert_eq!(attestation_indicies.len(), attestation_bitfield.len());
    for (bitfield_bit, validators_index) in attestation_indicies.iter().enumerate() {
        if attestation_bitfield.get_bit(&bitfield_bit) {
            key_msg_tuples.push(
                (&validators[*validators_index].pubkey,
                &msg)
                );
            attesters.push(*validators_index);
        }
    }
    // TODO: figure out why this assert exists in the Python impl.
    assert!(attesters.len() <= 128, "Max attesters is 128.");

    /*
    // TODO: ensure signature verification actually takes place.
    // It is completely bypassed here.
    match aggregate_sig.verify(&key_msg_tuples) {
        false => None,
        true => Some(attesters)
    }
    */
    Some(attesters)
}

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

    #[test]
    fn test_process_recent_attesters() {
        let mut cry_state = CrystallizedState::zero();
        let mut config = Config::standard();
        let validator_count = 20;

        config.attester_reward = 12;

        let mut recent_attesters: Vec<usize> = vec![];

        for i in 0..validator_count {
            cry_state.active_validators
                .push(ValidatorRecord::zero_with_thread_rand_pub_key());
            if i % 2 == 0 {
                recent_attesters.push(i);
            }
        }

        let d = process_recent_attesters(
            &cry_state,
            &recent_attesters,
            &config);

        for i in 0..validator_count {
            if i % 2 == 0 {
                assert_eq!(d[i], config.attester_reward);
            } else {
                assert_eq!(d[i], 0);
            }
        }
    }

    #[test]
    fn test_attester_and_proposer_selection() {
        let mut cry_state = CrystallizedState::zero();

        (0..10).for_each(
            |_| cry_state.active_validators.push(
                ValidatorRecord::zero_with_thread_rand_pub_key()));

        let act_state = ActiveState::zero();
        let (attestors, proposer) = get_attesters_and_proposer(
            &cry_state,
            &act_state,
            &0,
            &Config::standard());
        assert_eq!(attestors, [0, 9, 7, 6, 4, 1, 8, 5, 2]);
        assert_eq!(proposer, 3);
    }

    #[test]
    #[should_panic(expected = "must be >=2 active validators")]
    fn test_attester_and_proposer_selection_with_zero_active_validators() {
        let mut cry_state = CrystallizedState::zero();
        cry_state.active_validators = Vec::new();
        let act_state = ActiveState::zero();
        let (_attestors, _proposer) = get_attesters_and_proposer(
            &cry_state,
            &act_state,
            &0,
            &Config::standard());
    }

    #[test]
    fn test_attestation_processing() {
        let validator_count = 10;
        let mut validators: Vec<ValidatorRecord> = vec![];
        let mut attestation_indicies: Vec<usize> = vec![];
        let mut bitfield = Bitfield::new();
        let mut agg_sig = AggregateSignature::new();
        let msg = "Message that's longer than 16 chars".as_bytes();

        for i in 0..validator_count {
            let (v, keypair) =
                ValidatorRecord::zero_with_thread_rand_keypair();
            validators.push(v);
            attestation_indicies.push(i);
            bitfield.set_bit(&i, &true);
            let sig = keypair.sign(&msg);
            agg_sig.aggregate(&sig);
        }

        let result = process_attestations(
            &validators,
            &attestation_indicies,
            &bitfield,
            &msg.to_vec(),
            &agg_sig);

        match result {
            None => panic!("Verification failed."),
            Some(x) => println!("{:?}", x)
        };
    }
}

<|endoftext|>


<commit_before>#[cfg(feature = "trace_log")]
extern crate env_logger;

extern crate radeco_lib;
extern crate r2pipe;
extern crate r2api;
extern crate base64;
extern crate rustyline;

use r2pipe::{R2, R2Pipe};
use radeco_lib::analysis::cse::cse::CSE;
use radeco_lib::analysis::interproc::fixcall::CallFixer;
use radeco_lib::analysis::sccp;
use radeco_lib::backend::lang_c::c_cfg::CCFGVerifier;
use radeco_lib::backend::lang_c::c_cfg::ctrl_flow_struct;
use radeco_lib::backend::lang_c::c_cfg_builder;
use radeco_lib::frontend::radeco_containers::*;
use radeco_lib::middle::{dce, dot};
use radeco_lib::middle::ir_writer;
use radeco_lib::middle::regfile::SubRegisterFile;
use radeco_lib::middle::ssa::ssastorage::SSAStorage;
use radeco_lib::middle::ssa::verifier;
use rustyline::Editor;

use rustyline::error::ReadlineError;
use std::cell::RefCell;
use std::collections::HashMap;
use std::env;

use std::fs;
use std::process;
use std::rc::Rc;
use std::str;
use std::sync::Arc;

mod scheme {
    pub const HTTP: &'static str = "http://";
    pub const TCP: &'static str = "tcp://";
}

// On unix platforms you can use ANSI escape sequences
#[cfg(unix)]
const PROMPT: &'static str = "\x1b[1;32m>>\x1b[0m ";

// Windows consoles typically don't support ANSI escape sequences out
// of the box
#[cfg(windows)]
const PROMPT: &'static str = ">> ";

fn main() {
    let mut rl = Editor::<()>::new();
    let mut proj = env::args().nth(1).and_then(|ref s| {
        if is_http(s) {
            load_proj_http(&s[scheme::HTTP.len()..])
        } else if is_tcp(s) {
            load_proj_tcp(&s[scheme::TCP.len()..])
        } else if is_file(s) {
            Ok(load_proj_by_path(s))
        } else {
            Err("Invalid argument")
        }.ok()
    });
    loop {
        let readline = rl.readline(PROMPT);
        match readline {
            Ok(line) => {
                let mut terms = line.split_whitespace();
                let o1 = terms.next();
                let o2 = terms.next();
                cmd(o1, o2, &mut proj);
            }
            Err(ReadlineError::Interrupted) |
            Err(ReadlineError::Eof) => break,
            Err(err) => {
                println!("Error: {:?}", err);
                break;
            }
        }
    }
}

mod command {
    pub const LOAD: &'static str = "load";
    pub const CONNECT: &'static str = "connect";
    pub const FNLIST: &'static str = "fn_list";
    pub const ANALYZE: &'static str = "analyze";
    pub const DOT: &'static str = "dot";
    pub const IR: &'static str = "ir";
    pub const DECOMPILE: &'static str = "decompile";
}

fn cmd(op1: Option<&str>, op2: Option<&str>, proj_opt: &mut Option<RadecoProject>) {
    match (op1, op2) {
        (Some(command::LOAD), Some(path)) => {
            if is_file(path) {
                *proj_opt = Some(load_proj_by_path(path));
                return;
            } else {
                println!("{} is not found.", path);
                return;
            }
        }
        (Some(command::CONNECT), Some(url)) => {
            let p_opt = if is_http(&url) {
                load_proj_http(&url[7..])
            } else if is_tcp(&url) {
                load_proj_tcp(&url[6..])
            } else {
                Err("Invalid url")
            };
            match p_opt {
                Ok(proj) => *proj_opt = Some(proj),
                Err(msg) => println!("{}", msg),
            }
            return;
        }
        _ => {}
    };
    let mut proj = match proj_opt {
        Some(proj) => proj,
        None => {
            println!("Load a project first");
            return;
        }
    };
    match (op1, op2) {
        (Some(command::ANALYZE), Some("*")) => {
            let rfns = proj.iter_mut().map(|i| i.module).flat_map(|rmod| {
                rmod.functions.values_mut()
            });
            for rfn in rfns {
                analyze(rfn);
            }
        }
        (Some(command::FNLIST), _) => {
            let funcs = fn_list(&proj);
            println!("{}", funcs.join("\n"));
        }
        // TODO Show list of dependency information of analyses
        // TODO Add command for individual analyses
        (Some(command::ANALYZE), Some(f)) => {
            if let Some(rfn) = get_function_mut(f, &mut proj) {
                analyze(rfn);
            } else {
                println!("{} is not found", f);
            }
        }
        (Some(command::DOT), Some(f)) => {
            if let Some(rfn) = get_function(f, &proj) {
                println!("{}", emit_dot(rfn.ssa()));
            } else {
                println!("{} is not found", f);
            }
        }
        (Some(command::IR), Some(f)) => {
            if let Some(rfn) = get_function(f, &proj) {
                println!("{}", emit_ir(rfn));
            } else {
                println!("{} is not found", f);
            }
        }
        (Some(command::DECOMPILE), Some(f)) => {
            if let Some(rfn) = get_function(f, &proj) {
                let rmod = proj.iter().map(|i| i.module).next().unwrap();
                let func_name_map = rmod.functions
                    .iter()
                    .map(|(&addr, f)| (addr, f.name.to_string()))
                    .collect();
                let strings = rmod.strings()
                    .iter()
                    .filter(|ref s| s.paddr.is_some() && s.string.is_some())
                    .cloned()
                    .map(|s| {
                        let (addr, _s) = (s.vaddr.unwrap(), s.string.unwrap());
                        let bytes = base64::decode(&_s).unwrap_or(Vec::new());
                        let ret_string = match str::from_utf8(bytes.as_slice()) {
                            Ok(v) => v.to_string(),
                            Err(_e) => _s,
                        };
                        (addr, ret_string)
                    })
                    .collect();
                println!("{}", decompile(rfn, &func_name_map, &strings));
            } else {
                println!("{} is not found", f);
            }
        }
        _ => {}
    }
}

fn load_proj_by_path(path: &str) -> RadecoProject {
    let mut p = ProjectLoader::new().path(path).load();
    let regfile = p.regfile().clone();
    for mut xy in p.iter_mut() {
        analyze_mod(regfile.clone(), xy.module);
    }
    p
}

fn load_proj_tcp(url: &str) -> Result<RadecoProject, &'static str> {
    let r2p = R2Pipe::tcp(url)?;
    Ok(load_project_by_r2pipe(r2p))
}

fn load_proj_http(url: &str) -> Result<RadecoProject, &'static str> {
    let r2p = R2Pipe::http(url)?;
    Ok(load_project_by_r2pipe(r2p))
}

fn load_project_by_r2pipe(r2p: R2Pipe) -> RadecoProject {
    let r2 = R2::from(r2p);
    let r2w = Rc::new(RefCell::new(r2));
    let mut p = ProjectLoader::new().source(Rc::new(r2w)).load();
    let regfile = p.regfile().clone();
    for mut xy in p.iter_mut() {
        analyze_mod(regfile.clone(), xy.module);
    }
    p
}

fn fn_list(proj: &RadecoProject) -> Vec<String> {
    proj.iter()
        .map(|i| i.module)
        .flat_map(|rmod| rmod.functions.values())
        .map(|s| s.name.to_string())
        .collect()
}

fn get_function<'a>(name: &str, proj: &'a RadecoProject) -> Option<&'a RadecoFunction> {
    proj.iter()
        .map(|i| i.module)
        .flat_map(|rmod| rmod.functions.values())
        .filter(|rfn| rfn.name == name)
        .next()
}

fn get_function_mut<'a>(name: &str, proj: &'a mut RadecoProject) -> Option<&'a mut RadecoFunction> {
    proj.iter_mut()
        .map(|i| i.module)
        .flat_map(|rmod| rmod.functions.values_mut())
        .filter(|rfn| rfn.name == name)
        .next()
}

fn analyze_mod(regfile: Arc<SubRegisterFile>, rmod: &mut RadecoModule) {
    // Analyze preserved for all functions.
    {
        println!("[*] Fixing Callee Information");
        let bp_name = regfile.get_name_by_alias(&"BP".to_string());
        let bp_name = bp_name.map(|s| s.to_owned());
        let sp_name = regfile.get_name_by_alias(&"SP".to_string());
        let sp_name = sp_name.map(|s| s.to_owned());
        let mut callfixer = CallFixer::new(rmod, bp_name, sp_name);
        callfixer.rounded_analysis();
    }

    // Fix call sites
    radeco_lib::analysis::functions::fix_ssa_opcalls::go(rmod);

    // Infer calling conventions
    radeco_lib::analysis::functions::infer_regusage::run(rmod, &*regfile);
}

fn analyze(rfn: &mut RadecoFunction) {
    println!("[+] Analyzing: {} @ {:#x}", rfn.name, rfn.offset);
    {
        println!("  [*] Eliminating Dead Code");
        dce::collect(rfn.ssa_mut());
    }
    let mut ssa = {
        // Constant Propagation (sccp)
        println!("  [*] Propagating Constants");
        let mut analyzer = sccp::Analyzer::new(rfn.ssa_mut());
        analyzer.analyze();
        analyzer.emit_ssa()
    };
    {
        println!("  [*] Eliminating More DeadCode");
        dce::collect(&mut ssa);
    }
    *rfn.ssa_mut() = ssa;
    {
        // Common SubExpression Elimination (cse)
        println!("  [*] Eliminating Common SubExpressions");
        let mut cse = CSE::new(rfn.ssa_mut());
        cse.run();
    }
    {
        // Verify SSA
        println!("  [*] Verifying SSA's Validity");
        match verifier::verify(rfn.ssa()) {
            Err(e) => {
                println!("  [*] Found Error: {}", e);
                process::exit(255);
            }
            Ok(_) => {}
        }
    }
}

fn emit_ir(rfn: &RadecoFunction) -> String {
    println!("  [*] Writing out IR");
    let mut res = String::new();
    ir_writer::emit_il(&mut res, Some(rfn.name.to_string()), rfn.ssa()).unwrap();
    res
}

fn emit_dot(ssa: &SSAStorage) -> String {
    dot::emit_dot(ssa)
}

fn decompile(
    rfn: &RadecoFunction,
    func_name_map: &HashMap<u64, String>,
    strings: &HashMap<u64, String>,
) -> String {
    let c_cfg = c_cfg_builder::recover_c_cfg(rfn, func_name_map, strings);
    if let Err(err) = CCFGVerifier::verify(&c_cfg) {
        println!("CCFG verification failed");
        println!("{}", err);
    }
    ctrl_flow_struct::structure_and_convert(c_cfg)
        .unwrap()
        .print()
}

fn is_file(path: &str) -> bool {
    fs::metadata(path).map(|f| f.is_file()).unwrap_or(false)
}

fn is_http(url: &str) -> bool {
    url.starts_with(scheme::HTTP)
}

fn is_tcp(url: &str) -> bool {
    url.starts_with(scheme::TCP)
}
<commit_msg>Impl strings, func_names<commit_after>#[cfg(feature = "trace_log")]
extern crate env_logger;

extern crate radeco_lib;
extern crate r2pipe;
extern crate r2api;
extern crate base64;
extern crate rustyline;

use r2pipe::{R2, R2Pipe};
use radeco_lib::analysis::cse::cse::CSE;
use radeco_lib::analysis::interproc::fixcall::CallFixer;
use radeco_lib::analysis::sccp;
use radeco_lib::backend::lang_c::c_cfg::CCFGVerifier;
use radeco_lib::backend::lang_c::c_cfg::ctrl_flow_struct;
use radeco_lib::backend::lang_c::c_cfg_builder;
use radeco_lib::frontend::radeco_containers::*;
use radeco_lib::middle::{dce, dot};
use radeco_lib::middle::ir_writer;
use radeco_lib::middle::regfile::SubRegisterFile;
use radeco_lib::middle::ssa::ssastorage::SSAStorage;
use radeco_lib::middle::ssa::verifier;
use rustyline::Editor;

use rustyline::error::ReadlineError;
use std::cell::RefCell;
use std::collections::HashMap;
use std::env;

use std::fs;
use std::process;
use std::rc::Rc;
use std::str;
use std::sync::Arc;

mod scheme {
    pub const HTTP: &'static str = "http://";
    pub const TCP: &'static str = "tcp://";
}

// On unix platforms you can use ANSI escape sequences
#[cfg(unix)]
const PROMPT: &'static str = "\x1b[1;32m>>\x1b[0m ";

// Windows consoles typically don't support ANSI escape sequences out
// of the box
#[cfg(windows)]
const PROMPT: &'static str = ">> ";

fn main() {
    let mut rl = Editor::<()>::new();
    let mut proj = env::args().nth(1).and_then(|ref s| {
        if is_http(s) {
            load_proj_http(&s[scheme::HTTP.len()..])
        } else if is_tcp(s) {
            load_proj_tcp(&s[scheme::TCP.len()..])
        } else if is_file(s) {
            Ok(load_proj_by_path(s))
        } else {
            Err("Invalid argument")
        }.ok()
    });
    loop {
        let readline = rl.readline(PROMPT);
        match readline {
            Ok(line) => {
                let mut terms = line.split_whitespace();
                let o1 = terms.next();
                let o2 = terms.next();
                cmd(o1, o2, &mut proj);
            }
            Err(ReadlineError::Interrupted) |
            Err(ReadlineError::Eof) => break,
            Err(err) => {
                println!("Error: {:?}", err);
                break;
            }
        }
    }
}

mod command {
    pub const LOAD: &'static str = "load";
    pub const CONNECT: &'static str = "connect";
    pub const FNLIST: &'static str = "fn_list";
    pub const ANALYZE: &'static str = "analyze";
    pub const DOT: &'static str = "dot";
    pub const IR: &'static str = "ir";
    pub const DECOMPILE: &'static str = "decompile";
}

fn cmd(op1: Option<&str>, op2: Option<&str>, proj_opt: &mut Option<RadecoProject>) {
    match (op1, op2) {
        (Some(command::LOAD), Some(path)) => {
            if is_file(path) {
                *proj_opt = Some(load_proj_by_path(path));
                return;
            } else {
                println!("{} is not found.", path);
                return;
            }
        }
        (Some(command::CONNECT), Some(url)) => {
            let p_opt = if is_http(&url) {
                load_proj_http(&url[7..])
            } else if is_tcp(&url) {
                load_proj_tcp(&url[6..])
            } else {
                Err("Invalid url")
            };
            match p_opt {
                Ok(proj) => *proj_opt = Some(proj),
                Err(msg) => println!("{}", msg),
            }
            return;
        }
        _ => {}
    };
    let mut proj = match proj_opt {
        Some(proj) => proj,
        None => {
            println!("Load a project first");
            return;
        }
    };
    match (op1, op2) {
        (Some(command::ANALYZE), Some("*")) => {
            let rfns = proj.iter_mut().map(|i| i.module).flat_map(|rmod| {
                rmod.functions.values_mut()
            });
            for rfn in rfns {
                analyze(rfn);
            }
        }
        (Some(command::FNLIST), _) => {
            let funcs = fn_list(&proj);
            println!("{}", funcs.join("\n"));
        }
        // TODO Show list of dependency information of analyses
        // TODO Add command for individual analyses
        (Some(command::ANALYZE), Some(f)) => {
            if let Some(rfn) = get_function_mut(f, &mut proj) {
                analyze(rfn);
            } else {
                println!("{} is not found", f);
            }
        }
        (Some(command::DOT), Some(f)) => {
            if let Some(rfn) = get_function(f, &proj) {
                println!("{}", emit_dot(rfn.ssa()));
            } else {
                println!("{} is not found", f);
            }
        }
        (Some(command::IR), Some(f)) => {
            if let Some(rfn) = get_function(f, &proj) {
                println!("{}", emit_ir(rfn));
            } else {
                println!("{} is not found", f);
            }
        }
        (Some(command::DECOMPILE), Some(f)) => {
            if let Some(rfn) = get_function(f, &proj) {
                let rmod = proj.iter().map(|i| i.module).next().unwrap();
                let func_name_map = func_names(&rmod);
                let strings = strings(&rmod);
                println!("{}", decompile(rfn, &func_name_map, &strings));
            } else {
                println!("{} is not found", f);
            }
        }
        _ => {}
    }
}

fn load_proj_by_path(path: &str) -> RadecoProject {
    let mut p = ProjectLoader::new().path(path).load();
    let regfile = p.regfile().clone();
    for mut xy in p.iter_mut() {
        analyze_mod(regfile.clone(), xy.module);
    }
    p
}

fn load_proj_tcp(url: &str) -> Result<RadecoProject, &'static str> {
    let r2p = R2Pipe::tcp(url)?;
    Ok(load_project_by_r2pipe(r2p))
}

fn load_proj_http(url: &str) -> Result<RadecoProject, &'static str> {
    let r2p = R2Pipe::http(url)?;
    Ok(load_project_by_r2pipe(r2p))
}

fn load_project_by_r2pipe(r2p: R2Pipe) -> RadecoProject {
    let r2 = R2::from(r2p);
    let r2w = Rc::new(RefCell::new(r2));
    let mut p = ProjectLoader::new().source(Rc::new(r2w)).load();
    let regfile = p.regfile().clone();
    for mut xy in p.iter_mut() {
        analyze_mod(regfile.clone(), xy.module);
    }
    p
}

fn fn_list(proj: &RadecoProject) -> Vec<String> {
    proj.iter()
        .map(|i| i.module)
        .flat_map(|rmod| rmod.functions.values())
        .map(|s| s.name.to_string())
        .collect()
}

fn get_function<'a>(name: &str, proj: &'a RadecoProject) -> Option<&'a RadecoFunction> {
    proj.iter()
        .map(|i| i.module)
        .flat_map(|rmod| rmod.functions.values())
        .filter(|rfn| rfn.name == name)
        .next()
}

fn get_function_mut<'a>(name: &str, proj: &'a mut RadecoProject) -> Option<&'a mut RadecoFunction> {
    proj.iter_mut()
        .map(|i| i.module)
        .flat_map(|rmod| rmod.functions.values_mut())
        .filter(|rfn| rfn.name == name)
        .next()
}

fn analyze_mod(regfile: Arc<SubRegisterFile>, rmod: &mut RadecoModule) {
    // Analyze preserved for all functions.
    {
        println!("[*] Fixing Callee Information");
        let bp_name = regfile.get_name_by_alias(&"BP".to_string());
        let bp_name = bp_name.map(|s| s.to_owned());
        let sp_name = regfile.get_name_by_alias(&"SP".to_string());
        let sp_name = sp_name.map(|s| s.to_owned());
        let mut callfixer = CallFixer::new(rmod, bp_name, sp_name);
        callfixer.rounded_analysis();
    }

    // Fix call sites
    radeco_lib::analysis::functions::fix_ssa_opcalls::go(rmod);

    // Infer calling conventions
    radeco_lib::analysis::functions::infer_regusage::run(rmod, &*regfile);
}

fn analyze(rfn: &mut RadecoFunction) {
    println!("[+] Analyzing: {} @ {:#x}", rfn.name, rfn.offset);
    {
        println!("  [*] Eliminating Dead Code");
        dce::collect(rfn.ssa_mut());
    }
    let mut ssa = {
        // Constant Propagation (sccp)
        println!("  [*] Propagating Constants");
        let mut analyzer = sccp::Analyzer::new(rfn.ssa_mut());
        analyzer.analyze();
        analyzer.emit_ssa()
    };
    {
        println!("  [*] Eliminating More DeadCode");
        dce::collect(&mut ssa);
    }
    *rfn.ssa_mut() = ssa;
    {
        // Common SubExpression Elimination (cse)
        println!("  [*] Eliminating Common SubExpressions");
        let mut cse = CSE::new(rfn.ssa_mut());
        cse.run();
    }
    {
        // Verify SSA
        println!("  [*] Verifying SSA's Validity");
        match verifier::verify(rfn.ssa()) {
            Err(e) => {
                println!("  [*] Found Error: {}", e);
                process::exit(255);
            }
            Ok(_) => {}
        }
    }
}

fn emit_ir(rfn: &RadecoFunction) -> String {
    println!("  [*] Writing out IR");
    let mut res = String::new();
    ir_writer::emit_il(&mut res, Some(rfn.name.to_string()), rfn.ssa()).unwrap();
    res
}

fn emit_dot(ssa: &SSAStorage) -> String {
    dot::emit_dot(ssa)
}

fn decompile(
    rfn: &RadecoFunction,
    func_name_map: &HashMap<u64, String>,
    strings: &HashMap<u64, String>,
) -> String {
    let c_cfg = c_cfg_builder::recover_c_cfg(rfn, func_name_map, strings);
    if let Err(err) = CCFGVerifier::verify(&c_cfg) {
        println!("CCFG verification failed");
        println!("{}", err);
    }
    ctrl_flow_struct::structure_and_convert(c_cfg)
        .unwrap()
        .print()
}

fn is_file(path: &str) -> bool {
    fs::metadata(path).map(|f| f.is_file()).unwrap_or(false)
}

fn is_http(url: &str) -> bool {
    url.starts_with(scheme::HTTP)
}

fn is_tcp(url: &str) -> bool {
    url.starts_with(scheme::TCP)
}

fn func_names(rmod: &RadecoModule) -> HashMap<u64, String> {
    rmod.functions
        .iter()
        .map(|(&addr, f)| (addr, f.name.to_string()))
        .collect()
}

fn strings(rmod: &RadecoModule) -> HashMap<u64, String> {
    rmod.strings()
        .iter()
        .filter(|ref s| s.paddr.is_some() && s.string.is_some())
        .cloned()
        .map(|s| {
            let (addr, _s) = (s.vaddr.unwrap(), s.string.unwrap());
            let bytes = base64::decode(&_s).unwrap_or(Vec::new());
            let ret_string = match str::from_utf8(bytes.as_slice()) {
                Ok(v) => v.to_string(),
                Err(_e) => _s,
            };
            (addr, ret_string)
        })
        .collect()
}
<|endoftext|>