<commit_before>/*******************************************************************************
 *
 * kit/kernel/terminal.rs
 *
 * vim:ft=rust:ts=4:sw=4:et:tw=80
 *
 * Copyright (C) 2015, Devyn Cairns
 * Redistribution of this file is permitted under the terms of the simplified
 * BSD license. See LICENSE for more information.
 *
 ******************************************************************************/

//! Early text mode 80x25 terminal handler.

use core::prelude::*;

use core::fmt;

#[derive(Copy)]
pub enum Color {
    Black        = 0,
    Blue         = 1,
    Green        = 2,
    Cyan         = 3,
    Red          = 4,
    Magenta      = 5,
    Brown        = 6,
    LightGrey    = 7,
    DarkGrey     = 8,
    LightBlue    = 9,
    LightGreen   = 10,
    LightCyan    = 11,
    LightRed     = 12,
    LightMagenta = 13,
    LightBrown   = 14,
    White        = 15,
}

/// A terminal.
pub trait Terminal: fmt::Write {
    fn reset(&mut self) -> fmt::Result;
    fn clear(&mut self) -> fmt::Result;

    fn get_cursor(&self) -> (usize, usize);
    fn set_cursor(&mut self, row: usize, col: usize) -> fmt::Result;

    fn get_color(&self) -> (Color, Color);
    fn set_color(&mut self, fg: Color, bg: Color) -> fmt::Result;

    fn put_raw_byte(&mut self,
                    byte: u8,
                    fg:   Color,
                    bg:   Color,
                    row:  usize,
                    col:  usize) -> fmt::Result;

    /// Does not flush.
    fn write_raw_byte(&mut self, byte: u8) -> fmt::Result;

    /// Does not flush.
    fn write_raw_bytes(&mut self, bytes: &[u8]) -> fmt::Result {
        for byte in bytes {
            try!(self.write_raw_byte(*byte));
        }

        Ok(())
    }

    fn flush(&mut self) -> fmt::Result;

    fn write_char(&mut self, ch: char) -> fmt::Result {
        let mut buf = [0u8, 4];

        let size = try!(ch.encode_utf8(&mut buf).ok_or(fmt::Error));

        try!(self.write_raw_bytes(&buf[0..size]));
        try!(self.flush());
        Ok(())
    }
}

/// Controls a VGA text-mode terminal.
pub struct Vga {
    width:  usize,
    height: usize,
    row:    usize,
    col:    usize,
    fg:     Color,
    bg:     Color,
    attr:   u8,
    buffer: *mut u16,
    port:   u16
}

impl Vga {
    pub unsafe fn new(width:  usize,
                      height: usize,
                      buffer: *mut u16,
                      port:   u16)
                      -> Vga {

        let mut vga = Vga {
            width:  width,
            height: height,
            row:    0,
            col:    0,
            fg:     Color::LightGrey,
            bg:     Color::Black,
            attr:   Vga::attr(Color::LightGrey, Color::Black),
            buffer: buffer,
            port:   port
        };

        vga.reset().unwrap();

        vga
    }

    pub fn color(c: Color) -> u8 {
        c as u8
    }

    pub fn attr(fg: Color, bg: Color) -> u8 {
        Vga::color(fg) | (Vga::color(bg) << 4)
    }

    fn update_attr(&mut self) {
        self.attr = Vga::attr(self.fg, self.bg);
    }

    fn update_cursor(&mut self) {
        unsafe fn outb(byte: u8, port: u16) {
            asm!("out %al, %dx" :: "{ax}" (byte), "{dx}" (port) :: "volatile");
        }

        let pos: u16 = ((self.row * self.width) + self.col) as u16;

        unsafe {
            outb(0x0F,             self.port);
            outb(pos as u8,        self.port + 1);

            outb(0x0E,             self.port);
            outb((pos >> 8) as u8, self.port + 1);
        }
    }

    pub fn put(&mut self, byte: u8, attr: u8, row: usize, col: usize) {
        unsafe {
            *self.buffer.offset((row * self.width + col) as isize) =
                (byte as u16) | ((attr as u16) << 8);
        }
    }

    pub fn put_here(&mut self, byte: u8) {
        let (attr, row, col) = (self.attr, self.row, self.col);

        self.put(byte, attr, row, col)
    }

    fn new_line(&mut self) {
        // Clear to the end of the line.
        while self.col < self.width {
            self.put_here(' ' as u8);
            self.col += 1;
        }

        // Go to the next line, scrolling if necessary.
        self.col  = 0;
        self.row += 1;

        while self.row >= self.height {
            self.scroll();
            self.row -= 1;
        }

        self.update_cursor();
    }

    fn scroll(&mut self) {
        // Shift everything one line back.
        for row in 1..self.height {
            for col in 0..self.width {
                let index = (row * self.width + col) as isize;

                unsafe {
                    *self.buffer.offset(index - self.width as isize) =
                        *self.buffer.offset(index);
                }
            }
        }

        // Clear last line.
        let (attr, height) = (self.attr, self.height);

        for col in 0..self.width {
            self.put(' ' as u8, attr, height - 1, col);
        }
    }
}

impl Terminal for Vga {
    fn reset(&mut self) -> fmt::Result {
        self.fg = Color::LightGrey;
        self.bg = Color::Black;
        self.update_attr();
        self.clear()
    }

    fn clear(&mut self) -> fmt::Result {
        self.row = 0;
        self.col = 0;

        let attr = self.attr;

        for row in 0..self.height {
            for col in 0..self.width {
                self.put(' ' as u8, attr, row, col);
            }
        }

        Ok(())
    }

    fn get_cursor(&self) -> (usize, usize) {
        (self.row, self.col)
    }

    fn set_cursor(&mut self, row: usize, col: usize) -> fmt::Result {
        self.row = row;
        self.col = col;

        self.update_cursor();
        Ok(())
    }

    fn get_color(&self) -> (Color, Color) {
        (self.fg, self.bg)
    }

    fn set_color(&mut self, fg: Color, bg: Color) -> fmt::Result {
        self.fg = fg;
        self.bg = bg;
        self.update_attr();
        Ok(())
    }

    fn put_raw_byte(&mut self,
                    byte: u8,
                    fg:   Color,
                    bg:   Color,
                    row:  usize,
                    col:  usize) -> fmt::Result {

        self.put(byte, Vga::attr(fg, bg), row, col);
        Ok(())
    }

    fn write_raw_byte(&mut self, byte: u8) -> fmt::Result {
        match byte {
            0x0A /* newline */ => {
                self.new_line();
            },

            0x08 /* backspace */ => {
                if self.col > 0 {
                    self.col -= 1;
                }

                self.put_here(' ' as u8);
            },

            _ => {
                self.put_here(byte);

                if self.col + 1 >= self.width {
                    self.new_line();
                } else {
                    self.col += 1;
                }
            }
        }

        Ok(())
    }

    fn flush(&mut self) -> fmt::Result {
        self.update_cursor();
        Ok(())
    }
}

impl fmt::Write for Vga {
    fn write_str(&mut self, s: &str) -> fmt::Result {
        try!(self.write_raw_bytes(s.as_bytes()));
        try!(self.flush());
        Ok(())
    }
}

static mut CONSOLE: Option<Vga> = None;

/// Get the current global console.
pub fn console() -> &'static mut Terminal {
    unsafe {
        if CONSOLE.is_none() {
            CONSOLE = Some(Vga::new(80, 25,
                                    0xffffffff800b8000 as *mut u16, 0x3d4));
        }

        CONSOLE.as_mut().unwrap()
    }
}

/// C (legacy) interface. See `kit/kernel/include/terminal.h`.
pub mod ffi {
    use super::*;

    use core::mem;
    use core::slice;
    use core::ptr::PtrExt;

    use libc::{c_char, size_t};

    #[no_mangle]
    pub extern fn terminal_initialize() {
        console().reset().unwrap();
    }

    #[no_mangle]
    pub extern fn terminal_clear() {
        console().clear().unwrap();
    }

    #[no_mangle]
    pub extern fn terminal_updatecursor() {
        console().flush().unwrap();
    }

    #[no_mangle]
    pub unsafe extern fn terminal_getcursor(row: *mut size_t, column: *mut size_t) {
        let (row_us, col_us) = console().get_cursor();

        *row    = row_us as size_t;
        *column = col_us as size_t;
    }

    #[no_mangle]
    pub extern fn terminal_setcursor(row: size_t, column: size_t) {
        console().set_cursor(row as usize, column as usize).unwrap();
    }

    #[repr(C)]
    #[derive(Copy)]
    pub enum VgaColor {
        Black        = 0,
        Blue         = 1,
        Green        = 2,
        Cyan         = 3,
        Red          = 4,
        Magenta      = 5,
        Brown        = 6,
        LightGrey    = 7,
        DarkGrey     = 8,
        LightBlue    = 9,
        LightGreen   = 10,
        LightCyan    = 11,
        LightRed     = 12,
        LightMagenta = 13,
        LightBrown   = 14,
        White        = 15,
    }

    impl VgaColor {
        pub fn from_color(color: Color) -> VgaColor {
            unsafe { mem::transmute(color as i32) }
        }

        pub fn to_color(self) -> Color {
            unsafe { mem::transmute(self as u8) }
        }
    }

    #[no_mangle]
    pub unsafe extern fn terminal_getcolor(fg: *mut VgaColor,
                                           bg: *mut VgaColor) {
        let (fg_c, bg_c) = console().get_color();

        *fg = VgaColor::from_color(fg_c);
        *bg = VgaColor::from_color(bg_c);
    }

    #[no_mangle]
    pub extern fn terminal_setcolor(fg: VgaColor, bg: VgaColor) {
        console().set_color(fg.to_color(), bg.to_color()).unwrap();
    }

    #[no_mangle]
    pub unsafe extern fn terminal_putentryat(c: c_char,
                                      color: u8,
                                      x: size_t,
                                      y: size_t) {

        let fg_v: VgaColor = mem::transmute((color & 0x0f) as i32);
        let bg_v: VgaColor = mem::transmute((color >> 4)   as i32);

        console()
            .put_raw_byte(c as u8,
                          fg_v.to_color(),
                          bg_v.to_color(),
                          y as usize,
                          x as usize)
            .unwrap();
    }

    #[no_mangle]
    pub extern fn terminal_newline() {
        console().write_raw_byte('\n' as u8).unwrap();
        console().flush().unwrap();
    }

    #[no_mangle]
    pub extern fn terminal_writechar_internal(c: c_char) {
        console().write_raw_byte(c as u8).unwrap();
    }

