<commit_before>use conduit::Request;
use flate2::read::GzDecoder;
use openssl::error::ErrorStack;
use openssl::hash::{Hasher, MessageDigest};
use reqwest::header;

use crate::util::LimitErrorReader;
use crate::util::{cargo_err, internal, AppResult, ChainError, Maximums};

use std::env;
use std::fs::{self, File};
use std::io::{Cursor, Read};
use std::sync::Arc;

use crate::middleware::app::RequestApp;
use crate::models::Crate;

const CACHE_CONTROL_IMMUTABLE: &str = "public,max-age=31536000,immutable";
const CACHE_CONTROL_README: &str = "public,max-age=604800";

#[derive(Clone, Debug)]
pub enum Uploader {
    /// For production usage, uploads and redirects to s3.
    /// For test usage with `TestApp::with_proxy()`, the recording proxy is used.
    S3 {
        bucket: s3::Bucket,
        cdn: Option<String>,
    },

    /// For development usage only: "uploads" crate files to `dist` and serves them
    /// from there as well to enable local publishing and download
    Local,
}

impl Uploader {
    /// Returns the URL of an uploaded crate's version archive.
    ///
    /// The function doesn't check for the existence of the file.
    pub fn crate_location(&self, crate_name: &str, version: &str) -> String {
        match *self {
            Uploader::S3 {
                ref bucket,
                ref cdn,
                ..
            } => {
                let host = match *cdn {
                    Some(ref s) => s.clone(),
                    None => bucket.host(),
                };
                let path = Uploader::crate_path(crate_name, version);
                format!("https://{}/{}", host, path)
            }
            Uploader::Local => format!("/{}", Uploader::crate_path(crate_name, version)),
        }
    }

    /// Returns the URL of an uploaded crate's version readme.
    ///
    /// The function doesn't check for the existence of the file.
    pub fn readme_location(&self, crate_name: &str, version: &str) -> String {
        match *self {
            Uploader::S3 {
                ref bucket,
                ref cdn,
                ..
            } => {
                let host = match *cdn {
                    Some(ref s) => s.clone(),
                    None => bucket.host(),
                };
                let path = Uploader::readme_path(crate_name, version);
                format!("https://{}/{}", host, path)
            }
            Uploader::Local => format!("/{}", Uploader::readme_path(crate_name, version)),
        }
    }

    /// Returns the internal path of an uploaded crate's version archive.
    fn crate_path(name: &str, version: &str) -> String {
        // No slash in front so we can use join
        format!("crates/{}/{}-{}.crate", name, name, version)
    }

    /// Returns the internal path of an uploaded crate's version readme.
    fn readme_path(name: &str, version: &str) -> String {
        format!("readmes/{}/{}-{}.html", name, name, version)
    }

    /// Uploads a file using the configured uploader (either `S3`, `Local`).
    ///
    /// It returns the path of the uploaded file.
    ///
    /// # Panics
    ///
    /// This function can panic on an `Self::Local` during development.
    /// Production and tests use `Self::S3` which should not panic.
    pub fn upload<R: std::io::Read + Send + 'static>(
        &self,
        client: &reqwest::Client,
        path: &str,
        mut content: R,
        content_length: u64,
        content_type: &str,
        extra_headers: header::HeaderMap,
    ) -> AppResult<Option<String>> {
        match *self {
            Uploader::S3 { ref bucket, .. } => {
                bucket
                    .put(
                        client,
                        path,
                        content,
                        content_length,
                        content_type,
                        extra_headers,
                    )
                    .map_err(|e| internal(&format_args!("failed to upload to S3: {}", e)))?;
                Ok(Some(String::from(path)))
            }
            Uploader::Local => {
                let filename = env::current_dir().unwrap().join("local_uploads").join(path);
                let dir = filename.parent().unwrap();
                fs::create_dir_all(dir)?;
                let mut file = File::create(&filename)?;
                std::io::copy(&mut content, &mut file)?;
                Ok(filename.to_str().map(String::from))
            }
        }
    }

    /// Uploads a crate and returns the checksum of the uploaded crate file.
    pub fn upload_crate(
        &self,
        req: &mut dyn Request,
        krate: &Crate,
        maximums: Maximums,
        vers: &semver::Version,
    ) -> AppResult<Vec<u8>> {
        let app = Arc::clone(req.app());
        let path = Uploader::crate_path(&krate.name, &vers.to_string());
        let mut body = Vec::new();
        LimitErrorReader::new(req.body(), maximums.max_upload_size).read_to_end(&mut body)?;
        verify_tarball(krate, vers, &body, maximums.max_unpack_size)?;
        let checksum = hash(&body)?;
        let content_length = body.len() as u64;
        let content = Cursor::new(body);
        let mut extra_headers = header::HeaderMap::new();
        extra_headers.insert(header::CACHE_CONTROL, CACHE_CONTROL_README.parse().unwrap());
        self.upload(
            app.http_client(),
            &path,
            content,
            content_length,
            "application/x-tar",
            extra_headers,
        )?;
        Ok(checksum)
    }

    pub(crate) fn upload_readme(
        &self,
        http_client: &reqwest::Client,
        crate_name: &str,
        vers: &str,
        readme: String,
    ) -> AppResult<()> {
        let path = Uploader::readme_path(crate_name, vers);
        let content_length = readme.len() as u64;
        let content = Cursor::new(readme);
        let mut extra_headers = header::HeaderMap::new();
        extra_headers.insert(
            header::CACHE_CONTROL,
            CACHE_CONTROL_IMMUTABLE.parse().unwrap(),
        );
        self.upload(
            http_client,
            &path,
            content,
            content_length,
            "text/html",
            extra_headers,
        )?;
        Ok(())
    }
}

fn verify_tarball(
    krate: &Crate,
    vers: &semver::Version,
    tarball: &[u8],
    max_unpack: u64,
) -> AppResult<()> {
    // All our data is currently encoded with gzip
    let decoder = GzDecoder::new(tarball);

    // Don't let gzip decompression go into the weeeds, apply a fixed cap after
    // which point we say the decompressed source is "too large".
    let decoder = LimitErrorReader::new(decoder, max_unpack);

    // Use this I/O object now to take a peek inside
    let mut archive = tar::Archive::new(decoder);
    let prefix = format!("{}-{}", krate.name, vers);
    for entry in archive.entries()? {
        let entry = entry.chain_error(|| {
            cargo_err("uploaded tarball is malformed or too large when decompressed")
        })?;

        // Verify that all entries actually start with `$name-$vers/`.
        // Historically Cargo didn't verify this on extraction so you could
        // upload a tarball that contains both `foo-0.1.0/` source code as well
        // as `bar-0.1.0/` source code, and this could overwrite other crates in
        // the registry!
        if !entry.path()?.starts_with(&prefix) {
            return Err(cargo_err("invalid tarball uploaded"));
        }

        // Historical versions of the `tar` crate which Cargo uses internally
        // don't properly prevent hard links and symlinks from overwriting
        // arbitrary files on the filesystem. As a bit of a hammer we reject any
        // tarball with these sorts of links. Cargo doesn't currently ever
        // generate a tarball with these file types so this should work for now.
        let entry_type = entry.header().entry_type();
        if entry_type.is_hard_link() || entry_type.is_symlink() {
            return Err(cargo_err("invalid tarball uploaded"));
        }
    }
    Ok(())
}

fn hash(data: &[u8]) -> Result<Vec<u8>, ErrorStack> {
    let mut hasher = Hasher::new(MessageDigest::sha256())?;
    hasher.update(data)?;
    Ok(hasher.finish()?.to_vec())
}
<commit_msg>Auto merge of #2071 - jtgeibel:fix/swapped-cache-headers, r=smarnach<commit_after>use conduit::Request;
use flate2::read::GzDecoder;
use openssl::error::ErrorStack;
use openssl::hash::{Hasher, MessageDigest};
use reqwest::header;

use crate::util::LimitErrorReader;
use crate::util::{cargo_err, internal, AppResult, ChainError, Maximums};

use std::env;
use std::fs::{self, File};
use std::io::{Cursor, Read};
use std::sync::Arc;

use crate::middleware::app::RequestApp;
use crate::models::Crate;

const CACHE_CONTROL_IMMUTABLE: &str = "public,max-age=31536000,immutable";
const CACHE_CONTROL_README: &str = "public,max-age=604800";

#[derive(Clone, Debug)]
pub enum Uploader {
    /// For production usage, uploads and redirects to s3.
    /// For test usage with `TestApp::with_proxy()`, the recording proxy is used.
    S3 {
        bucket: s3::Bucket,
        cdn: Option<String>,
    },

    /// For development usage only: "uploads" crate files to `dist` and serves them
    /// from there as well to enable local publishing and download
    Local,
}

impl Uploader {
    /// Returns the URL of an uploaded crate's version archive.
    ///
    /// The function doesn't check for the existence of the file.
    pub fn crate_location(&self, crate_name: &str, version: &str) -> String {
        match *self {
            Uploader::S3 {
                ref bucket,
                ref cdn,
                ..
            } => {
                let host = match *cdn {
                    Some(ref s) => s.clone(),
                    None => bucket.host(),
                };
                let path = Uploader::crate_path(crate_name, version);
                format!("https://{}/{}", host, path)
            }
            Uploader::Local => format!("/{}", Uploader::crate_path(crate_name, version)),
        }
    }

    /// Returns the URL of an uploaded crate's version readme.
    ///
    /// The function doesn't check for the existence of the file.
    pub fn readme_location(&self, crate_name: &str, version: &str) -> String {
        match *self {
            Uploader::S3 {
                ref bucket,
                ref cdn,
                ..
            } => {
                let host = match *cdn {
                    Some(ref s) => s.clone(),
                    None => bucket.host(),
                };
                let path = Uploader::readme_path(crate_name, version);
                format!("https://{}/{}", host, path)
            }
            Uploader::Local => format!("/{}", Uploader::readme_path(crate_name, version)),
        }
    }

    /// Returns the internal path of an uploaded crate's version archive.
    fn crate_path(name: &str, version: &str) -> String {
        // No slash in front so we can use join
        format!("crates/{}/{}-{}.crate", name, name, version)
    }

    /// Returns the internal path of an uploaded crate's version readme.
    fn readme_path(name: &str, version: &str) -> String {
        format!("readmes/{}/{}-{}.html", name, name, version)
    }