    #[no_mangle]
    pub extern fn terminal_writechar(c: c_char) {
        console().write_raw_byte(c as u8).unwrap();
        console().flush().unwrap();
    }

    #[no_mangle]
    pub unsafe extern fn terminal_writebuf(length: u64, buffer: *const u8) {
        let bytes = slice::from_raw_parts(buffer, length as usize);

        console().write_raw_bytes(bytes).unwrap();
        console().flush().unwrap();
    }

    #[no_mangle]
    pub unsafe extern fn terminal_writestring(data: *const u8) {
        let mut data_len = 0usize;
        let mut data_end = data;

        while *data_end != 0 {
            data_len += 1;
            data_end = data_end.offset(1);
        }

        let bytes = slice::from_raw_parts(data, data_len);

        console().write_raw_bytes(bytes).unwrap();
        console().flush().unwrap();
    }
}
<commit_msg>[kernel] fix #6: terminal module now writes to col. 79 properly<commit_after>/*******************************************************************************
 *
 * kit/kernel/terminal.rs
 *
 * vim:ft=rust:ts=4:sw=4:et:tw=80
 *
 * Copyright (C) 2015, Devyn Cairns
 * Redistribution of this file is permitted under the terms of the simplified
 * BSD license. See LICENSE for more information.
 *
 ******************************************************************************/

//! Early text mode 80x25 terminal handler.

use core::prelude::*;

use core::fmt;

#[derive(Copy)]
pub enum Color {
    Black        = 0,
    Blue         = 1,
    Green        = 2,
    Cyan         = 3,
    Red          = 4,
    Magenta      = 5,
    Brown        = 6,
    LightGrey    = 7,
    DarkGrey     = 8,
    LightBlue    = 9,
    LightGreen   = 10,
    LightCyan    = 11,
    LightRed     = 12,
    LightMagenta = 13,
    LightBrown   = 14,
    White        = 15,
}

/// A terminal.
pub trait Terminal: fmt::Write {
    fn reset(&mut self) -> fmt::Result;
    fn clear(&mut self) -> fmt::Result;

    fn get_cursor(&self) -> (usize, usize);
    fn set_cursor(&mut self, row: usize, col: usize) -> fmt::Result;

    fn get_color(&self) -> (Color, Color);
    fn set_color(&mut self, fg: Color, bg: Color) -> fmt::Result;

    fn put_raw_byte(&mut self,
                    byte: u8,
                    fg:   Color,
                    bg:   Color,
                    row:  usize,
                    col:  usize) -> fmt::Result;

    /// Does not flush.
    fn write_raw_byte(&mut self, byte: u8) -> fmt::Result;

    /// Does not flush.
    fn write_raw_bytes(&mut self, bytes: &[u8]) -> fmt::Result {
        for byte in bytes {
            try!(self.write_raw_byte(*byte));
        }

        Ok(())
    }

    fn flush(&mut self) -> fmt::Result;

    fn write_char(&mut self, ch: char) -> fmt::Result {
        let mut buf = [0u8, 4];

        let size = try!(ch.encode_utf8(&mut buf).ok_or(fmt::Error));

        try!(self.write_raw_bytes(&buf[0..size]));
        try!(self.flush());
        Ok(())
    }
}

/// Controls a VGA text-mode terminal.
pub struct Vga {
    width:  usize,
    height: usize,
    row:    usize,
    col:    usize,
    fg:     Color,
    bg:     Color,
    attr:   u8,
    buffer: *mut u16,
    port:   u16
}

impl Vga {
    pub unsafe fn new(width:  usize,
                      height: usize,
                      buffer: *mut u16,
                      port:   u16)
                      -> Vga {

        let mut vga = Vga {
            width:  width,
            height: height,
            row:    0,
            col:    0,
            fg:     Color::LightGrey,
            bg:     Color::Black,
            attr:   Vga::attr(Color::LightGrey, Color::Black),
            buffer: buffer,
            port:   port
        };

        vga.reset().unwrap();

        vga
    }

    pub fn color(c: Color) -> u8 {
        c as u8
    }

    pub fn attr(fg: Color, bg: Color) -> u8 {
        Vga::color(fg) | (Vga::color(bg) << 4)
    }

    fn update_attr(&mut self) {
        self.attr = Vga::attr(self.fg, self.bg);
    }

    fn update_cursor(&mut self) {
        unsafe fn outb(byte: u8, port: u16) {
            asm!("out %al, %dx" :: "{ax}" (byte), "{dx}" (port) :: "volatile");
        }

        let pos: u16 = ((self.row * self.width) + self.col) as u16;

        unsafe {
            outb(0x0F,             self.port);
            outb(pos as u8,        self.port + 1);

            outb(0x0E,             self.port);
            outb((pos >> 8) as u8, self.port + 1);
        }
    }

    pub fn put(&mut self, byte: u8, attr: u8, row: usize, col: usize) {
        unsafe {
            *self.buffer.offset((row * self.width + col) as isize) =
                (byte as u16) | ((attr as u16) << 8);
        }
    }

    pub fn put_here(&mut self, byte: u8) {
        let (attr, row, col) = (self.attr, self.row, self.col);

        self.put(byte, attr, row, col)
    }

    fn new_line(&mut self) {
        // Clear to the end of the line.
        while self.col < self.width {
            self.put_here(' ' as u8);
            self.col += 1;
        }

        // Go to the next line, scrolling if necessary.
        self.col  = 0;
        self.row += 1;

        while self.row >= self.height {
            self.scroll();
            self.row -= 1;
        }

        self.update_cursor();
    }

    fn scroll(&mut self) {
        // Shift everything one line back.
        for row in 1..self.height {
            for col in 0..self.width {
                let index = (row * self.width + col) as isize;

                unsafe {
                    *self.buffer.offset(index - self.width as isize) =
                        *self.buffer.offset(index);
                }
            }
        }

        // Clear last line.
        let (attr, height) = (self.attr, self.height);

        for col in 0..self.width {
            self.put(' ' as u8, attr, height - 1, col);
        }
    }
}

impl Terminal for Vga {
    fn reset(&mut self) -> fmt::Result {
        self.fg = Color::LightGrey;
        self.bg = Color::Black;
        self.update_attr();
        self.clear()
    }

    fn clear(&mut self) -> fmt::Result {
        self.row = 0;
        self.col = 0;

        let attr = self.attr;

        for row in 0..self.height {
            for col in 0..self.width {
                self.put(' ' as u8, attr, row, col);
            }
        }

        Ok(())
    }

    fn get_cursor(&self) -> (usize, usize) {
        (self.row, self.col)
    }

    fn set_cursor(&mut self, row: usize, col: usize) -> fmt::Result {
        self.row = row;
        self.col = col;

        self.update_cursor();
        Ok(())
    }

    fn get_color(&self) -> (Color, Color) {
        (self.fg, self.bg)
    }

    fn set_color(&mut self, fg: Color, bg: Color) -> fmt::Result {
        self.fg = fg;
        self.bg = bg;
        self.update_attr();
        Ok(())
    }

    fn put_raw_byte(&mut self,
                    byte: u8,
                    fg:   Color,
                    bg:   Color,
                    row:  usize,
                    col:  usize) -> fmt::Result {

        self.put(byte, Vga::attr(fg, bg), row, col);
        Ok(())
    }

    fn write_raw_byte(&mut self, byte: u8) -> fmt::Result {
        match byte {
            0x0A /* newline */ => {
                self.new_line();
            },

            0x08 /* backspace */ => {
                if self.col > 0 {
                    self.col -= 1;
                }

                self.put_here(' ' as u8);
            },

            _ => {
                self.put_here(byte);
                self.col += 1;

                if self.col >= self.width {
                    self.new_line();
                }
            }
        }

        Ok(())
    }

    fn flush(&mut self) -> fmt::Result {
        self.update_cursor();
        Ok(())
    }
}

impl fmt::Write for Vga {
    fn write_str(&mut self, s: &str) -> fmt::Result {
        try!(self.write_raw_bytes(s.as_bytes()));
        try!(self.flush());
        Ok(())
    }
}

static mut CONSOLE: Option<Vga> = None;

/// Get the current global console.
pub fn console() -> &'static mut Terminal {
    unsafe {
        if CONSOLE.is_none() {
            CONSOLE = Some(Vga::new(80, 25,
                                    0xffffffff800b8000 as *mut u16, 0x3d4));
        }

        CONSOLE.as_mut().unwrap()
    }
}

/// C (legacy) interface. See `kit/kernel/include/terminal.h`.
pub mod ffi {
    use super::*;

    use core::mem;
    use core::slice;
    use core::ptr::PtrExt;

    use libc::{c_char, size_t};

    #[no_mangle]
    pub extern fn terminal_initialize() {
        console().reset().unwrap();
    }

    #[no_mangle]
    pub extern fn terminal_clear() {
        console().clear().unwrap();
    }

    #[no_mangle]
    pub extern fn terminal_updatecursor() {
        console().flush().unwrap();
    }

    #[no_mangle]
    pub unsafe extern fn terminal_getcursor(row: *mut size_t, column: *mut size_t) {
        let (row_us, col_us) = console().get_cursor();

        *row    = row_us as size_t;
        *column = col_us as size_t;
    }

    #[no_mangle]
    pub extern fn terminal_setcursor(row: size_t, column: size_t) {
        console().set_cursor(row as usize, column as usize).unwrap();
    }

    #[repr(C)]
    #[derive(Copy)]
    pub enum VgaColor {
        Black        = 0,
        Blue         = 1,
        Green        = 2,
        Cyan         = 3,
        Red          = 4,
        Magenta      = 5,
        Brown        = 6,
        LightGrey    = 7,
        DarkGrey     = 8,
        LightBlue    = 9,
        LightGreen   = 10,
        LightCyan    = 11,
        LightRed     = 12,
        LightMagenta = 13,
        LightBrown   = 14,
        White        = 15,
    }

    impl VgaColor {
        pub fn from_color(color: Color) -> VgaColor {
            unsafe { mem::transmute(color as i32) }
        }

        pub fn to_color(self) -> Color {
            unsafe { mem::transmute(self as u8) }
        }
    }

    #[no_mangle]
    pub unsafe extern fn terminal_getcolor(fg: *mut VgaColor,
                                           bg: *mut VgaColor) {
        let (fg_c, bg_c) = console().get_color();

        *fg = VgaColor::from_color(fg_c);
        *bg = VgaColor::from_color(bg_c);
    }

    #[no_mangle]
    pub extern fn terminal_setcolor(fg: VgaColor, bg: VgaColor) {
        console().set_color(fg.to_color(), bg.to_color()).unwrap();
    }

    #[no_mangle]
    pub unsafe extern fn terminal_putentryat(c: c_char,
                                      color: u8,
                                      x: size_t,
                                      y: size_t) {

        let fg_v: VgaColor = mem::transmute((color & 0x0f) as i32);
        let bg_v: VgaColor = mem::transmute((color >> 4)   as i32);

        console()
            .put_raw_byte(c as u8,
                          fg_v.to_color(),
                          bg_v.to_color(),
                          y as usize,
                          x as usize)
            .unwrap();
    }

    #[no_mangle]
    pub extern fn terminal_newline() {
        console().write_raw_byte('\n' as u8).unwrap();
        console().flush().unwrap();
    }

    #[no_mangle]
    pub extern fn terminal_writechar_internal(c: c_char) {
        console().write_raw_byte(c as u8).unwrap();
    }

    #[no_mangle]
    pub extern fn terminal_writechar(c: c_char) {
        console().write_raw_byte(c as u8).unwrap();
        console().flush().unwrap();
    }

    #[no_mangle]
    pub unsafe extern fn terminal_writebuf(length: u64, buffer: *const u8) {
        let bytes = slice::from_raw_parts(buffer, length as usize);

        console().write_raw_bytes(bytes).unwrap();
        console().flush().unwrap();
    }

    #[no_mangle]
    pub unsafe extern fn terminal_writestring(data: *const u8) {
        let mut data_len = 0usize;
        let mut data_end = data;

        while *data_end != 0 {
            data_len += 1;
            data_end = data_end.offset(1);
        }

        let bytes = slice::from_raw_parts(data, data_len);

        console().write_raw_bytes(bytes).unwrap();
        console().flush().unwrap();
    }
}
<|endoftext|>


<commit_before>use aes::Aes128;
use aes::{BLOCK_SIZE, chunks_count};

use errors::*;

use prefix_suffix_oracles::Oracle;
use prefix_suffix_oracles::Oracle13;

use super::challenge12::prefix_plus_suffix_length;
use super::prefix_length;

// The following function works under the single assumption that the target value "user" (to be
// replaced by "admin") is stored at the very end of the profile.

pub fn run() -> Result<(), Error> {
    let oracle = Oracle13::new()?;

    let prefix_len = prefix_length(&oracle)?;
    let (prefix_chunks_count, prefix_fill_len) = chunks_count(prefix_len);
    let target_cleartext = b"admin".pad();
    let mut input = vec![0; prefix_fill_len];
    input.extend_from_slice(&target_cleartext);
    let target_last_block = oracle
        .encrypt(&input)?
        .split_off(prefix_chunks_count * BLOCK_SIZE);

    let (chunks, fill_len) = chunks_count(prefix_plus_suffix_length(&oracle)?);
    let mut ciphertext = oracle.encrypt(&vec![0; fill_len + "user".len()])?;
    compare_eq((chunks + 1) * BLOCK_SIZE, ciphertext.len())?;

    ciphertext[chunks * BLOCK_SIZE..].copy_from_slice(&target_last_block[0..BLOCK_SIZE]);

    oracle.verify_solution(&ciphertext)
}
<commit_msg>Add comment<commit_after>use aes::Aes128;
use aes::{BLOCK_SIZE, chunks_count};

use errors::*;

use prefix_suffix_oracles::Oracle;
use prefix_suffix_oracles::Oracle13;

use super::challenge12::prefix_plus_suffix_length;
use super::prefix_length;

// The following function works under the single assumption that the target value "user" (to be
// replaced by "admin") is stored at the very end of the profile.

pub fn run() -> Result<(), Error> {
    let oracle = Oracle13::new()?;

    let prefix_len = prefix_length(&oracle)?;
    let (prefix_chunks_count, prefix_fill_len) = chunks_count(prefix_len);
    let target_cleartext = b"admin".pad();
    let mut input = vec![0; prefix_fill_len];
    input.extend_from_slice(&target_cleartext);

    // Determine the ciphertext for target_cleartext
    let target_last_block = &oracle
        .encrypt(&input)?
        .split_off(prefix_chunks_count * BLOCK_SIZE)[0..BLOCK_SIZE];

    // The following input is chosen in such a way that the cleartext in oracle looks as follows:
    // email=\0 ... \0 || \0 ...\0&uid=10&role= || user <- padding ->
    let (chunks_count, fill_len) = chunks_count(prefix_plus_suffix_length(&oracle)?);
    let mut ciphertext = oracle.encrypt(&vec![0; fill_len + "user".len()])?;

    // Sanity check
    compare_eq((chunks_count + 1) * BLOCK_SIZE, ciphertext.len())?;

    // Replace last block with target_last_block
    ciphertext[chunks_count * BLOCK_SIZE..].copy_from_slice(target_last_block);

    oracle.verify_solution(&ciphertext)
}
<|endoftext|>


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

use compositor_data::{CompositorData, WantsScrollEvents};
use windowing::{MouseWindowEvent, MouseWindowClickEvent, MouseWindowMouseDownEvent};
use windowing::MouseWindowMouseUpEvent;

use geom::length::Length;
use geom::point::{Point2D, TypedPoint2D};
use geom::rect::Rect;
use geom::size::TypedSize2D;
use layers::geometry::LayerPixel;
use layers::layers::Layer;
use script_traits::{ClickEvent, MouseDownEvent, MouseMoveEvent, MouseUpEvent, SendEventMsg};
use script_traits::{ScriptControlChan};
use servo_msg::compositor_msg::FixedPosition;
use std::rc::Rc;

use geom::matrix::identity;

trait Clampable {
    fn clamp(&self, mn: &Self, mx: &Self) -> Self;
}

impl Clampable for f32 {
    /// Returns the number constrained within the range `mn <= self <= mx`.
    /// If any of the numbers are `NAN` then `NAN` is returned.
    #[inline]
    fn clamp(&self, mn: &f32, mx: &f32) -> f32 {
        match () {
            _ if self.is_nan()   => *self,
            _ if !(*self <= *mx) => *mx,
            _ if !(*self >= *mn) => *mn,
            _                    => *self,
        }
    }
}

#[deriving(PartialEq)]
pub enum ScrollEventResult {
    ScrollEventUnhandled,
    ScrollPositionChanged,
    ScrollPositionUnchanged,
}

/// Move the layer's descendants that don't want scroll events and scroll by a relative
/// specified amount in page coordinates. This also takes in a cursor position to see if the
/// mouse is over child layers first. If a layer successfully scrolled, returns true; otherwise
/// returns false, so a parent layer can scroll instead.
pub fn handle_scroll_event(layer: Rc<Layer<CompositorData>>,
                           delta: TypedPoint2D<LayerPixel, f32>,
                           cursor: TypedPoint2D<LayerPixel, f32>)
                           -> ScrollEventResult {
    // If this layer doesn't want scroll events, neither it nor its children can handle scroll
    // events.
    if layer.extra_data.borrow().wants_scroll_events != WantsScrollEvents {
        return ScrollEventUnhandled;
    }

    // Allow children to scroll.
    let scroll_offset = layer.extra_data.borrow().scroll_offset;
    let new_cursor = cursor - scroll_offset;
    for child in layer.children().iter() {
        let child_bounds = child.bounds.borrow();
        if child_bounds.contains(&new_cursor) {
            let result = handle_scroll_event(child.clone(),
                                             delta,
                                             new_cursor - child_bounds.origin);
            if result != ScrollEventUnhandled {
                return result;
            }
        }
    }

    clamp_scroll_offset_and_scroll_layer(layer, scroll_offset + delta)
}

pub fn calculate_content_size_for_layer(layer: Rc<Layer<CompositorData>>)
                                         -> TypedSize2D<LayerPixel, f32> {
    layer.children().iter().fold(Rect::zero(),
                                 |unioned_rect, child_rect| {
                                    unioned_rect.union(&*child_rect.bounds.borrow())
                                 }).size
}

pub fn clamp_scroll_offset_and_scroll_layer(layer: Rc<Layer<CompositorData>>,
                                            new_offset: TypedPoint2D<LayerPixel, f32>)
                                            -> ScrollEventResult {
    let layer_size = layer.bounds.borrow().size;
    let content_size = calculate_content_size_for_layer(layer.clone());
    let min_x = (layer_size.width - content_size.width).get().min(0.0);
    let min_y = (layer_size.height - content_size.height).get().min(0.0);
    let new_offset : TypedPoint2D<LayerPixel, f32> =
        Point2D(Length(new_offset.x.get().clamp(&min_x, &0.0)),
                Length(new_offset.y.get().clamp(&min_y, &0.0)));

    if layer.extra_data.borrow().scroll_offset == new_offset {
        return ScrollPositionUnchanged;
    }

    // The scroll offset is just a record of the scroll position of this scrolling root,
    // but scroll_layer_and_all_child_layers actually moves the child layers.
    layer.extra_data.borrow_mut().scroll_offset = new_offset;

    let mut result = false;
    for child in layer.children().iter() {
        result |= scroll_layer_and_all_child_layers(child.clone(), new_offset);
    }

    if result {
        return ScrollPositionChanged;
    } else {
        return ScrollPositionUnchanged;
    }
}

fn scroll_layer_and_all_child_layers(layer: Rc<Layer<CompositorData>>,
                                     new_offset: TypedPoint2D<LayerPixel, f32>)
                                     -> bool {
    let mut result = false;

    // Only scroll this layer if it's not fixed-positioned.
    if layer.extra_data.borrow().scroll_policy != FixedPosition {
        let new_offset = new_offset.to_untyped();
        *layer.transform.borrow_mut() = identity().translate(new_offset.x,
                                                             new_offset.y,
                                                             0.0);
        *layer.content_offset.borrow_mut() = Point2D::from_untyped(&new_offset);
        result = true
    }

    let offset_for_children = new_offset + layer.extra_data.borrow().scroll_offset;
    for child in layer.children().iter() {
        result |= scroll_layer_and_all_child_layers(child.clone(), offset_for_children);
    }

    return result;
}