    /// Uploads a file using the configured uploader (either `S3`, `Local`).
    ///
    /// It returns the path of the uploaded file.
    ///
    /// # Panics
    ///
    /// This function can panic on an `Self::Local` during development.
    /// Production and tests use `Self::S3` which should not panic.
    pub fn upload<R: std::io::Read + Send + 'static>(
        &self,
        client: &reqwest::Client,
        path: &str,
        mut content: R,
        content_length: u64,
        content_type: &str,
        extra_headers: header::HeaderMap,
    ) -> AppResult<Option<String>> {
        match *self {
            Uploader::S3 { ref bucket, .. } => {
                bucket
                    .put(
                        client,
                        path,
                        content,
                        content_length,
                        content_type,
                        extra_headers,
                    )
                    .map_err(|e| internal(&format_args!("failed to upload to S3: {}", e)))?;
                Ok(Some(String::from(path)))
            }
            Uploader::Local => {
                let filename = env::current_dir().unwrap().join("local_uploads").join(path);
                let dir = filename.parent().unwrap();
                fs::create_dir_all(dir)?;
                let mut file = File::create(&filename)?;
                std::io::copy(&mut content, &mut file)?;
                Ok(filename.to_str().map(String::from))
            }
        }
    }

    /// Uploads a crate and returns the checksum of the uploaded crate file.
    pub fn upload_crate(
        &self,
        req: &mut dyn Request,
        krate: &Crate,
        maximums: Maximums,
        vers: &semver::Version,
    ) -> AppResult<Vec<u8>> {
        let app = Arc::clone(req.app());
        let path = Uploader::crate_path(&krate.name, &vers.to_string());
        let mut body = Vec::new();
        LimitErrorReader::new(req.body(), maximums.max_upload_size).read_to_end(&mut body)?;
        verify_tarball(krate, vers, &body, maximums.max_unpack_size)?;
        let checksum = hash(&body)?;
        let content_length = body.len() as u64;
        let content = Cursor::new(body);
        let mut extra_headers = header::HeaderMap::new();
        extra_headers.insert(
            header::CACHE_CONTROL,
            CACHE_CONTROL_IMMUTABLE.parse().unwrap(),
        );
        self.upload(
            app.http_client(),
            &path,
            content,
            content_length,
            "application/x-tar",
            extra_headers,
        )?;
        Ok(checksum)
    }

    pub(crate) fn upload_readme(
        &self,
        http_client: &reqwest::Client,
        crate_name: &str,
        vers: &str,
        readme: String,
    ) -> AppResult<()> {
        let path = Uploader::readme_path(crate_name, vers);
        let content_length = readme.len() as u64;
        let content = Cursor::new(readme);
        let mut extra_headers = header::HeaderMap::new();
        extra_headers.insert(header::CACHE_CONTROL, CACHE_CONTROL_README.parse().unwrap());
        self.upload(
            http_client,
            &path,
            content,
            content_length,
            "text/html",
            extra_headers,
        )?;
        Ok(())
    }
}

fn verify_tarball(
    krate: &Crate,
    vers: &semver::Version,
    tarball: &[u8],
    max_unpack: u64,
) -> AppResult<()> {
    // All our data is currently encoded with gzip
    let decoder = GzDecoder::new(tarball);

    // Don't let gzip decompression go into the weeeds, apply a fixed cap after
    // which point we say the decompressed source is "too large".
    let decoder = LimitErrorReader::new(decoder, max_unpack);

    // Use this I/O object now to take a peek inside
    let mut archive = tar::Archive::new(decoder);
    let prefix = format!("{}-{}", krate.name, vers);
    for entry in archive.entries()? {
        let entry = entry.chain_error(|| {
            cargo_err("uploaded tarball is malformed or too large when decompressed")
        })?;

        // Verify that all entries actually start with `$name-$vers/`.
        // Historically Cargo didn't verify this on extraction so you could
        // upload a tarball that contains both `foo-0.1.0/` source code as well
        // as `bar-0.1.0/` source code, and this could overwrite other crates in
        // the registry!
        if !entry.path()?.starts_with(&prefix) {
            return Err(cargo_err("invalid tarball uploaded"));
        }

        // Historical versions of the `tar` crate which Cargo uses internally
        // don't properly prevent hard links and symlinks from overwriting
        // arbitrary files on the filesystem. As a bit of a hammer we reject any
        // tarball with these sorts of links. Cargo doesn't currently ever
        // generate a tarball with these file types so this should work for now.
        let entry_type = entry.header().entry_type();
        if entry_type.is_hard_link() || entry_type.is_symlink() {
            return Err(cargo_err("invalid tarball uploaded"));
        }
    }
    Ok(())
}

fn hash(data: &[u8]) -> Result<Vec<u8>, ErrorStack> {
    let mut hasher = Hasher::new(MessageDigest::sha256())?;
    hasher.update(data)?;
    Ok(hasher.finish()?.to_vec())
}
<|endoftext|>


<commit_before>extern crate libc;

use indy::api::payments::indy_register_payment_method;
use std::sync::atomic::Ordering;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::ATOMIC_USIZE_INIT;
use std::ffi::CString;
use std::os::raw::c_char;

type CommonResponseCallback = extern fn(command_handle_: i32,
                                        err: i32,
                                        res1: *const c_char) -> i32;

pub extern fn init() {
    let _cmd_handle = get_next_id();
    let payment_method_name = CString::new("null_payment_plugin").unwrap();
    indy_register_payment_method(
        _cmd_handle,
        payment_method_name.as_ptr(),
        Some(create_payment_address),
        None,
        None,
        None,
        None,
        None,
        None,
        None,
        None,
        None,
        None,
        None
    );
}

extern fn create_payment_address(
    cmd_handle:i32,
    config: *const c_char,
    cb: Option<extern fn(command_handle_: i32,
                         err: i32,
                         payment_address: *const c_char) -> i32>
) -> i32 {
    let addr = CString::new("pay:null_payment_plugin:null").unwrap();
    cb.unwrap()(cmd_handle, 0, addr.as_ptr())
}

extern fn

lazy_static! {
    static ref IDS_COUNTER: AtomicUsize = ATOMIC_USIZE_INIT; //TODO use AtomicI32
}

fn get_next_id() -> i32 {
    (IDS_COUNTER.fetch_add(1, Ordering::SeqCst) + 1) as i32
}<commit_msg>Add injection of results returned in null payment plugin<commit_after>extern crate libc;

use indy::api::payments::indy_register_payment_method;
use indy::api::ledger::indy_build_get_txn_request;
use indy::api::ErrorCode;
use std::sync::Mutex;
use std::sync::atomic::Ordering;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::ATOMIC_USIZE_INIT;
use std::collections::HashMap;
use std::ffi::CString;
use std::os::raw::c_char;
use std::ffi::CStr;

type CommonResponseCallback = extern fn(command_handle_: i32,
                                        err: ErrorCode,
                                        res1: *const c_char) -> ErrorCode;

lazy_static! {
    static ref IDS_COUNTER: AtomicUsize = ATOMIC_USIZE_INIT; //TODO use AtomicI32
}

pub extern fn nullpayment_init(cb: Option<extern fn(cmd_handle: i32, err: ErrorCode)>) {
    let _cmd_handle = get_next_id();
    let payment_method_name = CString::new("null_payment_plugin").unwrap();

    indy_register_payment_method(
        _cmd_handle,
        payment_method_name.as_ptr(),
        Some(_create_payment_address_handler),
        Some(_add_request_fees_handler),
        Some(_parse_response_with_fees_handler),
        Some(_build_get_utxo_request_handler),
        Some(_parse_get_utxo_response_handler),
        Some(_build_payment_req_handler),
        Some(_parse_payment_response_handler),
        Some(_build_mint_req_handler),
        Some(_build_set_txn_fees_req_handler),
        Some(_build_get_txn_fees_req_handler),
        Some(_parse_get_txn_fees_response_handler),
        cb
    );
}

lazy_static! {
      static ref CREATE_ADDRESS_RESULT_INJECTIONS: Mutex<Vec<(ErrorCode, CString)>> = Default::default();
      static ref ADD_REQUEST_FEES_RESULT_INJECTIONS: Mutex<Vec<(ErrorCode, CString)>> = Default::default();
      static ref PARSE_RESPONSE_WITH_FEES_RESULT_INJECTIONS: Mutex<Vec<(ErrorCode, CString)>> = Default::default();
      static ref BUILD_GET_UTXO_REQUEST_RESULT_INJECTIONS: Mutex<Vec<(ErrorCode, CString)>> = Default::default();
      static ref PARSE_GET_UTXO_RESPONSE_RESULT_INJECTIONS: Mutex<Vec<(ErrorCode, CString)>> = Default::default();
      static ref BUILD_PAYMENT_REQ_RESULT_INJECTIONS: Mutex<Vec<(ErrorCode, CString)>> = Default::default();
      static ref PARSE_PAYMENT_RESPONSE_RESULT_INJECTIONS: Mutex<Vec<(ErrorCode, CString)>> = Default::default();
      static ref BUILD_MINT_REQ_RESULT_INJECTIONS: Mutex<Vec<(ErrorCode, CString)>> = Default::default();
      static ref BUILD_SET_TXN_FEES_REQ_RESULT_INJECTIONS: Mutex<Vec<(ErrorCode, CString)>> = Default::default();
      static ref BUILD_GET_TXN_FEES_REQ_RESULT_INJECTIONS: Mutex<Vec<(ErrorCode, CString)>> = Default::default();
      static ref PARSE_GET_TXN_FEES_RESPONSE_RESULT_INJECTIONS: Mutex<Vec<(ErrorCode, CString)>> = Default::default();
}

extern fn _clear_vec(vec: Mutex<Vec<(ErrorCode, CString)>>) {
    let mut vec = vec.lock().unwrap();
    vec.clear();
}

extern fn _create_payment_address_handler(
    cmd_handle:i32,
    _config: *const c_char,
    cb: Option<CommonResponseCallback>) -> ErrorCode {

    let addr = CString::new("pay:null:_payment_plugin:null").unwrap();
    let mut results = CREATE_ADDRESS_RESULT_INJECTIONS.lock().unwrap();
    let (err, res) = match results.is_empty() {
        false => results.remove(0),
        true => (ErrorCode::Success, addr)
    };
    _execute_cb(cb, cmd_handle, res, err)
}

pub extern fn nullpayment_inject_create_payment_address_result(err: ErrorCode, res: *const c_char) {
    let mut results = CREATE_ADDRESS_RESULT_INJECTIONS.lock().unwrap();
    let res = CString::from(unsafe { CStr::from_ptr(res) });
    results.push((err, res));
}

pub extern fn nullpayment_clear_create_payment_address_injections() {
    let mut vec = CREATE_ADDRESS_RESULT_INJECTIONS.lock().unwrap();
	vec.clear();
}

extern fn _add_request_fees_handler(
    command_handle: i32,
    req_json: *const c_char,
    _inputs_json: *const c_char,
    _outputs_json: *const c_char,
    cb: Option<CommonResponseCallback>) -> ErrorCode {
    let mut results = ADD_REQUEST_FEES_RESULT_INJECTIONS.lock().unwrap();
    let req_json = CString::from(unsafe { CStr::from_ptr(req_json) });
    let (err, res) = match results.is_empty() {
        false => results.remove(0),
        true => (ErrorCode::Success, req_json)
    };
    _execute_cb(cb, command_handle, res, err)
}

pub extern fn nullpayment_inject_add_request_fees_result(err: ErrorCode, res: *const c_char) {
    let mut results = ADD_REQUEST_FEES_RESULT_INJECTIONS.lock().unwrap();
    let res = CString::from(unsafe { CStr::from_ptr(res) });
    results.push((err, res));
}

pub extern fn nullpayment_clear_add_request_fees_injections() {
    let mut vec = ADD_REQUEST_FEES_RESULT_INJECTIONS.lock().unwrap();
	vec.clear();
}

extern fn _parse_response_with_fees_handler(
    command_handle: i32,
    resp_json: *const c_char,
    cb: Option<CommonResponseCallback>) -> ErrorCode {
    let mut results = PARSE_RESPONSE_WITH_FEES_RESULT_INJECTIONS.lock().unwrap();
    let resp_json = CString::from(unsafe { CStr::from_ptr(resp_json) });
    let (err, res) = match results.is_empty() {
        false => results.remove(0),
        true => (ErrorCode::Success, resp_json)
    };
    _execute_cb(cb, command_handle, res, err)
}

pub extern fn nullpayment_inject_parse_response_with_fees_result(err: ErrorCode, res: *const c_char) {
    let mut results = PARSE_RESPONSE_WITH_FEES_RESULT_INJECTIONS.lock().unwrap();
    let res = CString::from(unsafe { CStr::from_ptr(res) });
    results.push((err, res));
}

pub extern fn nullpayment_clear_parse_response_with_fees_injections() {
    let mut vec = PARSE_RESPONSE_WITH_FEES_RESULT_INJECTIONS.lock().unwrap();
	vec.clear();
}

extern fn _build_get_utxo_request_handler(
    command_handle: i32,
    _payment_address: *const c_char,
    cb: Option<CommonResponseCallback>
) -> ErrorCode {
    let mut results = BUILD_GET_UTXO_REQUEST_RESULT_INJECTIONS.lock().unwrap();
    let res = match results.is_empty() {
        false => Some(results.remove(0)),
        true => None
    };
    _build_get_txn_request(command_handle, cb, res)
}
pub extern fn nullpayment_inject_build_get_utxo_request_result(err: ErrorCode, res: *const c_char) {
    let mut results = BUILD_GET_UTXO_REQUEST_RESULT_INJECTIONS.lock().unwrap();
    let res = CString::from(unsafe { CStr::from_ptr(res) });
    results.push((err, res));
}

pub extern fn nullpayment_clear_build_get_utxo_request_injections() {
    let mut vec = BUILD_GET_UTXO_REQUEST_RESULT_INJECTIONS.lock().unwrap();
	vec.clear();
}

extern fn _parse_get_utxo_response_handler(
    command_handle: i32,
    _resp_json: *const c_char,
    cb: Option<CommonResponseCallback>
) -> ErrorCode {
    let utxo_example = r#"[{"input":"pov:null:1", "amount":1, "extra":"1"}, {"input":"pov:null:2", "amount":2, "extra":"2"}]"#;
    let utxo_json = CString::new(utxo_example).unwrap();
    let mut results = PARSE_GET_UTXO_RESPONSE_RESULT_INJECTIONS.lock().unwrap();

    let (err, res) = match results.is_empty() {
        false => results.remove(0),
        true => (ErrorCode::Success, utxo_json)
    };

    _execute_cb(cb, command_handle, res, err)
}

pub extern fn nullpayment_inject_parse_get_utxo_response_result(err: ErrorCode, res: *const c_char) {
    let mut results = PARSE_GET_UTXO_RESPONSE_RESULT_INJECTIONS.lock().unwrap();
    let res = CString::from(unsafe { CStr::from_ptr(res) });
    results.push((err, res));
}

pub extern fn nullpayment_clear_parse_get_utxo_response_injections() {
    let mut vec = PARSE_GET_UTXO_RESPONSE_RESULT_INJECTIONS.lock().unwrap();
	vec.clear();
}

extern fn _build_payment_req_handler(
    command_handle: i32,
    _inputs_json: *const c_char,
    outputs_json: *const c_char,
    cb: Option<CommonResponseCallback>
) -> ErrorCode {
    let mut results = BUILD_PAYMENT_REQ_RESULT_INJECTIONS.lock().unwrap();
    let outputs_json = CString::from(unsafe { CStr::from_ptr(outputs_json) });
    let (err, res) = match results.is_empty() {
        false => results.remove(0),
        true => (ErrorCode::Success, outputs_json)
    };
    _execute_cb(cb, command_handle, res, err)
}

pub extern fn nullpayment_inject_build_payment_req_result(err: ErrorCode, res: *const c_char) {
    let mut results = BUILD_PAYMENT_REQ_RESULT_INJECTIONS.lock().unwrap();
    let res = CString::from(unsafe { CStr::from_ptr(res) });
    results.push((err, res));
}

pub extern fn nullpayment_clear_build_payment_req_injections() {
    let mut vec = BUILD_PAYMENT_REQ_RESULT_INJECTIONS.lock().unwrap();
	vec.clear();
}

extern fn _parse_payment_response_handler(
    command_handle: i32,
    resp_json: *const c_char,
    cb: Option<CommonResponseCallback>
) -> ErrorCode {
    let mut results = PARSE_PAYMENT_RESPONSE_RESULT_INJECTIONS.lock().unwrap();
    let resp_json = CString::from(unsafe { CStr::from_ptr(resp_json) });
    let (err, res) = match results.is_empty() {
        false => results.remove(0),
        true => (ErrorCode::Success, resp_json)
    };
    _execute_cb(cb, command_handle, res, err)
}

pub extern fn nullpayment_inject_parse_payment_response_result(err: ErrorCode, res: *const c_char){
    let mut results = PARSE_PAYMENT_RESPONSE_RESULT_INJECTIONS.lock().unwrap();
    let res = CString::from(unsafe { CStr::from_ptr(res) });
    results.push((err, res));
}

pub extern fn nullpayment_clear_parse_payment_response_injections() {
    let mut vec = PARSE_PAYMENT_RESPONSE_RESULT_INJECTIONS.lock().unwrap();
	vec.clear();
}

extern fn _build_mint_req_handler(
    command_handle: i32,
    _outputs_json: *const c_char,
    cb: Option<CommonResponseCallback>
) -> ErrorCode {
    let mut results = BUILD_MINT_REQ_RESULT_INJECTIONS.lock().unwrap();
    let res = match results.is_empty() {
        false => Some(results.remove(0)),
        true => None
    };
    _build_get_txn_request(command_handle, cb, res)
}

pub extern fn nullpayment_inject_build_mint_req_result(err: ErrorCode, res: *const c_char) {
    let mut results = BUILD_MINT_REQ_RESULT_INJECTIONS.lock().unwrap();
    let res = CString::from(unsafe { CStr::from_ptr(res) });
    results.push((err, res));
}

pub extern fn nullpayment_clear_build_mint_req_injections() {
    let mut vec = BUILD_MINT_REQ_RESULT_INJECTIONS.lock().unwrap();
	vec.clear();
}

extern fn _build_set_txn_fees_req_handler(
    command_handle: i32,
    outputs_json: *const c_char,
    cb: Option<CommonResponseCallback>
) -> ErrorCode {
    let mut results = BUILD_SET_TXN_FEES_REQ_RESULT_INJECTIONS.lock().unwrap();
    let outputs_json = CString::from(unsafe { CStr::from_ptr( outputs_json )});
    let (err, res) = match results.is_empty() {
        false => results.remove(0),
        true => (ErrorCode::Success, outputs_json)
    };
    _execute_cb(cb, command_handle, res, err)
}

pub extern fn nullpayment_inject_build_set_txn_fees_req_result(err: ErrorCode, res: *const c_char) {
    let mut results = BUILD_SET_TXN_FEES_REQ_RESULT_INJECTIONS.lock().unwrap();
    let res = CString::from(unsafe { CStr::from_ptr(res) });
    results.push((err, res));
}

pub extern fn nullpayment_clear_build_set_txn_fees_req_injections() {
    let mut vec = BUILD_SET_TXN_FEES_REQ_RESULT_INJECTIONS.lock().unwrap();
	vec.clear();
}

extern fn _build_get_txn_fees_req_handler(
    command_handle: i32,
    cb: Option<CommonResponseCallback>
) -> ErrorCode {
    let mut results = BUILD_GET_TXN_FEES_REQ_RESULT_INJECTIONS.lock().unwrap();
    let res = match results.is_empty() {
        false => Some(results.remove(0)),
        true => None
    };
    _build_get_txn_request(command_handle, cb, res)
}

pub extern fn nullpayment_inject_build_get_txn_fees_req_result(err: ErrorCode, res: *const c_char) {
    let mut results = BUILD_GET_TXN_FEES_REQ_RESULT_INJECTIONS.lock().unwrap();
    let res = CString::from(unsafe { CStr::from_ptr(res) });
    results.push((err, res));
}

pub extern fn nullpayment_clear_build_get_txn_fees_req_injections() {
    let mut vec = BUILD_GET_TXN_FEES_REQ_RESULT_INJECTIONS.lock().unwrap();
	vec.clear();
}

extern fn _parse_get_txn_fees_response_handler(
    command_handle: i32,
    _resp_json: *const c_char,
    cb: Option<CommonResponseCallback>
) -> ErrorCode {
    let parsed_response = CString::new(
        r#"{"txnType1":1, "txnType2":2, "txnType3":3}"#
    ).unwrap();
    let mut results = PARSE_GET_TXN_FEES_RESPONSE_RESULT_INJECTIONS.lock().unwrap();
    let (err, res) = match results.is_empty() {
        true => (ErrorCode::Success, parsed_response),
        false => results.remove(0)
    };
    _execute_cb(cb, command_handle, res, err)
}

pub extern fn nullpayment_inject_parse_get_txn_fees_response_result(err: ErrorCode, res: *const c_char) {
    let mut results = PARSE_GET_TXN_FEES_RESPONSE_RESULT_INJECTIONS.lock().unwrap();
    let res = CString::from(unsafe { CStr::from_ptr(res) });
    results.push((err, res));
}

pub extern fn nullpayment_clear_parse_get_txn_fees_response_injections() {
    let mut vec = PARSE_GET_TXN_FEES_RESPONSE_RESULT_INJECTIONS.lock().unwrap();
	vec.clear();
}

fn _execute_cb(
    cb: Option<CommonResponseCallback>,
    command_handle: i32,
    res: CString,
    err: ErrorCode
) -> ErrorCode {
    cb.unwrap_or_else(|| {
        extern fn cb (_a: i32, _b: ErrorCode, _c: *const c_char) -> ErrorCode { ErrorCode::UnknownPaymentMethod }
        cb
    })(command_handle, err, res.as_ptr())
}