// Takes in a MouseWindowEvent, determines if it should be passed to children, and
// sends the event off to the appropriate pipeline. NB: the cursor position is in
// page coordinates.
pub fn send_mouse_event(layer: Rc<Layer<CompositorData>>,
                        event: MouseWindowEvent,
                        cursor: TypedPoint2D<LayerPixel, f32>) {
    let content_offset = *layer.content_offset.borrow();
    let cursor = cursor - content_offset;
    for child in layer.children().iter() {
        let child_bounds = child.bounds.borrow();
        if child_bounds.contains(&cursor) {
            send_mouse_event(child.clone(), event, cursor - child_bounds.origin);
            return;
        }
    }

    // This mouse event is mine!
    let message = match event {
        MouseWindowClickEvent(button, _) => ClickEvent(button, cursor.to_untyped()),
        MouseWindowMouseDownEvent(button, _) => MouseDownEvent(button, cursor.to_untyped()),
        MouseWindowMouseUpEvent(button, _) => MouseUpEvent(button, cursor.to_untyped()),
    };
    let ScriptControlChan(ref chan) = layer.extra_data.borrow().pipeline.script_chan;
    let _ = chan.send_opt(SendEventMsg(layer.extra_data.borrow().pipeline.id.clone(), message));
}

pub fn send_mouse_move_event(layer: Rc<Layer<CompositorData>>,
                             cursor: TypedPoint2D<LayerPixel, f32>) {
    let message = MouseMoveEvent(cursor.to_untyped());
    let ScriptControlChan(ref chan) = layer.extra_data.borrow().pipeline.script_chan;
    let _ = chan.send_opt(SendEventMsg(layer.extra_data.borrow().pipeline.id.clone(), message));
}

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

use compositor_data::{CompositorData, WantsScrollEvents};
use windowing::{MouseWindowEvent, MouseWindowClickEvent, MouseWindowMouseDownEvent};
use windowing::MouseWindowMouseUpEvent;

use geom::length::Length;
use geom::point::{Point2D, TypedPoint2D};
use geom::rect::Rect;
use geom::size::TypedSize2D;
use layers::geometry::LayerPixel;
use layers::layers::Layer;
use script_traits::{ClickEvent, MouseDownEvent, MouseMoveEvent, MouseUpEvent, SendEventMsg};
use script_traits::{ScriptControlChan};
use servo_msg::compositor_msg::FixedPosition;
use std::rc::Rc;

use geom::matrix::identity;

trait Clampable {
    fn clamp(&self, mn: &Self, mx: &Self) -> Self;
}

impl Clampable for f32 {
    /// Returns the number constrained within the range `mn <= self <= mx`.
    /// If any of the numbers are `NAN` then `NAN` is returned.
    #[inline]
    fn clamp(&self, mn: &f32, mx: &f32) -> f32 {
        match () {
            _ if self.is_nan()   => *self,
            _ if !(*self <= *mx) => *mx,
            _ if !(*self >= *mn) => *mn,
            _                    => *self,
        }
    }
}

#[deriving(PartialEq)]
pub enum ScrollEventResult {
    ScrollEventUnhandled,
    ScrollPositionChanged,
    ScrollPositionUnchanged,
}

/// Move the layer's descendants that don't want scroll events and scroll by a relative
/// specified amount in page coordinates. This also takes in a cursor position to see if the
/// mouse is over child layers first. If a layer successfully scrolled, returns true; otherwise
/// returns false, so a parent layer can scroll instead.
pub fn handle_scroll_event(layer: Rc<Layer<CompositorData>>,
                           delta: TypedPoint2D<LayerPixel, f32>,
                           cursor: TypedPoint2D<LayerPixel, f32>)
                           -> ScrollEventResult {
    // If this layer doesn't want scroll events, neither it nor its children can handle scroll
    // events.
    if layer.extra_data.borrow().wants_scroll_events != WantsScrollEvents {
        return ScrollEventUnhandled;
    }

    // Allow children to scroll.
    let scroll_offset = layer.extra_data.borrow().scroll_offset;
    let new_cursor = cursor - scroll_offset;
    for child in layer.children().iter() {
        let child_bounds = child.bounds.borrow();
        if child_bounds.contains(&new_cursor) {
            let result = handle_scroll_event(child.clone(),
                                             delta,
                                             new_cursor - child_bounds.origin);
            if result != ScrollEventUnhandled {
                return result;
            }
        }
    }

    clamp_scroll_offset_and_scroll_layer(layer, scroll_offset + delta)
}

pub fn calculate_content_size_for_layer(layer: Rc<Layer<CompositorData>>)
                                         -> TypedSize2D<LayerPixel, f32> {
    layer.children().iter().fold(Rect::zero(),
                                 |unioned_rect, child_rect| {
                                    unioned_rect.union(&*child_rect.bounds.borrow())
                                 }).size
}

pub fn clamp_scroll_offset_and_scroll_layer(layer: Rc<Layer<CompositorData>>,
                                            new_offset: TypedPoint2D<LayerPixel, f32>)
                                            -> ScrollEventResult {
    let layer_size = layer.bounds.borrow().size;
    let content_size = calculate_content_size_for_layer(layer.clone());
    let min_x = (layer_size.width - content_size.width).get().min(0.0);
    let min_y = (layer_size.height - content_size.height).get().min(0.0);
    let new_offset : TypedPoint2D<LayerPixel, f32> =
        Point2D(Length(new_offset.x.get().clamp(&min_x, &0.0)),
                Length(new_offset.y.get().clamp(&min_y, &0.0)));

    if layer.extra_data.borrow().scroll_offset == new_offset {
        return ScrollPositionUnchanged;
    }

    // The scroll offset is just a record of the scroll position of this scrolling root,
    // but scroll_layer_and_all_child_layers actually moves the child layers.
    layer.extra_data.borrow_mut().scroll_offset = new_offset;

    let mut result = false;
    for child in layer.children().iter() {
        result |= scroll_layer_and_all_child_layers(child.clone(), new_offset);
    }

    if result {
        return ScrollPositionChanged;
    } else {
        return ScrollPositionUnchanged;
    }
}

fn scroll_layer_and_all_child_layers(layer: Rc<Layer<CompositorData>>,
                                     new_offset: TypedPoint2D<LayerPixel, f32>)
                                     -> bool {
    let mut result = false;

    // Only scroll this layer if it's not fixed-positioned.
    if layer.extra_data.borrow().scroll_policy != FixedPosition {
        let new_offset = new_offset.to_untyped();
        *layer.transform.borrow_mut() = identity().translate(new_offset.x,
                                                             new_offset.y,
                                                             0.0);
        *layer.content_offset.borrow_mut() = Point2D::from_untyped(&new_offset);
        result = true
    }

    let offset_for_children = new_offset + layer.extra_data.borrow().scroll_offset;
    for child in layer.children().iter() {
        result |= scroll_layer_and_all_child_layers(child.clone(), offset_for_children);
    }

    return result;
}

struct HitTestResult {
    layer: Rc<Layer<CompositorData>>,
    point: TypedPoint2D<LayerPixel, f32>,
}

pub fn find_topmost_layer_at_point(layer: Rc<Layer<CompositorData>>,
                                   point: TypedPoint2D<LayerPixel, f32>)
                                   -> Option<HitTestResult> {
    let child_point = point - layer.bounds.borrow().origin;
    for child in layer.children().iter().rev() {
        let result = find_topmost_layer_at_point(child.clone(), child_point);
        if result.is_some() {
            return result;
        }
    }

    let point = point - *layer.content_offset.borrow();
    if !layer.bounds.borrow().contains(&point) {
        return None;
    }

    return Some(HitTestResult { layer: layer, point: point });
}

// Takes in a MouseWindowEvent, determines if it should be passed to children, and
// sends the event off to the appropriate pipeline. NB: the cursor position is in
// page coordinates.
pub fn send_mouse_event(layer: Rc<Layer<CompositorData>>,
                        event: MouseWindowEvent,
                        cursor: TypedPoint2D<LayerPixel, f32>) {
    match find_topmost_layer_at_point(layer.clone(), cursor) {
        Some(result) => {
            let event_point = result.point.to_untyped();
            let message = match event {
                MouseWindowClickEvent(button, _) => ClickEvent(button, event_point),
                MouseWindowMouseDownEvent(button, _) => MouseDownEvent(button, event_point),
                MouseWindowMouseUpEvent(button, _) => MouseUpEvent(button, event_point),
            };
            let pipeline = &result.layer.extra_data.borrow().pipeline;
            let ScriptControlChan(ref chan) = pipeline.script_chan;
            let _ = chan.send_opt(SendEventMsg(pipeline.id.clone(), message));
        },
        None => {},
    }

}

pub fn send_mouse_move_event(layer: Rc<Layer<CompositorData>>,
                             cursor: TypedPoint2D<LayerPixel, f32>) {
    match find_topmost_layer_at_point(layer.clone(), cursor) {
        Some(result) => {
            let message = MouseMoveEvent(result.point.to_untyped());
            let pipeline = &result.layer.extra_data.borrow().pipeline;
            let ScriptControlChan(ref chan) = pipeline.script_chan;
            let _ = chan.send_opt(SendEventMsg(pipeline.id.clone(), message));
        },
        None => {},
    }
}

<|endoftext|>


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

use devtools_traits::{ConsoleMessage, LogLevel, ScriptToDevtoolsControlMsg};
use dom::bindings::global::GlobalRef;
use dom::bindings::str::DOMString;

// https://developer.mozilla.org/en-US/docs/Web/API/Console
pub struct Console(());

impl Console {
    fn send_to_devtools(global: GlobalRef, level: LogLevel, message: DOMString) {
        let global_scope = global.as_global_scope();
        if let Some(chan) = global_scope.devtools_chan() {
            let console_message = prepare_message(level, message);
            let worker_id = if let GlobalRef::Worker(worker) = global {
                Some(worker.get_worker_id())
            } else {
                None
            };
            let devtools_message = ScriptToDevtoolsControlMsg::ConsoleAPI(
                global_scope.pipeline_id(),
                console_message,
                worker_id);
            chan.send(devtools_message).unwrap();
        }
    }
}

impl Console {
    // https://developer.mozilla.org/en-US/docs/Web/API/Console/log
    pub fn Log(global: GlobalRef, messages: Vec<DOMString>) {
        for message in messages {
            println!("{}", message);
            Self::send_to_devtools(global, LogLevel::Log, message);
        }
    }

    // https://developer.mozilla.org/en-US/docs/Web/API/Console
    pub fn Debug(global: GlobalRef, messages: Vec<DOMString>) {
        for message in messages {
            println!("{}", message);
            Self::send_to_devtools(global, LogLevel::Debug, message);
        }
    }

    // https://developer.mozilla.org/en-US/docs/Web/API/Console/info
    pub fn Info(global: GlobalRef, messages: Vec<DOMString>) {
        for message in messages {
            println!("{}", message);
            Self::send_to_devtools(global, LogLevel::Info, message);
        }
    }