fn _build_get_txn_request(
    command_handle: i32,
    cb: Option<CommonResponseCallback>,
    res: Option<(ErrorCode, CString)>
) -> ErrorCode {
    match res {
        Some((err, res)) => _execute_cb(cb, command_handle, res, err),
        None => {
            lazy_static!{
                static ref CALLBACKS : Mutex<HashMap<i32, Box<CommonResponseCallback>>> = Default::default();
            }

            extern "C" fn _callback(command_handle:i32, err: ErrorCode, req_json: *const c_char) {
                let mut map = CALLBACKS.lock().unwrap();
                let _cb = map.remove(&command_handle);
                //WARN: maybe dereferencing is needed
                _cb.unwrap()(command_handle, err, req_json);
            }

            let mut map = CALLBACKS.lock().unwrap();
            map.insert(command_handle, Box::new(cb.unwrap()));

            let submitter_did = CString::new("null_payment_plugin").unwrap();

            indy_build_get_txn_request(
                command_handle,
                submitter_did.as_ptr(),
                1,
                Some(_callback)
            );
            ErrorCode::Success
        }
    }
}

fn get_next_id() -> i32 {
    (IDS_COUNTER.fetch_add(1, Ordering::SeqCst) + 1) as i32
}<|endoftext|>


<commit_before>use irc;
use matrix;
use irc::protocol::{Command,Message};
use mio;
use mio::{EventLoop,Handler,Token,EventSet,PollOpt,Sender};
use std::thread;
use std::collections::HashMap;

const CLIENT: Token = Token(0);
const MATRIX: Token = Token(1);

#[derive(Debug)]
pub enum Event {
    EndPoll,
    Matrix(matrix::events::Event)
}

pub struct Bridge {
    client: irc::streams::Client,
    matrix: matrix::client::Client,
    rooms: HashMap<matrix::events::RoomID, Room>,
    seenEvents: Vec<matrix::events::EventID>,
}

impl Handler for Bridge {
    type Timeout = ();
    type Message = Event;

    fn ready(&mut self, event_loop: &mut EventLoop<Bridge>, token: Token, _: EventSet) {
        match token {
            CLIENT =>
                self.handle_client(event_loop),
            _ => unreachable!()
        }
    }

    fn notify(&mut self, event_loop: &mut EventLoop<Bridge>, msg: Self::Message) {
        println!("Got message from matrix! {:?}", msg);
        match msg {
            Event::EndPoll => {
                self.poll_matrix(event_loop.channel());
            },
            Event::Matrix(e) =>
                self.handle_matrix(e)
        };
    }
}

unsafe impl Sync for Bridge{}

struct Room {
    id: matrix::events::RoomID,
    canonical_alias: Option<String>,
    join_rules: Option<String>,
    members: Vec<matrix::events::UserID>,
    pending_events: Vec<matrix::events::RoomEvent>
}

impl Room {
    fn new(id: matrix::events::RoomID) -> Self {
        Room {
            id: id,
            canonical_alias: None,
            join_rules: None,
            members: vec![],
            pending_events: vec![]
        }
    }

    fn run_pending<F>(&mut self, mut callback: &mut F)
            where F: FnMut(irc::protocol::Message) {
        while let Some(evt) = self.pending_events.pop() {
            self.handle_with_alias(evt, callback);
        }
    }

    fn handle_with_alias<F>(&mut self, evt: matrix::events::RoomEvent, mut callback: &mut F)
            where F: FnMut(irc::protocol::Message) {
        if self.canonical_alias != None {
            match evt {
                matrix::events::RoomEvent::Membership(user, matrix::events::MembershipAction::Join) => {
                    callback(irc::protocol::Message {
                        prefix: Some(format!("{}@anony.oob", user.nickname)),
                        command: irc::protocol::Command::Join,
                        args: vec![self.canonical_alias.clone().unwrap()],
                        suffix: None
                    });
                    self.members.push(user);
                },
                matrix::events::RoomEvent::Membership(user, matrix::events::MembershipAction::Leave) => {
                    if self.canonical_alias != None {
                        callback(irc::protocol::Message {
                            prefix: Some(format!("{}@anony.oob", user.nickname)),
                            command: irc::protocol::Command::Part,
                            args: vec![self.canonical_alias.clone().unwrap()],
                            suffix: None
                        });
                    }
                    self.members.push(user);
                },
                matrix::events::RoomEvent::Membership(user, _) => (),
                matrix::events::RoomEvent::Message(user, text) => {
                    callback(irc::protocol::Message {
                        prefix: Some(format!("{}@anony.oob", user.nickname)),
                        command: irc::protocol::Command::Privmsg,
                        args: vec![self.canonical_alias.clone().unwrap()],
                        suffix: Some(text)
                    });
                },
                matrix::events::RoomEvent::Topic(user, topic) => {
                    callback(irc::protocol::Message {
                        prefix: Some(format!("{}@anony.oob", user.nickname)),
                        command: irc::protocol::Command::Topic,
                        args: vec![self.canonical_alias.clone().unwrap()],
                        suffix: Some(topic.clone())
                    });
                },
                matrix::events::RoomEvent::CanonicalAlias(_) => unreachable!(),
                _ => {
                    println!("Unhandled event {:?}", evt)
                }
            }
        } else {
            self.pending_events.push(evt);
        }
    }

    fn handle_event<F>(&mut self, evt: matrix::events::RoomEvent, mut callback: F)
            where F: FnMut(irc::protocol::Message) {
        match evt {
            matrix::events::RoomEvent::CanonicalAlias(name) => {
                let was_empty = self.canonical_alias == None;
                self.canonical_alias = Some(name.clone());
                for ref user in &self.members {
                    callback(irc::protocol::Message {
                        prefix: Some(format!("{}@anony.oob", user.nickname)),
                        command: irc::protocol::Command::Join,
                        args: vec![self.canonical_alias.clone().unwrap()],
                        suffix: None
                    });
                };
                if was_empty {
                    self.run_pending(&mut callback);
                }
            },
            matrix::events::RoomEvent::JoinRules(rules) =>
                self.join_rules = Some(rules.clone()),
            matrix::events::RoomEvent::Create => (),
            matrix::events::RoomEvent::Aliases => (),
            matrix::events::RoomEvent::PowerLevels => (),
            matrix::events::RoomEvent::HistoryVisibility(_) => (),
            matrix::events::RoomEvent::Name(_, _) => (),
            matrix::events::RoomEvent::Avatar(_, _) => (),
            matrix::events::RoomEvent::Unknown(json) => {
                println!("Unknown room event: {:?}", json);
            }
            _ => self.handle_with_alias(evt, &mut callback)
        };
    }
}

impl Bridge {
    pub fn room(&mut self, id: &matrix::events::RoomID) -> &mut Room {
        if !self.rooms.contains_key(id) {
            self.rooms.insert(id.clone(), Room::new(id.clone()));
        }
        match self.rooms.get_mut(id) {
            Some(room) => room,
            None => unreachable!()
        }
    }

    pub fn new(client: irc::streams::Client, url: &str) -> Self {
        Bridge {
            client: client,
            matrix: matrix::client::Client::new(url),
            rooms: HashMap::new(),
            seenEvents: vec![]
        }
    }

    pub fn run(&mut self) {
        let mut events = EventLoop::new().unwrap();
        events.register(self.client.stream(), CLIENT, EventSet::all(), PollOpt::edge()).unwrap();
        events.run(self).unwrap();
    }

    fn handle_matrix(&mut self, evt: matrix::events::Event) {
        let duplicate = match evt.id {
            Some(ref id) =>
                self.seenEvents.contains(id),
            _ => false
        };
        if !duplicate {
            let mut messages: Vec<irc::protocol::Message> = vec![];
            {
                let appendMsg = |msg: irc::protocol::Message| {
                    messages.push(msg);
                };
                match evt.data {
                    matrix::events::EventData::Room(room_id, room_event) => {
                        self.room(&room_id).handle_event(room_event, appendMsg);
                    },
                    _ => println!("Unhandled {:?}", evt)
                }
            }
            match evt.id {
                Some(id) =>
                    self.seenEvents.push(id),
                None => ()
            };
            for ref msg in messages {
                self.client.send(msg);
            }
        }
    }

    fn poll_matrix(&mut self, channel: mio::Sender<Event>) ->
        thread::JoinHandle<matrix::client::Result> {
        let poll = self.matrix.pollAsync();
        thread::spawn(move|| {
            poll.send().and_then(|evts| {
                for evt in evts {
                    channel.send(Event::Matrix(evt));
                };
                channel.send(Event::EndPoll);
                Ok(())
            })
        })
    }

    fn start_matrix(&mut self, channel: mio::Sender<Event>) ->
        matrix::client::Result {
        self.matrix.sync(|evt: matrix::events::Event| {
            channel.send(Event::Matrix(evt));
        }).and_then(|r| {
            self.poll_matrix(channel);
            Ok(r)
        })
    }

    fn handle_client(&mut self, events: &mut EventLoop<Bridge>) {
        loop {
            match self.client.read_message() {
                None => return,
                Some(message) => {
                    println!("Got a message! {:?}", message);
                    match message.command {
                        Command::Pass => {
                            self.client.auth.set_password(message.args[0].clone())
                        }
                        Command::Nick => {
                            self.client.set_nickname(message.args[0].clone());
                        },
                        Command::User => {
                            println!("User logged in: {}", message.args[0]);
                            self.client.auth.set_username(message.args[0].clone());
                            let auth = self.client.auth.consume();
                            match (auth.username, auth.password) {
                                (Some(username), Some(password)) => {
                                    self.matrix.login(username.trim(), password.trim())
                                        .and(self.start_matrix(events.channel()))
                                        .and_then(|_| {
                                            self.client.welcome(username.trim());
                                            println!("Logged in {:?}", username);
                                            Ok(())
                                        }).expect("Could not login!");
                                },
                                _ => panic!("Username and/or password missing")
                            };
                        },
                        Command::Join => {
                            self.client.join(&message.args[0]);
                        },
                        Command::Ping => {
                            self.client.pong();
                        },
                        Command::Quit => {
                            // FIXME: Logout of matrix and exit thread
                            return;
                        },
                        Command::Privmsg => {
                            let uid = matrix::events::UserID::from_str("@tdfischer:localhost");
                            let roomid = matrix::events::RoomID::from_str("!SNCDinqFeGteFrlCoN:localhost");
                            let evt = matrix::events::EventData::Room(
                                roomid,
                                matrix::events::RoomEvent::Message(
                                    uid, message.suffix.unwrap()));
                            self.seenEvents.push(self.matrix.send(evt));
                        },
                        _ =>
                            println!("unhandled {:?}", message)
                    }
                }
            }
        }
    }
}

<commit_msg>bridge: server name is almost configurable now<commit_after>use irc;
use matrix;
use irc::protocol::{Command,Message};
use mio;
use mio::{EventLoop,Handler,Token,EventSet,PollOpt,Sender};
use std::thread;
use std::collections::HashMap;

const CLIENT: Token = Token(0);
const MATRIX: Token = Token(1);

#[derive(Debug)]
pub enum Event {
    EndPoll,
    Matrix(matrix::events::Event)
}

pub struct Bridge {
    client: irc::streams::Client,
    matrix: matrix::client::Client,
    rooms: HashMap<matrix::events::RoomID, Room>,
    seenEvents: Vec<matrix::events::EventID>,
    server_name: String,
}

impl Handler for Bridge {
    type Timeout = ();
    type Message = Event;

    fn ready(&mut self, event_loop: &mut EventLoop<Bridge>, token: Token, _: EventSet) {
        match token {
            CLIENT =>
                self.handle_client(event_loop),
            _ => unreachable!()
        }
    }

    fn notify(&mut self, event_loop: &mut EventLoop<Bridge>, msg: Self::Message) {
        println!("Got message from matrix! {:?}", msg);
        match msg {
            Event::EndPoll => {
                self.poll_matrix(event_loop.channel());
            },
            Event::Matrix(e) =>
                self.handle_matrix(e)
        };
    }
}

unsafe impl Sync for Bridge{}

struct Room {
    id: matrix::events::RoomID,
    canonical_alias: Option<String>,
    join_rules: Option<String>,
    members: Vec<matrix::events::UserID>,
    pending_events: Vec<matrix::events::RoomEvent>
}

impl Room {
    fn new(id: matrix::events::RoomID) -> Self {
        Room {
            id: id,
            canonical_alias: None,
            join_rules: None,
            members: vec![],
            pending_events: vec![]
        }
    }

    fn run_pending<F>(&mut self, mut callback: &mut F)
            where F: FnMut(irc::protocol::Message) {
        while let Some(evt) = self.pending_events.pop() {
            self.handle_with_alias(evt, callback);
        }
    }

    fn handle_with_alias<F>(&mut self, evt: matrix::events::RoomEvent, mut callback: &mut F)
            where F: FnMut(irc::protocol::Message) {
        if self.canonical_alias != None {
            match evt {
                matrix::events::RoomEvent::Membership(user, matrix::events::MembershipAction::Join) => {
                    callback(irc::protocol::Message {
                        prefix: Some(format!("{}@anony.oob", user.nickname)),
                        command: irc::protocol::Command::Join,
                        args: vec![self.canonical_alias.clone().unwrap()],
                        suffix: None
                    });
                    self.members.push(user);
                },
                matrix::events::RoomEvent::Membership(user, matrix::events::MembershipAction::Leave) => {
                    if self.canonical_alias != None {
                        callback(irc::protocol::Message {
                            prefix: Some(format!("{}@anony.oob", user.nickname)),
                            command: irc::protocol::Command::Part,
                            args: vec![self.canonical_alias.clone().unwrap()],
                            suffix: None
                        });
                    }
                    self.members.push(user);
                },
                matrix::events::RoomEvent::Membership(user, _) => (),
                matrix::events::RoomEvent::Message(user, text) => {
                    callback(irc::protocol::Message {
                        prefix: Some(format!("{}@anony.oob", user.nickname)),
                        command: irc::protocol::Command::Privmsg,
                        args: vec![self.canonical_alias.clone().unwrap()],
                        suffix: Some(text)
                    });
                },
                matrix::events::RoomEvent::Topic(user, topic) => {
                    callback(irc::protocol::Message {
                        prefix: Some(format!("{}@anony.oob", user.nickname)),
                        command: irc::protocol::Command::Topic,
                        args: vec![self.canonical_alias.clone().unwrap()],
                        suffix: Some(topic.clone())
                    });
                },
                matrix::events::RoomEvent::CanonicalAlias(_) => unreachable!(),
                _ => {
                    println!("Unhandled event {:?}", evt)
                }
            }
        } else {
            self.pending_events.push(evt);
        }
    }

    fn handle_event<F>(&mut self, evt: matrix::events::RoomEvent, mut callback: F)
            where F: FnMut(irc::protocol::Message) {
        match evt {
            matrix::events::RoomEvent::CanonicalAlias(name) => {
                let was_empty = self.canonical_alias == None;
                self.canonical_alias = Some(name.clone());
                for ref user in &self.members {
                    callback(irc::protocol::Message {
                        prefix: Some(format!("{}@anony.oob", user.nickname)),
                        command: irc::protocol::Command::Join,
                        args: vec![self.canonical_alias.clone().unwrap()],
                        suffix: None
                    });
                };
                if was_empty {
                    self.run_pending(&mut callback);
                }
            },
            matrix::events::RoomEvent::JoinRules(rules) =>
                self.join_rules = Some(rules.clone()),
            matrix::events::RoomEvent::Create => (),
            matrix::events::RoomEvent::Aliases => (),
            matrix::events::RoomEvent::PowerLevels => (),
            matrix::events::RoomEvent::HistoryVisibility(_) => (),
            matrix::events::RoomEvent::Name(_, _) => (),
            matrix::events::RoomEvent::Avatar(_, _) => (),
            matrix::events::RoomEvent::Unknown(json) => {
                println!("Unknown room event: {:?}", json);
            }
            _ => self.handle_with_alias(evt, &mut callback)
        };
    }
}

impl Bridge {
    pub fn room(&mut self, id: &matrix::events::RoomID) -> &mut Room {
        if !self.rooms.contains_key(id) {
            self.rooms.insert(id.clone(), Room::new(id.clone()));
        }
        match self.rooms.get_mut(id) {
            Some(room) => room,
            None => unreachable!()
        }
    }

    pub fn new(client: irc::streams::Client, url: &str) -> Self {
        Bridge {
            client: client,
            matrix: matrix::client::Client::new(url),
            rooms: HashMap::new(),
            seenEvents: vec![],
            server_name: "oob.systems".to_string()
        }
    }

    pub fn run(&mut self) {
        let mut events = EventLoop::new().unwrap();
        events.register(self.client.stream(), CLIENT, EventSet::all(), PollOpt::edge()).unwrap();
        events.run(self).unwrap();
    }

    fn handle_matrix(&mut self, evt: matrix::events::Event) {
        let duplicate = match evt.id {
            Some(ref id) =>
                self.seenEvents.contains(id),
            _ => false
        };
        if !duplicate {
            let mut messages: Vec<irc::protocol::Message> = vec![];
            {
                let appendMsg = |msg: irc::protocol::Message| {
                    messages.push(msg);
                };
                match evt.data {
                    matrix::events::EventData::Room(room_id, room_event) => {
                        self.room(&room_id).handle_event(room_event, appendMsg);
                    },
                    _ => println!("Unhandled {:?}", evt)
                }
            }
            match evt.id {
                Some(id) =>
                    self.seenEvents.push(id),
                None => ()
            };
            for ref msg in messages {
                self.client.send(msg);
            }
        }
    }

    fn poll_matrix(&mut self, channel: mio::Sender<Event>) ->
        thread::JoinHandle<matrix::client::Result> {
        let poll = self.matrix.pollAsync();
        thread::spawn(move|| {
            poll.send().and_then(|evts| {
                for evt in evts {
                    channel.send(Event::Matrix(evt));
                };
                channel.send(Event::EndPoll);
                Ok(())
            })
        })
    }

    fn start_matrix(&mut self, channel: mio::Sender<Event>) ->
        matrix::client::Result {
        self.matrix.sync(|evt: matrix::events::Event| {
            channel.send(Event::Matrix(evt));
        }).and_then(|r| {
            self.poll_matrix(channel);
            Ok(r)
        })
    }

    fn handle_client(&mut self, events: &mut EventLoop<Bridge>) {
        loop {
            match self.client.read_message() {
                None => return,
                Some(message) => {
                    println!("Got a message! {:?}", message);
                    match message.command {
                        Command::Pass => {
                            self.client.auth.set_password(message.args[0].clone())
                        }
                        Command::Nick => {
                            self.client.set_nickname(message.args[0].clone());
                        },
                        Command::User => {
                            println!("User logged in: {}", message.args[0]);
                            self.client.auth.set_username(message.args[0].clone());
                            let auth = self.client.auth.consume();
                            match (auth.username, auth.password) {
                                (Some(username), Some(password)) => {
                                    self.matrix.login(username.trim(), password.trim())
                                        .and(self.start_matrix(events.channel()))
                                        .and_then(|_| {
                                            self.client.welcome(username.trim());
                                            println!("Logged in {:?}", username);
                                            Ok(())
                                        }).expect("Could not login!");
                                },
                                _ => panic!("Username and/or password missing")
                            };
                        },
                        Command::Join => {
                            self.client.join(&message.args[0]);
                        },
                        Command::Ping => {
                            self.client.pong();
                        },
                        Command::Quit => {
                            // FIXME: Logout of matrix and exit thread
                            return;
                        },
                        Command::Privmsg => {
                            let uid = matrix::events::UserID::from_str("@tdfischer:localhost");
                            let roomid = matrix::events::RoomID::from_str("!SNCDinqFeGteFrlCoN:localhost");
                            let evt = matrix::events::EventData::Room(
                                roomid,
                                matrix::events::RoomEvent::Message(
                                    uid, message.suffix.unwrap()));
                            self.seenEvents.push(self.matrix.send(evt));
                        },
                        _ =>
                            println!("unhandled {:?}", message)
                    }
                }
            }
        }
    }
}

<|endoftext|>