    // https://developer.mozilla.org/en-US/docs/Web/API/Console/warn
    pub fn Warn(global: GlobalRef, messages: Vec<DOMString>) {
        for message in messages {
            println!("{}", message);
            Self::send_to_devtools(global, LogLevel::Warn, message);
        }
    }

    // https://developer.mozilla.org/en-US/docs/Web/API/Console/error
    pub fn Error(global: GlobalRef, messages: Vec<DOMString>) {
        for message in messages {
            println!("{}", message);
            Self::send_to_devtools(global, LogLevel::Error, message);
        }
    }

    // https://developer.mozilla.org/en-US/docs/Web/API/Console/assert
    pub fn Assert(global: GlobalRef, condition: bool, message: Option<DOMString>) {
        if !condition {
            let message = message.unwrap_or_else(|| DOMString::from("no message"));
            println!("Assertion failed: {}", message);
            Self::send_to_devtools(global, LogLevel::Error, message);
        }
    }

    // https://developer.mozilla.org/en-US/docs/Web/API/Console/time
    pub fn Time(global: GlobalRef, label: DOMString) {
        if let Ok(()) = global.as_global_scope().time(label.clone()) {
            let message = DOMString::from(format!("{}: timer started", label));
            println!("{}", message);
            Self::send_to_devtools(global, LogLevel::Log, message);
        }
    }

    // https://developer.mozilla.org/en-US/docs/Web/API/Console/timeEnd
    pub fn TimeEnd(global: GlobalRef, label: DOMString) {
        if let Ok(delta) = global.as_global_scope().time_end(&label) {
            let message = DOMString::from(
                format!("{}: {}ms", label, delta)
            );
            println!("{}", message);
            Self::send_to_devtools(global, LogLevel::Log, message);
        };
    }
}

fn prepare_message(log_level: LogLevel, message: DOMString) -> ConsoleMessage {
    // TODO: Sending fake values for filename, lineNumber and columnNumber in LogMessage; adjust later
    ConsoleMessage {
        message: String::from(message),
        logLevel: log_level,
        filename: "test".to_owned(),
        lineNumber: 1,
        columnNumber: 1,
    }
}
<commit_msg>Make Console::send_to_devtools take a &GlobalScope<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use devtools_traits::{ConsoleMessage, LogLevel, ScriptToDevtoolsControlMsg};
use dom::bindings::global::GlobalRef;
use dom::bindings::inheritance::Castable;
use dom::bindings::str::DOMString;
use dom::globalscope::GlobalScope;
use dom::workerglobalscope::WorkerGlobalScope;

// https://developer.mozilla.org/en-US/docs/Web/API/Console
pub struct Console(());

impl Console {
    fn send_to_devtools(global: &GlobalScope, level: LogLevel, message: DOMString) {
        if let Some(chan) = global.devtools_chan() {
            let console_message = prepare_message(level, message);
            let worker_id = global.downcast::<WorkerGlobalScope>().map(|worker| {
                worker.get_worker_id()
            });
            let devtools_message = ScriptToDevtoolsControlMsg::ConsoleAPI(
                global.pipeline_id(),
                console_message,
                worker_id);
            chan.send(devtools_message).unwrap();
        }
    }
}

impl Console {
    // https://developer.mozilla.org/en-US/docs/Web/API/Console/log
    pub fn Log(global: GlobalRef, messages: Vec<DOMString>) {
        for message in messages {
            println!("{}", message);
            Self::send_to_devtools(global.as_global_scope(), LogLevel::Log, message);
        }
    }

    // https://developer.mozilla.org/en-US/docs/Web/API/Console
    pub fn Debug(global: GlobalRef, messages: Vec<DOMString>) {
        for message in messages {
            println!("{}", message);
            Self::send_to_devtools(global.as_global_scope(), LogLevel::Debug, message);
        }
    }

    // https://developer.mozilla.org/en-US/docs/Web/API/Console/info
    pub fn Info(global: GlobalRef, messages: Vec<DOMString>) {
        for message in messages {
            println!("{}", message);
            Self::send_to_devtools(global.as_global_scope(), LogLevel::Info, message);
        }
    }

    // https://developer.mozilla.org/en-US/docs/Web/API/Console/warn
    pub fn Warn(global: GlobalRef, messages: Vec<DOMString>) {
        for message in messages {
            println!("{}", message);
            Self::send_to_devtools(global.as_global_scope(), LogLevel::Warn, message);
        }
    }

    // https://developer.mozilla.org/en-US/docs/Web/API/Console/error
    pub fn Error(global: GlobalRef, messages: Vec<DOMString>) {
        for message in messages {
            println!("{}", message);
            Self::send_to_devtools(global.as_global_scope(), LogLevel::Error, message);
        }
    }

    // https://developer.mozilla.org/en-US/docs/Web/API/Console/assert
    pub fn Assert(global: GlobalRef, condition: bool, message: Option<DOMString>) {
        if !condition {
            let message = message.unwrap_or_else(|| DOMString::from("no message"));
            println!("Assertion failed: {}", message);
            Self::send_to_devtools(global.as_global_scope(), LogLevel::Error, message);
        }
    }

    // https://developer.mozilla.org/en-US/docs/Web/API/Console/time
    pub fn Time(global: GlobalRef, label: DOMString) {
        if let Ok(()) = global.as_global_scope().time(label.clone()) {
            let message = DOMString::from(format!("{}: timer started", label));
            println!("{}", message);
            Self::send_to_devtools(global.as_global_scope(), LogLevel::Log, message);
        }
    }

    // https://developer.mozilla.org/en-US/docs/Web/API/Console/timeEnd
    pub fn TimeEnd(global: GlobalRef, label: DOMString) {
        if let Ok(delta) = global.as_global_scope().time_end(&label) {
            let message = DOMString::from(
                format!("{}: {}ms", label, delta)
            );
            println!("{}", message);
            Self::send_to_devtools(global.as_global_scope(), LogLevel::Log, message);
        };
    }
}

fn prepare_message(log_level: LogLevel, message: DOMString) -> ConsoleMessage {
    // TODO: Sending fake values for filename, lineNumber and columnNumber in LogMessage; adjust later
    ConsoleMessage {
        message: String::from(message),
        logLevel: log_level,
        filename: "test".to_owned(),
        lineNumber: 1,
        columnNumber: 1,
    }
}
<|endoftext|>


<commit_before>extern crate compiletest_rs as compiletest;

use std::path::{PathBuf, Path};
use std::io::Write;

fn run_mode(dir: &'static str, mode: &'static str, sysroot: &str) {
    // Disable rustc's new error fomatting. It breaks these tests.
    std::env::remove_var("RUST_NEW_ERROR_FORMAT");
    let flags = format!("--sysroot {} -Dwarnings", sysroot);
    for_all_targets(sysroot, |target| {
        let mut config = compiletest::default_config();
        config.host_rustcflags = Some(flags.clone());
        config.mode = mode.parse().expect("Invalid mode");
        config.run_lib_path = Path::new(sysroot).join("lib").join("rustlib").join(&target).join("lib");
        config.rustc_path = "target/debug/miri".into();
        config.src_base = PathBuf::from(format!("tests/{}", dir));
        config.target = target.to_owned();
        config.target_rustcflags = Some(flags.clone());
        compiletest::run_tests(&config);
    });
}

fn for_all_targets<F: Fn(String)>(sysroot: &str, f: F) {
    for target in std::fs::read_dir(format!("{}/lib/rustlib/", sysroot)).unwrap() {
        let target = target.unwrap();
        if !target.metadata().unwrap().is_dir() {
            continue;
        }
        let target = target.file_name().into_string().unwrap();
        if target == "etc" {
            continue;
        }
        let stderr = std::io::stderr();
        writeln!(stderr.lock(), "running tests for target {}", target).unwrap();
        f(target);
    }
}

#[test]
fn compile_test() {
    let mut failed = false;
    // Taken from https://github.com/Manishearth/rust-clippy/pull/911.
    let home = option_env!("RUSTUP_HOME").or(option_env!("MULTIRUST_HOME"));
    let toolchain = option_env!("RUSTUP_TOOLCHAIN").or(option_env!("MULTIRUST_TOOLCHAIN"));
    let sysroot = match (home, toolchain) {
        (Some(home), Some(toolchain)) => format!("{}/toolchains/{}", home, toolchain),
        _ => option_env!("RUST_SYSROOT")
            .expect("need to specify RUST_SYSROOT env var or use rustup or multirust")
            .to_owned(),
    };
    run_mode("compile-fail", "compile-fail", &sysroot);
    for_all_targets(&sysroot, |target| {
        for file in std::fs::read_dir("tests/run-pass").unwrap() {
            let file = file.unwrap();
            if !file.metadata().unwrap().is_file() {
                continue;
            }
            let file = file.path();
            let stderr = std::io::stderr();
            write!(stderr.lock(), "test [miri-pass] {} ", file.to_str().unwrap()).unwrap();
            let mut cmd = std::process::Command::new("target/debug/miri");
            cmd.arg(file);
            cmd.arg(format!("--sysroot={}", sysroot));
            cmd.arg("-Dwarnings");
            cmd.arg(format!("--target={}", target));
            let libs = Path::new(&sysroot).join("lib");
            let sysroot = libs.join("rustlib").join(&target).join("lib");
            let paths = std::env::join_paths(&[libs, sysroot]).unwrap();
            cmd.env(compiletest::procsrv::dylib_env_var(), paths);
            match cmd.output() {
                Ok(ref output) if output.status.success() => writeln!(stderr.lock(), "ok").unwrap(),
                Ok(output) => {
                    failed = true;
                    writeln!(stderr.lock(), "FAILED with exit code {}", output.status.code().unwrap_or(0)).unwrap();
                    writeln!(stderr.lock(), "stdout: \n {}", std::str::from_utf8(&output.stdout).unwrap()).unwrap();
                    writeln!(stderr.lock(), "stderr: \n {}", std::str::from_utf8(&output.stderr).unwrap()).unwrap();
                }
                Err(e) => {
                    failed = true;
                    writeln!(stderr.lock(), "FAILED: {}", e).unwrap();
                },
            }
        }
        let stderr = std::io::stderr();
        writeln!(stderr.lock(), "").unwrap();
    })
    if failed {
        panic!("some tests failed");
    }
}
<commit_msg>forward `RUST_SYSROOT` to miri test calls<commit_after>extern crate compiletest_rs as compiletest;

use std::path::{PathBuf, Path};
use std::io::Write;

fn run_mode(dir: &'static str, mode: &'static str, sysroot: &str) {
    // Disable rustc's new error fomatting. It breaks these tests.
    std::env::remove_var("RUST_NEW_ERROR_FORMAT");
    let flags = format!("--sysroot {} -Dwarnings", sysroot);
    for_all_targets(sysroot, |target| {
        let mut config = compiletest::default_config();
        config.host_rustcflags = Some(flags.clone());
        config.mode = mode.parse().expect("Invalid mode");
        config.run_lib_path = Path::new(sysroot).join("lib").join("rustlib").join(&target).join("lib");
        config.rustc_path = "target/debug/miri".into();
        config.src_base = PathBuf::from(format!("tests/{}", dir));
        config.target = target.to_owned();
        config.target_rustcflags = Some(flags.clone());
        compiletest::run_tests(&config);
    });
}

fn for_all_targets<F: Fn(String)>(sysroot: &str, f: F) {
    for target in std::fs::read_dir(format!("{}/lib/rustlib/", sysroot)).unwrap() {
        let target = target.unwrap();
        if !target.metadata().unwrap().is_dir() {
            continue;
        }
        let target = target.file_name().into_string().unwrap();
        if target == "etc" {
            continue;
        }
        let stderr = std::io::stderr();
        writeln!(stderr.lock(), "running tests for target {}", target).unwrap();
        f(target);
    }
}

#[test]
fn compile_test() {
    let mut failed = false;
    // Taken from https://github.com/Manishearth/rust-clippy/pull/911.
    let home = option_env!("RUSTUP_HOME").or(option_env!("MULTIRUST_HOME"));
    let toolchain = option_env!("RUSTUP_TOOLCHAIN").or(option_env!("MULTIRUST_TOOLCHAIN"));
    let sysroot = match (home, toolchain) {
        (Some(home), Some(toolchain)) => format!("{}/toolchains/{}", home, toolchain),
        _ => option_env!("RUST_SYSROOT")
            .expect("need to specify RUST_SYSROOT env var or use rustup or multirust")
            .to_owned(),
    };
    run_mode("compile-fail", "compile-fail", &sysroot);
    for_all_targets(&sysroot, |target| {
        for file in std::fs::read_dir("tests/run-pass").unwrap() {
            let file = file.unwrap();
            if !file.metadata().unwrap().is_file() {
                continue;
            }
            let file = file.path();
            let stderr = std::io::stderr();
            write!(stderr.lock(), "test [miri-pass] {} ", file.to_str().unwrap()).unwrap();
            let mut cmd = std::process::Command::new("target/debug/miri");
            cmd.arg(file);
            cmd.arg(format!("--sysroot={}", sysroot));
            cmd.arg("-Dwarnings");
            cmd.arg(format!("--target={}", target));
            cmd.env("RUST_SYSROOT", sysroot);
            let libs = Path::new(&sysroot).join("lib");
            let sysroot = libs.join("rustlib").join(&target).join("lib");
            let paths = std::env::join_paths(&[libs, sysroot]).unwrap();
            cmd.env(compiletest::procsrv::dylib_env_var(), paths);
            match cmd.output() {
                Ok(ref output) if output.status.success() => writeln!(stderr.lock(), "ok").unwrap(),
                Ok(output) => {
                    failed = true;
                    writeln!(stderr.lock(), "FAILED with exit code {}", output.status.code().unwrap_or(0)).unwrap();
                    writeln!(stderr.lock(), "stdout: \n {}", std::str::from_utf8(&output.stdout).unwrap()).unwrap();
                    writeln!(stderr.lock(), "stderr: \n {}", std::str::from_utf8(&output.stderr).unwrap()).unwrap();
                }
                Err(e) => {
                    failed = true;
                    writeln!(stderr.lock(), "FAILED: {}", e).unwrap();
                },
            }
        }
        let stderr = std::io::stderr();
        writeln!(stderr.lock(), "").unwrap();
    })
    if failed {
        panic!("some tests failed");
    }
}
<|endoftext|>


<commit_before>extern crate libc;
extern crate security_framework;
extern crate security_framework_sys;
extern crate tempdir;
extern crate twox_hash;

use self::security_framework::base;
use self::security_framework::certificate::SecCertificate;
use self::security_framework::identity::SecIdentity;
use self::security_framework::import_export::Pkcs12ImportOptions;
use self::security_framework::secure_transport::{self, SslContext, ProtocolSide, ConnectionType,
                                                 SslProtocol, ClientBuilder};
use self::security_framework::os::macos::keychain::{self, SecKeychain, KeychainSettings};
use self::security_framework_sys::base::errSecIO;
use self::tempdir::TempDir;
use self::twox_hash::XxHash;
use std::collections::HashMap;
use std::collections::hash_map::Entry::*;
use std::fmt;
use std::hash::BuildHasherDefault as HashBuilder;
use std::io;
use std::error;
use std::sync::Mutex;
use std::sync::{Once, ONCE_INIT};

use Protocol;

static SET_AT_EXIT: Once = ONCE_INIT;

lazy_static! {
    static ref TEMP_DIRS: Mutex<HashMap<Vec<u8>, (SecKeychain, TempDir), HashBuilder<XxHash>>> =
        Mutex::new(HashMap::default());
}

fn convert_protocol(protocol: Protocol) -> SslProtocol {
    match protocol {
        Protocol::Sslv3 => SslProtocol::Ssl3,
        Protocol::Tlsv10 => SslProtocol::Tls1,
        Protocol::Tlsv11 => SslProtocol::Tls11,
        Protocol::Tlsv12 => SslProtocol::Tls12,
        Protocol::__NonExhaustive => unreachable!(),
    }
}

fn protocol_min_max(protocols: &[Protocol]) -> (SslProtocol, SslProtocol) {
    let mut min = Protocol::Tlsv12;
    let mut max = Protocol::Sslv3;
    for protocol in protocols {
        if (*protocol as usize) < (min as usize) {
            min = *protocol;
        }
        if (*protocol as usize) > (max as usize) {
            max = *protocol;
        }
    }
    (convert_protocol(min), convert_protocol(max))
}

pub struct Error(base::Error);

impl error::Error for Error {
    fn description(&self) -> &str {
        error::Error::description(&self.0)
    }

    fn cause(&self) -> Option<&error::Error> {
        error::Error::cause(&self.0)
    }
}

impl fmt::Display for Error {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt::Display::fmt(&self.0, fmt)
    }
}

impl fmt::Debug for Error {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt::Debug::fmt(&self.0, fmt)
    }
}

impl From<base::Error> for Error {
    fn from(error: base::Error) -> Error {
        Error(error)
    }
}

#[derive(Clone)]
pub struct Pkcs12 {
    identity: SecIdentity,
    chain: Vec<SecCertificate>,
}

impl Pkcs12 {
    pub fn from_der(buf: &[u8], pass: &str) -> Result<Pkcs12, Error> {
        SET_AT_EXIT.call_once(|| {
            extern "C" fn atexit() {
                TEMP_DIRS.lock().unwrap().clear();
            }
            unsafe { libc::atexit(atexit); }
        });

        let keychain = match TEMP_DIRS.lock().unwrap().entry(buf.into()) {
            Vacant(entry) => {
                let dir = TempDir::new("native-tls")
                    .map_err(|_| Error(base::Error::from(errSecIO)))?;
                let mut keychain = keychain::CreateOptions::new()
                    .password(pass)
                    .create(dir.path().join("tmp.keychain"))?;
                keychain.set_settings(&KeychainSettings::new())?;
                entry.insert((keychain.clone(), dir));
                keychain
            },
            Occupied(entry) => entry.get().0.clone(),
        };

        let mut imports = try!(
            Pkcs12ImportOptions::new()
                .passphrase(pass)
                .keychain(keychain)
                .import(buf)
        );
        let import = imports.pop().unwrap();

        // FIXME: Compare the certificates for equality using CFEqual
        let identity_cert = try!(import.identity.certificate()).to_der();

        Ok(Pkcs12 {
            identity: import.identity,
            chain: import
                .cert_chain
                .into_iter()
                .filter(|c| c.to_der() != identity_cert)
                .collect(),
        })
    }
}

pub struct Certificate(SecCertificate);

impl Certificate {
    pub fn from_der(buf: &[u8]) -> Result<Certificate, Error> {
        let cert = try!(SecCertificate::from_der(buf));
        Ok(Certificate(cert))
    }
}

pub enum HandshakeError<S> {
    Interrupted(MidHandshakeTlsStream<S>),
    Failure(Error),
}

impl<S> From<secure_transport::HandshakeError<S>> for HandshakeError<S> {
    fn from(e: secure_transport::HandshakeError<S>) -> HandshakeError<S> {
        match e {
            secure_transport::HandshakeError::Failure(e) => HandshakeError::Failure(e.into()),
            secure_transport::HandshakeError::Interrupted(s) => {
                HandshakeError::Interrupted(MidHandshakeTlsStream::Server(s))
            }
        }
    }
}

impl<S> From<secure_transport::ClientHandshakeError<S>> for HandshakeError<S> {
    fn from(e: secure_transport::ClientHandshakeError<S>) -> HandshakeError<S> {
        match e {
            secure_transport::ClientHandshakeError::Failure(e) => HandshakeError::Failure(e.into()),
            secure_transport::ClientHandshakeError::Interrupted(s) => {
                HandshakeError::Interrupted(MidHandshakeTlsStream::Client(s))
            }
        }
    }
}

impl<S> From<base::Error> for HandshakeError<S> {
    fn from(e: base::Error) -> HandshakeError<S> {
        HandshakeError::Failure(e.into())
    }
}

pub enum MidHandshakeTlsStream<S> {
    Server(secure_transport::MidHandshakeSslStream<S>),
    Client(secure_transport::MidHandshakeClientBuilder<S>),
}

impl<S> fmt::Debug for MidHandshakeTlsStream<S>
where
    S: fmt::Debug,
{
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            MidHandshakeTlsStream::Server(ref s) => s.fmt(fmt),
            MidHandshakeTlsStream::Client(ref s) => s.fmt(fmt),
        }
    }
}

impl<S> MidHandshakeTlsStream<S>
where
    S: io::Read + io::Write,
{
    pub fn get_ref(&self) -> &S {
        match *self {
            MidHandshakeTlsStream::Server(ref s) => s.get_ref(),
            MidHandshakeTlsStream::Client(ref s) => s.get_ref(),
        }
    }

    pub fn get_mut(&mut self) -> &mut S {
        match *self {
            MidHandshakeTlsStream::Server(ref mut s) => s.get_mut(),
            MidHandshakeTlsStream::Client(ref mut s) => s.get_mut(),
        }
    }

    pub fn handshake(self) -> Result<TlsStream<S>, HandshakeError<S>> {
        match self {
            MidHandshakeTlsStream::Server(s) => {
                match s.handshake() {
                    Ok(s) => Ok(TlsStream(s)),
                    Err(e) => Err(e.into()),
                }
            }
            MidHandshakeTlsStream::Client(s) => {
                match s.handshake() {
                    Ok(s) => Ok(TlsStream(s)),
                    Err(e) => Err(e.into()),
                }
            }
        }
    }
}