<commit_before>/*
 * Copyright 2015-2016 Torrie Fischer <tdfischer@hackerbots.net>
 *
 * 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 irc;
use matrix;
use irc::protocol::{Command,Message};
use irc::streams::AsEvented;
use mio;
use mio::{EventLoop,Handler,Token,EventSet,PollOpt,Sender};
use std::thread;
use std::collections::HashMap;
use std::io;
use hyper;

const CLIENT: Token = Token(0);

#[derive(Debug)]
pub enum Event {
    EndPoll,
    Matrix(matrix::events::Event)
}

pub struct Bridge {
    client: irc::streams::Client,
    matrix: matrix::client::Client,
    rooms: HashMap<matrix::model::RoomID, Room>,
    seen_events: Vec<matrix::model::EventID>,
    last_token: String
}

impl Handler for Bridge {
    type Timeout = ();
    type Message = Event;

    fn ready(&mut self, event_loop: &mut EventLoop<Bridge>, token: Token, _: EventSet) {
        match token {
            CLIENT =>
                self.handle_client(event_loop),
            _ => unreachable!("Got a really weird Token in the mio event loop!")
        }
    }

    fn notify(&mut self, event_loop: &mut EventLoop<Bridge>, msg: Self::Message) {
        match msg {
            Event::EndPoll => {
                self.poll_matrix(event_loop.channel());
            },
            Event::Matrix(e) =>
                match self.handle_matrix(e) {
                    Err(err) => warn!("Could not handle matrix event: {:?}", err),
                    _ => ()
                }
        };
    }
}

unsafe impl Sync for Bridge{}

struct Room {
    id: matrix::model::RoomID,
    irc_name: Option<String>,
    canonical_alias: Option<String>,
    join_rules: Option<String>,
    members: Vec<matrix::model::UserID>,
    aliases: Vec<String>,
    pending_events: Vec<matrix::events::RoomEvent>,
    pending_sync: bool
}

impl Room {
    fn userid_to_irc(uid: &matrix::model::UserID) -> String {
        format!("{}!{}@{}", uid.nickname, uid.nickname, uid.homeserver)
    }

    fn has_irc_name(&self) -> bool {
        self.irc_name != None
    }

    fn handle_part<F>(&mut self, user: matrix::model::UserID, mut callback: &mut F)
            where F: FnMut(irc::protocol::Message) {

        if self.irc_name != None && self.members.contains(&user) {
            callback(irc::protocol::Message {
                prefix: Some(format!("{}!{}@{}", user.nickname, user.nickname, user.homeserver)),
                command: irc::protocol::Command::Part,
                args: vec![self.irc_name.clone().unwrap()],
                suffix: None
            });
        }

        match self.members.iter().position(|u| u == &user) {
            Some(idx) => {
                self.members.remove(idx);
            },
            None => ()
        }
    }

    fn handle_join<F>(&mut self, user: matrix::model::UserID, mut callback: &mut F)
            where F: FnMut(irc::protocol::Message) {
        if self.irc_name != None && !self.members.contains(&user) {
            callback(irc::protocol::Message {
                prefix: Some(format!("{}!{}@{}", user.nickname, user.nickname, user.homeserver)),
                command: irc::protocol::Command::Join,
                args: vec![self.irc_name.clone().unwrap()],
                suffix: None
            });
        }
        self.members.push(user);
    }

    fn new(id: matrix::model::RoomID) -> Self {
        Room {
            id: id,
            canonical_alias: None,
            join_rules: None,
            members: vec![],
            pending_events: vec![],
            aliases: vec![],
            pending_sync: true,
            irc_name: None
        }
    }

    fn run_pending<F>(&mut self, mut callback: &mut F)
            where F: FnMut(irc::protocol::Message) {
        assert!(self.pending_sync);
        while let Some(evt) = self.pending_events.pop() {
            self.handle_with_alias(evt, callback);
        }
    }

    pub fn finish_sync<F>(&mut self, my_uid: &matrix::model::UserID, mut callback: &mut F)
            where F: FnMut(irc::protocol::Message) {
        for a in &self.aliases {
            if a.ends_with(&*format!(":{}", my_uid.homeserver)) {
                self.irc_name = Some(a.clone());
                break;
            }
        }
        if self.irc_name == None {
            self.irc_name = match self.canonical_alias {
                None => {
                    if self.aliases.len() == 0 {
                        Some(format!("#{}:{}", self.id.id, self.id.homeserver))
                    } else {
                        Some(self.aliases[0].clone())
                    }
                },
                Some(ref a) => Some(a.clone())
            }
        }
        callback(irc::protocol::Message {
            prefix: Some(format!("{}!{}@{}", my_uid.nickname, my_uid.nickname, my_uid.homeserver)),
            command: irc::protocol::Command::Join,
            args: vec![self.irc_name.clone().unwrap()],
            suffix: None
        });
        let mut usernames: Vec<String> = vec![];
        for u in &self.members {
            usernames.push(format!("{}", u.nickname));
        }
        callback(irc::protocol::Message {
            prefix: Some("pto".to_string()),
            command: irc::protocol::Command::Numeric(353),
            args: vec![my_uid.nickname.clone(), "@".to_string(), self.irc_name.clone().unwrap()],
            suffix: Some(usernames.join(" "))
        });

        self.run_pending(callback);
    }

    fn handle_with_alias<F>(&mut self, evt: matrix::events::RoomEvent, mut callback: &mut F)
            where F: FnMut(irc::protocol::Message) {
        if self.irc_name != None {
            match evt {
                matrix::events::RoomEvent::Membership(_, _) => (),
                matrix::events::RoomEvent::Message(user, text) => {
                    callback(irc::protocol::Message {
                        prefix: Some(format!("{}!{}@{}", user.nickname, user.nickname, user.homeserver)),
                        command: irc::protocol::Command::Privmsg,
                        args: vec![self.irc_name.clone().unwrap()],
                        suffix: Some(text)
                    });
                },
                matrix::events::RoomEvent::Topic(user, topic) => {
                    callback(irc::protocol::Message {
                        prefix: Some(format!("{}!{}@{}", user.nickname, user.nickname, user.homeserver)),
                        command: irc::protocol::Command::Topic,
                        args: vec![self.irc_name.clone().unwrap()],
                        suffix: Some(topic.clone())
                    });
                },
                _ => {
                    warn!("Unhandled event {:?}", evt)
                }
            }
        } else {
            self.pending_events.push(evt);
        }
    }

    fn handle_event<F>(&mut self, evt: matrix::events::RoomEvent, mut callback: F)
            where F: FnMut(irc::protocol::Message) {
        match evt {
            matrix::events::RoomEvent::CanonicalAlias(name) => {
                self.canonical_alias = Some(name.clone());
            },
            matrix::events::RoomEvent::JoinRules(rules) =>
                self.join_rules = Some(rules.clone()),
            matrix::events::RoomEvent::Create => (),
            matrix::events::RoomEvent::Aliases(aliases) =>
                self.aliases = aliases,
            matrix::events::RoomEvent::PowerLevels => (),
            matrix::events::RoomEvent::HistoryVisibility(_) => (),
            matrix::events::RoomEvent::Name(_, _) => (),
            matrix::events::RoomEvent::Avatar(_, _) => (),
            matrix::events::RoomEvent::Membership(user, matrix::events::MembershipAction::Join) => {
                self.handle_join(user, &mut callback);
            },
            matrix::events::RoomEvent::Membership(user, matrix::events::MembershipAction::Leave) => {
                self.handle_part(user, &mut callback);
            },
            matrix::events::RoomEvent::Unknown(unknown_type, json) => {
                warn!("Unknown room event {}", unknown_type);
                trace!("raw event: {:?}", json);
            }
            _ => self.handle_with_alias(evt, &mut callback)
        };
    }
}

impl Bridge {
    fn room_from_matrix(&mut self, id: &matrix::model::RoomID) -> &mut Room {
        if !self.rooms.contains_key(id) {
            self.rooms.insert(id.clone(), Room::new(id.clone()));
        }
        match self.rooms.get_mut(id) {
            Some(room) => room,
            None => unreachable!("Couldn't find the room that we just created")
        }
    }

    fn room_from_irc(&mut self, id: &String) -> Option<&mut Room> {
        let mut room_id: Option<matrix::model::RoomID> = None;
        for (_, r) in self.rooms.iter_mut() {
            if let Some(ref alias) = r.irc_name {
                if alias == id {
                    room_id = Some(r.id.clone())
                }
            }
        }
        match room_id {
            Some(id) => Some(self.room_from_matrix(&id)),
            None => None
        }
    }

    pub fn new(client: irc::streams::Client, url: hyper::Url) -> Self {
        Bridge {
            client: client,
            matrix: matrix::client::Client::new(url),
            rooms: HashMap::new(),
            seen_events: vec![],
            last_token: String::new()
        }
    }

    pub fn run(&mut self) {
        let mut events = EventLoop::new().unwrap();
        events.register(self.client.as_evented(), CLIENT, EventSet::all(), PollOpt::edge()).unwrap();
        events.run(self).unwrap();
    }

    fn finish_sync<F>(&mut self, mut callback: &mut F, token: String)
            where F: FnMut(irc::protocol::Message) {
        for (_, mut room) in &mut self.rooms {
            room.finish_sync(&self.matrix.uid.as_ref().unwrap(), callback);
        };
        self.last_token = token;
    }

    fn handle_matrix(&mut self, evt: matrix::events::Event) -> io::Result<usize> {
        let duplicate = match evt.id {
            Some(ref id) =>
                self.seen_events.contains(id),
            _ => false
        };
        if !duplicate {
            let mut messages: Vec<irc::protocol::Message> = vec![];
            {
                let mut append_msg = |msg: irc::protocol::Message| {
                    messages.push(msg);
                };
                match evt.data {
                    matrix::events::EventData::Room(room_id, room_event) => {
                        self.room_from_matrix(&room_id).handle_event(room_event, append_msg);
                    },
                    matrix::events::EventData::Typing(_) => (),
                    matrix::events::EventData::EndOfSync(token) => self.finish_sync(&mut append_msg, token),
                    _ => warn!("Unhandled {}", evt.data.type_str())
                }
            }
            match evt.id {
                Some(id) =>
                    self.seen_events.push(id),
                None => ()
            };
            let mut res: Option<io::Result<usize>> = None;
            for ref msg in messages {
                res = Some(match res {
                    None => self.client.send(msg),
                    Some(r) => r.and(self.client.send(msg))
                })
            }
            match res {
                None => Ok(0),
                Some(e) => e
            }
        } else {
            Ok(0)
        }
    }

    fn poll_matrix(&mut self, channel: mio::Sender<Event>) ->
        thread::JoinHandle<matrix::client::Result> {
        let poll = self.matrix.sync(Some(&*self.last_token));
        thread::spawn(move|| {
            poll.send().and_then(|evts| {
                for evt in evts {
                    channel.send(Event::Matrix(evt)).unwrap();
                };
                channel.send(Event::EndPoll).unwrap();
                Ok(())
            })
        })
    }

    fn start_matrix(&mut self, channel: mio::Sender<Event>) ->
        matrix::client::Result {
        self.matrix.sync(None).send().and_then(|events| {
            for e in events {
                match self.handle_matrix(e) {
                    // FIXME: Return error
                    Err(err) => warn!("Could not handle matrix event: {:?}", err),
                    _ => ()
                }
            }
            self.poll_matrix(channel);
            Ok(())
        })
    }

    fn handle_client(&mut self, events: &mut EventLoop<Bridge>) {
        loop {
            match self.client.read_message() {
                None => return,
                Some(message) => {
                    match message.command {
                        Command::Pass => {
                            self.client.auth.set_password(message.args[0].clone())
                        }
                        Command::Nick => {
                            let nickname = match message.suffix {
                                None => message.args[0].clone(),
                                Some(n) => n
                            };
                            self.client.set_nickname(nickname)
                        },
                        Command::User => {
                            self.client.auth.set_username(message.args[0].clone());
                            let auth = self.client.auth.consume();
                            match (auth.username, auth.password) {
                                (Some(username), Some(password)) => {
                                    self.matrix.login(&*username, &*password)
                                        .and_then(|_| {
                                            self.start_matrix(events.channel())
                                        })
                                        .and_then(|_| {
                                            self.client.welcome("Welcome to Perpetually Talking Online!").unwrap();
                                            debug!("Logged in a user");
                                            Ok(())
                                        }).expect("Could not login!");
                                },
                                _ => panic!("Username and/or password missing, and anonymous access isn't built yet.")
                            };
                        },
                        Command::Join => {
                            // FIXME: Send no such channel message
                        },
                        Command::Ping => {
                            self.client.pong().expect("Could not send PONG");
                        },
                        Command::Quit => {
                            // FIXME: Logout of matrix and exit thread
                            return;
                        },
                        Command::Privmsg => {
                            let room_id = match self.room_from_irc(&message.args[0]) {
                                None => return (),
                                Some(room) => room.id.clone()
                            };
                            let evt = {
                                let id = self.matrix.uid.clone().unwrap();
                                matrix::events::EventData::Room(
                                    room_id,
                                    matrix::events::RoomEvent::Message(
                                        id, message.suffix.unwrap()))
                            };
                            self.seen_events.push(self.matrix.send(evt).expect("Could not send event"));
                        },
                        _ =>
                            warn!("unhandled {:?}", message)
                    }
                }
            }
        }
    }
}

<commit_msg>bridge: use a BTreeSet intead of a Vec for room membership<commit_after>/*
 * Copyright 2015-2016 Torrie Fischer <tdfischer@hackerbots.net>
 *
 * 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 irc;
use matrix;
use irc::protocol::{Command,Message};
use irc::streams::AsEvented;
use mio;
use mio::{EventLoop,Handler,Token,EventSet,PollOpt,Sender};
use std::thread;
use std::collections::{HashMap, BTreeSet};
use std::io;
use hyper;

const CLIENT: Token = Token(0);

#[derive(Debug)]
pub enum Event {
    EndPoll,
    Matrix(matrix::events::Event)
}

pub struct Bridge {
    client: irc::streams::Client,
    matrix: matrix::client::Client,
    rooms: HashMap<matrix::model::RoomID, Room>,
    seen_events: Vec<matrix::model::EventID>,
    last_token: String
}

impl Handler for Bridge {
    type Timeout = ();
    type Message = Event;

    fn ready(&mut self, event_loop: &mut EventLoop<Bridge>, token: Token, _: EventSet) {
        match token {
            CLIENT =>
                self.handle_client(event_loop),
            _ => unreachable!("Got a really weird Token in the mio event loop!")
        }
    }

    fn notify(&mut self, event_loop: &mut EventLoop<Bridge>, msg: Self::Message) {
        match msg {
            Event::EndPoll => {
                self.poll_matrix(event_loop.channel());
            },
            Event::Matrix(e) =>
                match self.handle_matrix(e) {
                    Err(err) => warn!("Could not handle matrix event: {:?}", err),
                    _ => ()
                }
        };
    }
}

unsafe impl Sync for Bridge{}

struct Room {
    id: matrix::model::RoomID,
    irc_name: Option<String>,
    canonical_alias: Option<String>,
    join_rules: Option<String>,
    members: BTreeSet<matrix::model::UserID>,
    aliases: Vec<String>,
    pending_events: Vec<matrix::events::RoomEvent>,
    pending_sync: bool
}

impl Room {
    fn userid_to_irc(uid: &matrix::model::UserID) -> String {
        format!("{}!{}@{}", uid.nickname, uid.nickname, uid.homeserver)
    }

    fn has_irc_name(&self) -> bool {
        self.irc_name != None
    }

    fn handle_part<F>(&mut self, user: matrix::model::UserID, mut callback: &mut F)
            where F: FnMut(irc::protocol::Message) {

        let did_exist = self.members.remove(&user);
        if self.has_irc_name() && did_exist {
            callback(irc::protocol::Message {
                prefix: Some(Room::userid_to_irc(&user)),
                command: irc::protocol::Command::Part,
                args: vec![self.irc_name.clone().unwrap()],
                suffix: None
            });
        }
    }

    fn handle_join<F>(&mut self, user: matrix::model::UserID, mut callback: &mut F)
            where F: FnMut(irc::protocol::Message) {
        let uid = Room::userid_to_irc(&user);
        let was_added = self.members.insert(user);
        if self.has_irc_name() && was_added {
            callback(irc::protocol::Message {
                prefix: Some(uid),
                command: irc::protocol::Command::Join,
                args: vec![self.irc_name.clone().unwrap()],
                suffix: None
            });
        }
    }

    fn new(id: matrix::model::RoomID) -> Self {
        Room {
            id: id,
            canonical_alias: None,
            join_rules: None,
            members: BTreeSet::new(),
            pending_events: vec![],
            aliases: vec![],
            pending_sync: true,
            irc_name: None
        }
    }

    fn run_pending<F>(&mut self, mut callback: &mut F)
            where F: FnMut(irc::protocol::Message) {
        assert!(self.pending_sync);
        while let Some(evt) = self.pending_events.pop() {
            self.handle_with_alias(evt, callback);
        }
    }

    fn update_irc_name(&mut self, my_uid: &matrix::model::UserID) {
        // First check if we have a local alias that matches our homeserver
        for a in &self.aliases {
            if a.ends_with(&*format!(":{}", my_uid.homeserver)) {
                self.irc_name = Some(a.clone());
                break;
            }
        }
        // If we didn't get one from above, try some other heuristics
        if self.irc_name == None {
            self.irc_name = match self.canonical_alias {
                // No canonical alias set. See if we can grab some other alias.
                None => {
                    if self.aliases.len() == 0 {
                        // A room with two people is probably a PM.
                        // FIXME: Some other heuristics around private rooms, permissions, etc
                        if self.members.len() == 2 {
                            self.is_pm = true;
                            Some(format!("{}", self.members.iter().nth(0).unwrap().nickname))
                        } else {
                            Some(format!("#{}:{}", self.id.id, self.id.homeserver))
                        }
                    } else {
                        // First one is good as any I guess!
                        Some(self.aliases[0].clone())
                    }
                },
                // There's a canonical_alias set, so use that
                Some(ref a) => Some(a.clone())
            }
        }
        callback(irc::protocol::Message {
            prefix: Some(format!("{}!{}@{}", my_uid.nickname, my_uid.nickname, my_uid.homeserver)),
            command: irc::protocol::Command::Join,
            args: vec![self.irc_name.clone().unwrap()],
            suffix: None
        });
        let mut usernames: Vec<String> = vec![];
        for u in &self.members {
            usernames.push(format!("{}", u.nickname));
        }
        callback(irc::protocol::Message {
            prefix: Some("pto".to_string()),
            command: irc::protocol::Command::Numeric(353),
            args: vec![my_uid.nickname.clone(), "@".to_string(), self.irc_name.clone().unwrap()],
            suffix: Some(usernames.join(" "))
        });

        self.run_pending(callback);
    }

    fn handle_with_alias<F>(&mut self, evt: matrix::events::RoomEvent, mut callback: &mut F)
            where F: FnMut(irc::protocol::Message) {
        if self.has_irc_name() {
            match evt {
                matrix::events::RoomEvent::Membership(_, _) => (),
                matrix::events::RoomEvent::Message(user, text) => {
                    callback(irc::protocol::Message {
                        prefix: Some(format!("{}!{}@{}", user.nickname, user.nickname, user.homeserver)),
                        command: irc::protocol::Command::Privmsg,
                        args: vec![self.irc_name.clone().unwrap()],
                        suffix: Some(text)
                    });
                },
                matrix::events::RoomEvent::Topic(user, topic) => {
                    callback(irc::protocol::Message {
                        prefix: Some(Room::userid_to_irc(&user)),
                        command: irc::protocol::Command::Topic,
                        args: vec![self.irc_name.clone().unwrap()],
                        suffix: Some(topic.clone())
                    });
                },
                _ => {
                    warn!("Unhandled event {:?}", evt)
                }
            }
        } else {
            self.pending_events.push(evt);
        }
    }

    fn handle_event<F>(&mut self, evt: matrix::events::RoomEvent, mut callback: F)
            where F: FnMut(irc::protocol::Message) {
        match evt {
            matrix::events::RoomEvent::CanonicalAlias(name) => {
                self.canonical_alias = Some(name.clone());
            },
            matrix::events::RoomEvent::JoinRules(rules) =>
                self.join_rules = Some(rules.clone()),
            matrix::events::RoomEvent::Create => (),
            matrix::events::RoomEvent::Aliases(aliases) =>
                self.aliases = aliases,
            matrix::events::RoomEvent::PowerLevels => (),
            matrix::events::RoomEvent::HistoryVisibility(_) => (),
            matrix::events::RoomEvent::Name(_, _) => (),
            matrix::events::RoomEvent::Avatar(_, _) => (),
            matrix::events::RoomEvent::Membership(user, matrix::events::MembershipAction::Join) => {
                self.handle_join(user, &mut callback);
            },
            matrix::events::RoomEvent::Membership(user, matrix::events::MembershipAction::Leave) => {
                self.handle_part(user, &mut callback);
            },
            matrix::events::RoomEvent::Unknown(unknown_type, json) => {
                warn!("Unknown room event {}", unknown_type);
                trace!("raw event: {:?}", json);
            }
            _ => self.handle_with_alias(evt, &mut callback)
        };
    }
}

impl Bridge {
    fn room_from_matrix(&mut self, id: &matrix::model::RoomID) -> &mut Room {
        if !self.rooms.contains_key(id) {
            self.rooms.insert(id.clone(), Room::new(id.clone()));
        }
        match self.rooms.get_mut(id) {
            Some(room) => room,
            None => unreachable!("Couldn't find the room that we just created")
        }
    }

    fn room_from_irc(&mut self, id: &String) -> Option<&mut Room> {
        let mut room_id: Option<matrix::model::RoomID> = None;
        for (_, r) in self.rooms.iter_mut() {
            if let Some(ref alias) = r.irc_name {
                if alias == id {
                    room_id = Some(r.id.clone())
                }
            }
        }
        match room_id {
            Some(id) => Some(self.room_from_matrix(&id)),
            None => None
        }
    }

    pub fn new(client: irc::streams::Client, url: hyper::Url) -> Self {
        Bridge {
            client: client,
            matrix: matrix::client::Client::new(url),
            rooms: HashMap::new(),
            seen_events: vec![],
            last_token: String::new()
        }
    }

    pub fn run(&mut self) {
        let mut events = EventLoop::new().unwrap();
        events.register(self.client.as_evented(), CLIENT, EventSet::all(), PollOpt::edge()).unwrap();
        events.run(self).unwrap();
    }

    fn finish_sync<F>(&mut self, mut callback: &mut F, token: String)
            where F: FnMut(irc::protocol::Message) {
        for (_, mut room) in &mut self.rooms {
            room.finish_sync(&self.matrix.uid.as_ref().unwrap(), callback);
        };
        self.last_token = token;
    }

    fn handle_matrix(&mut self, evt: matrix::events::Event) -> io::Result<usize> {
        let duplicate = match evt.id {
            Some(ref id) =>
                self.seen_events.contains(id),
            _ => false
        };
        if !duplicate {
            let mut messages: Vec<irc::protocol::Message> = vec![];
            {
                let mut append_msg = |msg: irc::protocol::Message| {
                    messages.push(msg);
                };
                match evt.data {
                    matrix::events::EventData::Room(room_id, room_event) => {
                        self.room_from_matrix(&room_id).handle_event(room_event, append_msg);
                    },
                    matrix::events::EventData::Typing(_) => (),
                    matrix::events::EventData::EndOfSync(token) => self.finish_sync(&mut append_msg, token),
                    _ => warn!("Unhandled {}", evt.data.type_str())
                }
            }
            match evt.id {
                Some(id) =>
                    self.seen_events.push(id),
                None => ()
            };
            let mut res: Option<io::Result<usize>> = None;
            for ref msg in messages {
                res = Some(match res {
                    None => self.client.send(msg),
                    Some(r) => r.and(self.client.send(msg))
                })
            }
            match res {
                None => Ok(0),
                Some(e) => e
            }
        } else {
            Ok(0)
        }
    }

    fn poll_matrix(&mut self, channel: mio::Sender<Event>) ->
        thread::JoinHandle<matrix::client::Result> {
        let poll = self.matrix.sync(Some(&*self.last_token));
        thread::spawn(move|| {
            poll.send().and_then(|evts| {
                for evt in evts {
                    channel.send(Event::Matrix(evt)).unwrap();
                };
                channel.send(Event::EndPoll).unwrap();
                Ok(())
            })
        })
    }

    fn start_matrix(&mut self, channel: mio::Sender<Event>) ->
        matrix::client::Result {
        self.matrix.sync(None).send().and_then(|events| {
            for e in events {
                match self.handle_matrix(e) {
                    // FIXME: Return error
                    Err(err) => warn!("Could not handle matrix event: {:?}", err),
                    _ => ()
                }
            }
            self.poll_matrix(channel);
            Ok(())
        })
    }

    fn handle_client(&mut self, events: &mut EventLoop<Bridge>) {
        loop {
            match self.client.read_message() {
                None => return,
                Some(message) => {
                    match message.command {
                        Command::Pass => {
                            self.client.auth.set_password(message.args[0].clone())
                        }
                        Command::Nick => {
                            let nickname = match message.suffix {
                                None => message.args[0].clone(),
                                Some(n) => n
                            };
                            self.client.set_nickname(nickname)
                        },
                        Command::User => {
                            self.client.auth.set_username(message.args[0].clone());
                            let auth = self.client.auth.consume();
                            match (auth.username, auth.password) {
                                (Some(username), Some(password)) => {
                                    self.matrix.login(&*username, &*password)
                                        .and_then(|_| {
                                            self.start_matrix(events.channel())
                                        })
                                        .and_then(|_| {
                                            self.client.welcome("Welcome to Perpetually Talking Online!").unwrap();
                                            debug!("Logged in a user");
                                            Ok(())
                                        }).expect("Could not login!");
                                },
                                _ => panic!("Username and/or password missing, and anonymous access isn't built yet.")
                            };
                        },
                        Command::Join => {
                            // FIXME: Send no such channel message
                        },
                        Command::Ping => {
                            self.client.pong().expect("Could not send PONG");
                        },
                        Command::Quit => {
                            // FIXME: Logout of matrix and exit thread
                            return;
                        },
                        Command::Privmsg => {
                            let room_id = match self.room_from_irc(&message.args[0]) {
                                None => return (),
                                Some(room) => room.id.clone()
                            };
                            let evt = {
                                let id = self.matrix.uid.clone().unwrap();
                                matrix::events::EventData::Room(
                                    room_id,
                                    matrix::events::RoomEvent::Message(
                                        id, message.suffix.unwrap()))
                            };
                            self.seen_events.push(self.matrix.send(evt).expect("Could not send event"));
                        },
                        _ =>
                            warn!("unhandled {:?}", message)
                    }
                }
            }
        }
    }
}

<|endoftext|>


<commit_before>use std::rc::{Rc};
use std::cell::{RefCell};
use std::mem::{transmute};

pub struct Broker<T: Subscriber> {
    subscriptions: Subscriptions<T>,
}

impl<T: Subscriber> Broker<T> {
    pub fn new() -> Self {
        Broker{ subscriptions: Subscriptions::<T>::new()}
    }

    pub fn publish(&mut self, topic: &str, payload: &[u8]) {
        self.subscriptions.publish(topic, payload);
    }

    pub fn subscribe(&mut self, subscriber: Rc<RefCell<T>>, topics: &[&str]) {
        for sub in &self.subscriptions.subscribers {
            let sub1 = sub.clone();
            let sub2 = subscriber.clone();
            if sub1.borrow().id() == sub2.borrow().id() {
                sub2.borrow_mut().append_topics(topics);
                return;
            }
        }
        self.subscriptions.subscribers.push(subscriber);
        let lesub = self.subscriptions.subscribers[self.subscriptions.subscribers.len() - 1].clone();
        lesub.borrow_mut().append_topics(topics);
    }
}

pub struct Subscriptions<T: Subscriber> {
    subscribers: Vec<Rc<RefCell<T>>>,
}

impl<T: Subscriber> Subscriptions<T> {
    fn new() -> Self {
        return Subscriptions { subscribers: vec![], }
    }

    fn publish(&mut self, topic: &str, payload: &[u8]) {
        for s in &mut self.subscribers {
            let sub_clone = s.clone();
            let mut s = sub_clone.borrow_mut();

            for t in s.topics() {
                if t == topic {
                    s.new_message(payload);
                }
            }
        }
    }
}

pub trait Subscriber {
    fn new_message(&mut self, bytes: &[u8]);
    fn id(&self) -> i64;
    fn append_topics(&mut self, topics: &[&str]);
    fn topics(&self) -> Vec<String>;
}

#[cfg(test)]
struct TestSubscriber {
    msgs: Vec<Vec<u8>>,
    topics: Vec<String>,
}
#[cfg(test)]
impl TestSubscriber {
    fn new() -> Self {
        return TestSubscriber{msgs: vec![], topics: vec![]}
    }
}

#[cfg(test)]
impl Subscriber for TestSubscriber {

    fn new_message(&mut self, bytes: &[u8]) {
        self.msgs.push(bytes.to_vec());
    }

    fn id(&self) -> i64 {
        self as *const TestSubscriber as i64
    }

    fn append_topics(&mut self, topics: &[&str]) {
        let mut new_topics = topics.to_vec().into_iter().map(|t| t.to_string()).collect();
        self.topics.append(&mut new_topics);
        println!("after append, len is {}", self.topics.len());
    }

    fn topics(&self) -> Vec<String> {
        println!("self topics len: {}", self.topics.len());
        self.topics.clone()
    }
}

#[test]
fn test_subscribe() {
    let mut broker = Broker::<TestSubscriber>::new();
    let sub_ref = Rc::new(RefCell::new(TestSubscriber::new()));
    let subscriber = sub_ref.clone();
    broker.publish("topics/foo", &[0, 1, 2]);
    assert_eq!(subscriber.borrow().msgs.len(), 0);

    broker.subscribe(subscriber.clone(), &["topics/foo"]);
    broker.publish("topics/foo", &[0, 1, 9]);
    broker.publish("topics/bar", &[2, 4, 6]);
    assert_eq!(subscriber.borrow().msgs.len(), 1);
    assert_eq!(subscriber.borrow().msgs[0], &[0, 1, 9]);

    broker.subscribe(subscriber.clone(), &["topics/bar"]);
    broker.publish("topics/foo", &[1, 3, 5, 7]);
    broker.publish("topics/bar", &[2, 4]);
    assert_eq!(subscriber.borrow().msgs.len(), 3);
    assert_eq!(subscriber.borrow().msgs[0], &[0, 1, 9]);
    assert_eq!(subscriber.borrow().msgs[1], &[1, 3, 5, 7]);
    assert_eq!(subscriber.borrow().msgs[2], &[2, 4]);
}
<commit_msg>Removed println<commit_after>use std::rc::{Rc};
use std::cell::{RefCell};
use std::mem::{transmute};

pub struct Broker<T: Subscriber> {
    subscriptions: Subscriptions<T>,
}

impl<T: Subscriber> Broker<T> {
    pub fn new() -> Self {
        Broker{ subscriptions: Subscriptions::<T>::new()}
    }

    pub fn publish(&mut self, topic: &str, payload: &[u8]) {
        self.subscriptions.publish(topic, payload);
    }

    pub fn subscribe(&mut self, subscriber: Rc<RefCell<T>>, topics: &[&str]) {
        for sub in &self.subscriptions.subscribers {
            let sub1 = sub.clone();
            let sub2 = subscriber.clone();
            if sub1.borrow().id() == sub2.borrow().id() {
                sub2.borrow_mut().append_topics(topics);
                return;
            }
        }
        self.subscriptions.subscribers.push(subscriber);
        let lesub = self.subscriptions.subscribers[self.subscriptions.subscribers.len() - 1].clone();
        lesub.borrow_mut().append_topics(topics);
    }
}

pub struct Subscriptions<T: Subscriber> {
    subscribers: Vec<Rc<RefCell<T>>>,
}

impl<T: Subscriber> Subscriptions<T> {
    fn new() -> Self {
        return Subscriptions { subscribers: vec![], }
    }

    fn publish(&mut self, topic: &str, payload: &[u8]) {
        for s in &mut self.subscribers {
            let sub_clone = s.clone();
            let mut s = sub_clone.borrow_mut();

            for t in s.topics() {
                if t == topic {
                    s.new_message(payload);
                }
            }
        }
    }
}

pub trait Subscriber {
    fn new_message(&mut self, bytes: &[u8]);
    fn id(&self) -> i64;
    fn append_topics(&mut self, topics: &[&str]);
    fn topics(&self) -> Vec<String>;
}

#[cfg(test)]
struct TestSubscriber {
    msgs: Vec<Vec<u8>>,
    topics: Vec<String>,
}
#[cfg(test)]
impl TestSubscriber {
    fn new() -> Self {
        return TestSubscriber{msgs: vec![], topics: vec![]}
    }
}

#[cfg(test)]
impl Subscriber for TestSubscriber {

    fn new_message(&mut self, bytes: &[u8]) {
        self.msgs.push(bytes.to_vec());
    }

    fn id(&self) -> i64 {
        self as *const TestSubscriber as i64
    }

    fn append_topics(&mut self, topics: &[&str]) {
        let mut new_topics = topics.to_vec().into_iter().map(|t| t.to_string()).collect();
        self.topics.append(&mut new_topics);
    }

    fn topics(&self) -> Vec<String> {
        self.topics.clone()
    }
}

#[test]
fn test_subscribe() {
    let mut broker = Broker::<TestSubscriber>::new();
    let sub_ref = Rc::new(RefCell::new(TestSubscriber::new()));
    let subscriber = sub_ref.clone();
    broker.publish("topics/foo", &[0, 1, 2]);
    assert_eq!(subscriber.borrow().msgs.len(), 0);

    broker.subscribe(subscriber.clone(), &["topics/foo"]);
    broker.publish("topics/foo", &[0, 1, 9]);
    broker.publish("topics/bar", &[2, 4, 6]);
    assert_eq!(subscriber.borrow().msgs.len(), 1);
    assert_eq!(subscriber.borrow().msgs[0], &[0, 1, 9]);

    broker.subscribe(subscriber.clone(), &["topics/bar"]);
    broker.publish("topics/foo", &[1, 3, 5, 7]);
    broker.publish("topics/bar", &[2, 4]);
    assert_eq!(subscriber.borrow().msgs.len(), 3);
    assert_eq!(subscriber.borrow().msgs[0], &[0, 1, 9]);
    assert_eq!(subscriber.borrow().msgs[1], &[1, 3, 5, 7]);
    assert_eq!(subscriber.borrow().msgs[2], &[2, 4]);
}
<|endoftext|>