pub struct TlsConnectorBuilder(TlsConnector);

impl TlsConnectorBuilder {
    pub fn identity(&mut self, pkcs12: Pkcs12) -> Result<(), Error> {
        self.0.pkcs12 = Some(pkcs12);
        Ok(())
    }

    pub fn add_root_certificate(&mut self, cert: Certificate) -> Result<(), Error> {
        self.0.roots.push(cert.0);
        Ok(())
    }

    pub fn supported_protocols(&mut self, protocols: &[Protocol]) -> Result<(), Error> {
        self.0.protocols = protocols.to_vec();
        Ok(())
    }

    pub fn build(self) -> Result<TlsConnector, Error> {
        Ok(self.0)
    }
}

#[derive(Clone)]
pub struct TlsConnector {
    pkcs12: Option<Pkcs12>,
    protocols: Vec<Protocol>,
    roots: Vec<SecCertificate>,
}

impl TlsConnector {
    pub fn builder() -> Result<TlsConnectorBuilder, Error> {
        Ok(TlsConnectorBuilder(TlsConnector {
            pkcs12: None,
            protocols: vec![Protocol::Tlsv10, Protocol::Tlsv11, Protocol::Tlsv12],
            roots: vec![],
        }))
    }

    pub fn connect<S>(&self, domain: &str, stream: S) -> Result<TlsStream<S>, HandshakeError<S>>
    where
        S: io::Read + io::Write,
    {
        self.connect_inner(Some(domain), stream)
    }

    pub fn connect_no_domain<S>(&self, stream: S) -> Result<TlsStream<S>, HandshakeError<S>>
    where
        S: io::Read + io::Write,
    {
        self.connect_inner(None, stream)
    }

    fn connect_inner<S>(
        &self,
        domain: Option<&str>,
        stream: S,
    ) -> Result<TlsStream<S>, HandshakeError<S>>
    where
        S: io::Read + io::Write,
    {
        let mut builder = ClientBuilder::new();
        let (min, max) = protocol_min_max(&self.protocols);
        builder.protocol_min(min);
        builder.protocol_max(max);
        if let Some(pkcs12) = self.pkcs12.as_ref() {
            builder.identity(&pkcs12.identity, &pkcs12.chain);
        }
        builder.anchor_certificates(&self.roots);

        let r = match domain {
            Some(domain) => builder.handshake2(domain, stream),
            None => builder.danger_handshake_without_providing_domain_for_certificate_validation_and_server_name_indication(stream),
        };
        match r {
            Ok(s) => Ok(TlsStream(s)),
            Err(e) => Err(e.into()),
        }
    }
}

pub struct TlsAcceptorBuilder(TlsAcceptor);

impl TlsAcceptorBuilder {
    pub fn supported_protocols(&mut self, protocols: &[Protocol]) -> Result<(), Error> {
        self.0.protocols = protocols.to_vec();
        Ok(())
    }

    pub fn build(self) -> Result<TlsAcceptor, Error> {
        Ok(self.0)
    }
}

#[derive(Clone)]
pub struct TlsAcceptor {
    pkcs12: Pkcs12,
    protocols: Vec<Protocol>,
}

impl TlsAcceptor {
    pub fn builder(pkcs12: Pkcs12) -> Result<TlsAcceptorBuilder, Error> {
        Ok(TlsAcceptorBuilder(TlsAcceptor {
            pkcs12: pkcs12,
            protocols: vec![Protocol::Tlsv10, Protocol::Tlsv11, Protocol::Tlsv12],
        }))
    }

    pub fn accept<S>(&self, stream: S) -> Result<TlsStream<S>, HandshakeError<S>>
    where
        S: io::Read + io::Write,
    {
        let mut ctx = try!(SslContext::new(
            ProtocolSide::Server,
            ConnectionType::Stream,
        ));

        let (min, max) = protocol_min_max(&self.protocols);
        try!(ctx.set_protocol_version_min(min));
        try!(ctx.set_protocol_version_max(max));
        try!(ctx.set_certificate(
            &self.pkcs12.identity,
            &self.pkcs12.chain,
        ));
        match ctx.handshake(stream) {
            Ok(s) => Ok(TlsStream(s)),
            Err(e) => Err(e.into()),
        }
    }
}

pub struct TlsStream<S>(secure_transport::SslStream<S>);

impl<S: fmt::Debug> fmt::Debug for TlsStream<S> {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt::Debug::fmt(&self.0, fmt)
    }
}

impl<S: io::Read + io::Write> TlsStream<S> {
    pub fn get_ref(&self) -> &S {
        self.0.get_ref()
    }

    pub fn get_mut(&mut self) -> &mut S {
        self.0.get_mut()
    }

    pub fn buffered_read_size(&self) -> Result<usize, Error> {
        Ok(try!(self.0.context().buffered_read_size()))
    }

    pub fn shutdown(&mut self) -> io::Result<()> {
        try!(self.0.close());
        Ok(())
    }
}

impl<S: io::Read + io::Write> io::Read for TlsStream<S> {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        self.0.read(buf)
    }
}

impl<S: io::Read + io::Write> io::Write for TlsStream<S> {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.0.write(buf)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.0.flush()
    }
}

/// Security Framework-specific extensions to `TlsStream`.
pub trait TlsStreamExt<S> {
    /// Returns a shared reference to the Security Framework `SslStream`.
    fn raw_stream(&self) -> &secure_transport::SslStream<S>;

    /// Returns a mutable reference to the Security Framework `SslStream`.
    fn raw_stream_mut(&mut self) -> &mut secure_transport::SslStream<S>;
}

impl<S> TlsStreamExt<S> for ::TlsStream<S> {
    fn raw_stream(&self) -> &secure_transport::SslStream<S> {
        &(self.0).0
    }

    fn raw_stream_mut(&mut self) -> &mut secure_transport::SslStream<S> {
        &mut (self.0).0
    }
}

/// Security Framework-specific extensions to `TlsConnectorBuilder`.
pub trait TlsConnectorBuilderExt {
    /// Deprecated
    #[deprecated(since = "0.1.2", note = "use add_root_certificate")]
    fn anchor_certificates(&mut self, certs: &[SecCertificate]) -> &mut Self;
}

impl TlsConnectorBuilderExt for ::TlsConnectorBuilder {
    fn anchor_certificates(&mut self, certs: &[SecCertificate]) -> &mut Self {
        (self.0).0.roots = certs.to_owned();
        self
    }
}

/// Security Framework-specific extensions to `Error`
pub trait ErrorExt {
    /// Extract the underlying Security Framework error for inspection.
    fn security_framework_error(&self) -> &base::Error;
}

impl ErrorExt for ::Error {
    fn security_framework_error(&self) -> &base::Error {
        &(self.0).0
    }
}
<commit_msg>Don't import same identity multiple times in Pkcs12::from_der<commit_after>extern crate libc;
extern crate security_framework;
extern crate security_framework_sys;
extern crate tempdir;
extern crate twox_hash;

use self::security_framework::base;
use self::security_framework::certificate::SecCertificate;
use self::security_framework::identity::SecIdentity;
use self::security_framework::import_export::Pkcs12ImportOptions;
use self::security_framework::secure_transport::{self, SslContext, ProtocolSide, ConnectionType,
                                                 SslProtocol, ClientBuilder};
use self::security_framework::os::macos::keychain::{self, KeychainSettings};
use self::security_framework_sys::base::errSecIO;
use self::tempdir::TempDir;
use self::twox_hash::XxHash;
use std::collections::HashMap;
use std::collections::hash_map::Entry::*;
use std::fmt;
use std::hash::BuildHasherDefault as HashBuilder;
use std::io;
use std::error;
use std::sync::Mutex;
use std::sync::{Once, ONCE_INIT};

use Protocol;

static SET_AT_EXIT: Once = ONCE_INIT;

lazy_static! {
    static ref TEMP_DIRS: Mutex<
        HashMap<Vec<u8>, (Pkcs12, TempDir), HashBuilder<XxHash>>> = Mutex::new(HashMap::default());
}

fn convert_protocol(protocol: Protocol) -> SslProtocol {
    match protocol {
        Protocol::Sslv3 => SslProtocol::Ssl3,
        Protocol::Tlsv10 => SslProtocol::Tls1,
        Protocol::Tlsv11 => SslProtocol::Tls11,
        Protocol::Tlsv12 => SslProtocol::Tls12,
        Protocol::__NonExhaustive => unreachable!(),
    }
}

fn protocol_min_max(protocols: &[Protocol]) -> (SslProtocol, SslProtocol) {
    let mut min = Protocol::Tlsv12;
    let mut max = Protocol::Sslv3;
    for protocol in protocols {
        if (*protocol as usize) < (min as usize) {
            min = *protocol;
        }
        if (*protocol as usize) > (max as usize) {
            max = *protocol;
        }
    }
    (convert_protocol(min), convert_protocol(max))
}

pub struct Error(base::Error);

impl error::Error for Error {
    fn description(&self) -> &str {
        error::Error::description(&self.0)
    }

    fn cause(&self) -> Option<&error::Error> {
        error::Error::cause(&self.0)
    }
}

impl fmt::Display for Error {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt::Display::fmt(&self.0, fmt)
    }
}

impl fmt::Debug for Error {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt::Debug::fmt(&self.0, fmt)
    }
}

impl From<base::Error> for Error {
    fn from(error: base::Error) -> Error {
        Error(error)
    }
}

#[derive(Clone)]
pub struct Pkcs12 {
    identity: SecIdentity,
    chain: Vec<SecCertificate>,
}

impl Pkcs12 {
    pub fn from_der(buf: &[u8], pass: &str) -> Result<Pkcs12, Error> {
        SET_AT_EXIT.call_once(|| {
            extern "C" fn atexit() {
                TEMP_DIRS.lock().unwrap().clear();
            }
            unsafe {
                libc::atexit(atexit);
            }
        });

        let pkcs12 = match TEMP_DIRS.lock().unwrap().entry(buf.into()) {
            Vacant(entry) => {
                let dir = TempDir::new("native-tls").map_err(|_| {
                    Error(base::Error::from(errSecIO))
                })?;

                let mut keychain = keychain::CreateOptions::new().password(pass).create(
                    dir.path().join("tmp.keychain"),
                )?;
                keychain.set_settings(&KeychainSettings::new())?;

                let mut imports = Pkcs12ImportOptions::new()
                    .passphrase(pass)
                    .keychain(keychain)
                    .import(buf)?;
                let import = imports.pop().unwrap();

                // FIXME: Compare the certificates for equality using CFEqual
                let identity_cert = import.identity.certificate()?.to_der();
                let identity = import.identity;
                let chain = import
                    .cert_chain
                    .into_iter()
                    .filter(|c| c.to_der() != identity_cert)
                    .collect();

                entry.insert((Pkcs12 { identity: identity, chain: chain }, dir)).0.clone()
            }
            Occupied(entry) => entry.get().0.clone(),
        };

        Ok(pkcs12)
    }
}

pub struct Certificate(SecCertificate);

impl Certificate {
    pub fn from_der(buf: &[u8]) -> Result<Certificate, Error> {
        let cert = try!(SecCertificate::from_der(buf));
        Ok(Certificate(cert))
    }
}

pub enum HandshakeError<S> {
    Interrupted(MidHandshakeTlsStream<S>),
    Failure(Error),
}

impl<S> From<secure_transport::HandshakeError<S>> for HandshakeError<S> {
    fn from(e: secure_transport::HandshakeError<S>) -> HandshakeError<S> {
        match e {
            secure_transport::HandshakeError::Failure(e) => HandshakeError::Failure(e.into()),
            secure_transport::HandshakeError::Interrupted(s) => {
                HandshakeError::Interrupted(MidHandshakeTlsStream::Server(s))
            }
        }
    }
}

impl<S> From<secure_transport::ClientHandshakeError<S>> for HandshakeError<S> {
    fn from(e: secure_transport::ClientHandshakeError<S>) -> HandshakeError<S> {
        match e {
            secure_transport::ClientHandshakeError::Failure(e) => HandshakeError::Failure(e.into()),
            secure_transport::ClientHandshakeError::Interrupted(s) => {
                HandshakeError::Interrupted(MidHandshakeTlsStream::Client(s))
            }
        }
    }
}

impl<S> From<base::Error> for HandshakeError<S> {
    fn from(e: base::Error) -> HandshakeError<S> {
        HandshakeError::Failure(e.into())
    }
}

pub enum MidHandshakeTlsStream<S> {
    Server(secure_transport::MidHandshakeSslStream<S>),
    Client(secure_transport::MidHandshakeClientBuilder<S>),
}

impl<S> fmt::Debug for MidHandshakeTlsStream<S>
where
    S: fmt::Debug,
{
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            MidHandshakeTlsStream::Server(ref s) => s.fmt(fmt),
            MidHandshakeTlsStream::Client(ref s) => s.fmt(fmt),
        }
    }
}

impl<S> MidHandshakeTlsStream<S>
where
    S: io::Read + io::Write,
{
    pub fn get_ref(&self) -> &S {
        match *self {
            MidHandshakeTlsStream::Server(ref s) => s.get_ref(),
            MidHandshakeTlsStream::Client(ref s) => s.get_ref(),
        }
    }

    pub fn get_mut(&mut self) -> &mut S {
        match *self {
            MidHandshakeTlsStream::Server(ref mut s) => s.get_mut(),
            MidHandshakeTlsStream::Client(ref mut s) => s.get_mut(),
        }
    }

    pub fn handshake(self) -> Result<TlsStream<S>, HandshakeError<S>> {
        match self {
            MidHandshakeTlsStream::Server(s) => {
                match s.handshake() {
                    Ok(s) => Ok(TlsStream(s)),
                    Err(e) => Err(e.into()),
                }
            }
            MidHandshakeTlsStream::Client(s) => {
                match s.handshake() {
                    Ok(s) => Ok(TlsStream(s)),
                    Err(e) => Err(e.into()),
                }
            }
        }
    }
}

pub struct TlsConnectorBuilder(TlsConnector);

impl TlsConnectorBuilder {
    pub fn identity(&mut self, pkcs12: Pkcs12) -> Result<(), Error> {
        self.0.pkcs12 = Some(pkcs12);
        Ok(())
    }

    pub fn add_root_certificate(&mut self, cert: Certificate) -> Result<(), Error> {
        self.0.roots.push(cert.0);
        Ok(())
    }

    pub fn supported_protocols(&mut self, protocols: &[Protocol]) -> Result<(), Error> {
        self.0.protocols = protocols.to_vec();
        Ok(())
    }

    pub fn build(self) -> Result<TlsConnector, Error> {
        Ok(self.0)
    }
}

#[derive(Clone)]
pub struct TlsConnector {
    pkcs12: Option<Pkcs12>,
    protocols: Vec<Protocol>,
    roots: Vec<SecCertificate>,
}

impl TlsConnector {
    pub fn builder() -> Result<TlsConnectorBuilder, Error> {
        Ok(TlsConnectorBuilder(TlsConnector {
            pkcs12: None,
            protocols: vec![Protocol::Tlsv10, Protocol::Tlsv11, Protocol::Tlsv12],
            roots: vec![],
        }))
    }

    pub fn connect<S>(&self, domain: &str, stream: S) -> Result<TlsStream<S>, HandshakeError<S>>
    where
        S: io::Read + io::Write,
    {
        self.connect_inner(Some(domain), stream)
    }

    pub fn connect_no_domain<S>(&self, stream: S) -> Result<TlsStream<S>, HandshakeError<S>>
    where
        S: io::Read + io::Write,
    {
        self.connect_inner(None, stream)
    }

    fn connect_inner<S>(
        &self,
        domain: Option<&str>,
        stream: S,
    ) -> Result<TlsStream<S>, HandshakeError<S>>
    where
        S: io::Read + io::Write,
    {
        let mut builder = ClientBuilder::new();
        let (min, max) = protocol_min_max(&self.protocols);
        builder.protocol_min(min);
        builder.protocol_max(max);
        if let Some(pkcs12) = self.pkcs12.as_ref() {
            builder.identity(&pkcs12.identity, &pkcs12.chain);
        }
        builder.anchor_certificates(&self.roots);

        let r = match domain {
            Some(domain) => builder.handshake2(domain, stream),
            None => builder.danger_handshake_without_providing_domain_for_certificate_validation_and_server_name_indication(stream),
        };
        match r {
            Ok(s) => Ok(TlsStream(s)),
            Err(e) => Err(e.into()),
        }
    }
}

pub struct TlsAcceptorBuilder(TlsAcceptor);

impl TlsAcceptorBuilder {
    pub fn supported_protocols(&mut self, protocols: &[Protocol]) -> Result<(), Error> {
        self.0.protocols = protocols.to_vec();
        Ok(())
    }

    pub fn build(self) -> Result<TlsAcceptor, Error> {
        Ok(self.0)
    }
}

#[derive(Clone)]
pub struct TlsAcceptor {
    pkcs12: Pkcs12,
    protocols: Vec<Protocol>,
}

impl TlsAcceptor {
    pub fn builder(pkcs12: Pkcs12) -> Result<TlsAcceptorBuilder, Error> {
        Ok(TlsAcceptorBuilder(TlsAcceptor {
            pkcs12: pkcs12,
            protocols: vec![Protocol::Tlsv10, Protocol::Tlsv11, Protocol::Tlsv12],
        }))
    }

    pub fn accept<S>(&self, stream: S) -> Result<TlsStream<S>, HandshakeError<S>>
    where
        S: io::Read + io::Write,
    {
        let mut ctx = try!(SslContext::new(
            ProtocolSide::Server,
            ConnectionType::Stream,
        ));

        let (min, max) = protocol_min_max(&self.protocols);
        try!(ctx.set_protocol_version_min(min));
        try!(ctx.set_protocol_version_max(max));
        try!(ctx.set_certificate(
            &self.pkcs12.identity,
            &self.pkcs12.chain,
        ));
        match ctx.handshake(stream) {
            Ok(s) => Ok(TlsStream(s)),
            Err(e) => Err(e.into()),
        }
    }
}

pub struct TlsStream<S>(secure_transport::SslStream<S>);

impl<S: fmt::Debug> fmt::Debug for TlsStream<S> {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt::Debug::fmt(&self.0, fmt)
    }
}

impl<S: io::Read + io::Write> TlsStream<S> {
    pub fn get_ref(&self) -> &S {
        self.0.get_ref()
    }

    pub fn get_mut(&mut self) -> &mut S {
        self.0.get_mut()
    }

    pub fn buffered_read_size(&self) -> Result<usize, Error> {
        Ok(try!(self.0.context().buffered_read_size()))
    }

    pub fn shutdown(&mut self) -> io::Result<()> {
        try!(self.0.close());
        Ok(())
    }
}

impl<S: io::Read + io::Write> io::Read for TlsStream<S> {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        self.0.read(buf)
    }
}

impl<S: io::Read + io::Write> io::Write for TlsStream<S> {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.0.write(buf)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.0.flush()
    }
}

/// Security Framework-specific extensions to `TlsStream`.
pub trait TlsStreamExt<S> {
    /// Returns a shared reference to the Security Framework `SslStream`.
    fn raw_stream(&self) -> &secure_transport::SslStream<S>;

    /// Returns a mutable reference to the Security Framework `SslStream`.
    fn raw_stream_mut(&mut self) -> &mut secure_transport::SslStream<S>;
}

impl<S> TlsStreamExt<S> for ::TlsStream<S> {
    fn raw_stream(&self) -> &secure_transport::SslStream<S> {
        &(self.0).0
    }

    fn raw_stream_mut(&mut self) -> &mut secure_transport::SslStream<S> {
        &mut (self.0).0
    }
}

/// Security Framework-specific extensions to `TlsConnectorBuilder`.
pub trait TlsConnectorBuilderExt {
    /// Deprecated
    #[deprecated(since = "0.1.2", note = "use add_root_certificate")]
    fn anchor_certificates(&mut self, certs: &[SecCertificate]) -> &mut Self;
}

impl TlsConnectorBuilderExt for ::TlsConnectorBuilder {
    fn anchor_certificates(&mut self, certs: &[SecCertificate]) -> &mut Self {
        (self.0).0.roots = certs.to_owned();
        self
    }
}

/// Security Framework-specific extensions to `Error`
pub trait ErrorExt {
    /// Extract the underlying Security Framework error for inspection.
    fn security_framework_error(&self) -> &base::Error;
}

impl ErrorExt for ::Error {
    fn security_framework_error(&self) -> &base::Error {
        &(self.0).0
    }
}
<|endoftext|>