<commit_before>use std::io;
use std::fmt::Display;

#[cfg(all(feature = "wrap_help", not(target_os = "windows")))]
use unicode_width::UnicodeWidthStr;

use args::AnyArg;
use args::settings::ArgSettings;
use term;
use strext::_StrExt;

#[cfg(any(not(feature = "wrap_help"), target_os = "windows"))]
fn str_width(s: &str) -> usize {
    s.len()
}

#[cfg(all(feature = "wrap_help", not(target_os = "windows")))]
fn str_width(s: &str) -> usize {
    UnicodeWidthStr::width(s)
}

const TAB: &'static str = "    ";

pub struct HelpWriter<'a, A> where A: 'a {
    a: &'a A,
    l: usize,
    nlh: bool,
    pub skip_pv: bool,
    term_w: Option<usize>,
}

impl<'a, 'n, 'e, A> HelpWriter<'a, A> where A: AnyArg<'n, 'e> + Display {
    pub fn new(a: &'a A, l: usize, nlh: bool) -> Self {
        HelpWriter {
            a: a,
            l: l,
            nlh: nlh,
            skip_pv: false,
            term_w: term::dimensions().map(|(w, _)| w),
        }
    }
    pub fn write_to<W: io::Write>(&self, w: &mut W) -> io::Result<()> {
        debugln!("fn=write_to;");
        try!(self.short(w));
        try!(self.long(w));
        try!(self.val(w));
        try!(self.help(w));
        write!(w, "\n")
    }

    fn short<W>(&self, w: &mut W) -> io::Result<()>
        where W: io::Write
    {
        debugln!("fn=short;");
        try!(write!(w, "{}", TAB));
        if let Some(s) = self.a.short() {
            write!(w, "-{}", s)
        } else if self.a.has_switch() {
            write!(w, "{}", TAB)
        } else {
            Ok(())
        }
    }

    fn long<W>(&self, w: &mut W) -> io::Result<()>
        where W: io::Write
    {
        debugln!("fn=long;");
        if !self.a.has_switch() {
            return Ok(());
        }
        if self.a.takes_value() {
            if let Some(l) = self.a.long() {
                try!(write!(w, "{}--{}", if self.a.short().is_some() { ", " } else { "" }, l));
            }
            try!(write!(w, " "));
        } else {
            if let Some(l) = self.a.long() {
                try!(write!(w, "{}--{}", if self.a.short().is_some() { ", " } else { "" }, l));
                if !self.nlh || !self.a.is_set(ArgSettings::NextLineHelp) {
                    write_spaces!((self.l + 4) - (l.len() + 2), w);
                }
            } else {
                if !self.nlh || !self.a.is_set(ArgSettings::NextLineHelp) {
                    // 6 is tab (4) + -- (2)
                    write_spaces!((self.l + 6), w);
                }
            }
        }
        Ok(())
    }

    fn val<W>(&self, w: &mut W) -> io::Result<()>
        where W: io::Write
    {
        debugln!("fn=val;");
        if !self.a.takes_value() {
            return Ok(());
        }
        if let Some(ref vec) = self.a.val_names() {
            let mut it = vec.iter().peekable();
            while let Some((_, val)) = it.next() {
                try!(write!(w, "<{}>", val));
                if it.peek().is_some() { try!(write!(w, " ")); }
            }
            let num = vec.len();
            if self.a.is_set(ArgSettings::Multiple) && num == 1 {
                try!(write!(w, "..."));
            }
        } else if let Some(num) = self.a.num_vals() {
            let mut it = (0..num).peekable();
            while let Some(_) = it.next() {
                try!(write!(w, "<{}>", self.a.name()));
                if it.peek().is_some() { try!(write!(w, " ")); }
            }
        } else {
            try!(write!(w, "{}", self.a));
        }
        if self.a.has_switch() {
            if !(self.nlh || self.a.is_set(ArgSettings::NextLineHelp)) {
                let self_len = self.a.to_string().len();
                // subtract ourself
                let mut spcs = self.l - self_len;
                // Since we're writing spaces from the tab point we first need to know if we
                // had a long and short, or just short
                if self.a.long().is_some() {
                    // Only account 4 after the val
                    spcs += 4;
                } else {
                    // Only account for ', --' + 4 after the val
                    spcs += 8;
                }
                write_spaces!(spcs, w);
            }
        } else if !(self.nlh || self.a.is_set(ArgSettings::NextLineHelp)) {
            write_spaces!(self.l + 4 - (self.a.to_string().len()), w);
        }
        Ok(())
    }

    fn help<W>(&self, w: &mut W) -> io::Result<()>
        where W: io::Write
    {
        debugln!("fn=help;");
        let spec_vals = self.spec_vals();
        let mut help = String::new();
        let h = self.a.help().unwrap_or("");
        let spcs = if self.nlh || self.a.is_set(ArgSettings::NextLineHelp) {
            8 // "tab" + "tab"
        } else {
            self.l + 12
        };
        // determine if our help fits or needs to wrap
        let width = self.term_w.unwrap_or(0);
        debugln!("Term width...{}", width);
        let too_long = self.term_w.is_some() && (spcs + str_width(h) + str_width(&*spec_vals) >= width);
        debugln!("Too long...{:?}", too_long);

        // Is help on next line, if so newline + 2x tab
        if self.nlh || self.a.is_set(ArgSettings::NextLineHelp) {
            try!(write!(w, "\n{}{}", TAB, TAB));
        }

        debug!("Too long...");
        if too_long {
            sdebugln!("Yes");
            help.push_str(h);
            help.push_str(&*spec_vals);
            debugln!("help: {}", help);
            debugln!("help width: {}", str_width(help));
            // Determine how many newlines we need to insert
            let avail_chars = width - spcs;
            debugln!("Usable space: {}", avail_chars);
            let longest_w = {
                let mut lw = 0;
                for l in help.split(' ').map(|s| str_width(s)) {
                    if l > lw {
                        lw = l;
                    }
                }
                lw
            };
            debugln!("Longest word...{}", longest_w);
            debug!("Enough space to wrap...");
            if longest_w < avail_chars {
                sdebugln!("Yes");
                let mut indices = vec![];
                let mut idx = 0;
                loop {
                    idx += avail_chars - 1;
                    if idx >= help.len() { break; }
                    // 'a' arbitrary non space char
                    if help.chars().nth(idx).unwrap_or('a') != ' ' {
                        idx = find_idx_of_space(&*help, idx);
                    }
                    debugln!("Adding idx: {}", idx);
                    debugln!("At {}: {:?}", idx, help.chars().nth(idx));
                    indices.push(idx);
                    if str_width(&help[idx..]) <= avail_chars {
                        break;
                    }
                }
                for (i, idx) in indices.iter().enumerate() {
                    debugln!("iter;i={},idx={}", i, idx);
                    let j = idx + (2 * i);
                    debugln!("removing: {}", j);
                    debugln!("at {}: {:?}", j, help.chars().nth(j));
                    help.remove(j);
                    help.insert(j, '{');
                    help.insert(j + 1 , 'n');
                    help.insert(j + 2, '}');
                }
            } else {
                sdebugln!("No");
            }
        } else { sdebugln!("No"); }
        let help = if !help.is_empty() {
            &*help
        } else if !spec_vals.is_empty() {
            help.push_str(h);
            help.push_str(&*spec_vals);
            &*help
        } else {
            h
        };
        if help.contains("{n}") {
            if let Some(part) = help.split("{n}").next() {
                try!(write!(w, "{}", part));
            }
            for part in help.split("{n}").skip(1) {
                try!(write!(w, "\n"));
                if self.nlh || self.a.is_set(ArgSettings::NextLineHelp) {
                    try!(write!(w, "{}{}", TAB, TAB));
                } else if self.a.has_switch() {
                    write_spaces!(self.l + 12, w);
                } else {
                    write_spaces!(self.l + 8, w);
                }
                try!(write!(w, "{}", part));
            }
        } else {
            try!(write!(w, "{}", help));
        }
        Ok(())
    }

    fn spec_vals(&self) -> String {
        debugln!("fn=spec_vals;");
        if let Some(ref pv) = self.a.default_val() {
            debugln!("Writing defaults");
            return format!(" [default: {}] {}", pv,
                if !self.skip_pv {
                    if let Some(ref pv) = self.a.possible_vals() {
                        format!(" [values: {}]", pv.join(", "))
                    } else { "".into() }
                } else { "".into() }
            );
        } else if !self.skip_pv {
            debugln!("Writing values");
            if let Some(ref pv) = self.a.possible_vals() {
                debugln!("Possible vals...{:?}", pv);
                return format!(" [values: {}]", pv.join(", "));
            }
        }
        String::new()
    }
}

fn find_idx_of_space(full: &str, mut start: usize) -> usize {
    debugln!("fn=find_idx_of_space;");
    let haystack = if full._is_char_boundary(start) {
        &full[..start]
    } else {
        while !full._is_char_boundary(start) { start -= 1; }
        &full[..start]
    };
    debugln!("haystack: {}", haystack);
    for (i, c) in haystack.chars().rev().enumerate() {
        debugln!("iter;c={},i={}", c, i);
        if c == ' ' {
            debugln!("Found space returning start-i...{}", start - (i + 1));
            return start - (i + 1);
        }
    }
    0
}
<commit_msg>fix: fixes compiling with debug cargo feature<commit_after>use std::io;
use std::fmt::Display;

#[cfg(all(feature = "wrap_help", not(target_os = "windows")))]
use unicode_width::UnicodeWidthStr;

use args::AnyArg;
use args::settings::ArgSettings;
use term;
use strext::_StrExt;

#[cfg(any(not(feature = "wrap_help"), target_os = "windows"))]
fn str_width(s: &str) -> usize {
    s.len()
}

#[cfg(all(feature = "wrap_help", not(target_os = "windows")))]
fn str_width(s: &str) -> usize {
    UnicodeWidthStr::width(s)
}

const TAB: &'static str = "    ";

pub struct HelpWriter<'a, A> where A: 'a {
    a: &'a A,
    l: usize,
    nlh: bool,
    pub skip_pv: bool,
    term_w: Option<usize>,
}

impl<'a, 'n, 'e, A> HelpWriter<'a, A> where A: AnyArg<'n, 'e> + Display {
    pub fn new(a: &'a A, l: usize, nlh: bool) -> Self {
        HelpWriter {
            a: a,
            l: l,
            nlh: nlh,
            skip_pv: false,
            term_w: term::dimensions().map(|(w, _)| w),
        }
    }
    pub fn write_to<W: io::Write>(&self, w: &mut W) -> io::Result<()> {
        debugln!("fn=write_to;");
        try!(self.short(w));
        try!(self.long(w));
        try!(self.val(w));
        try!(self.help(w));
        write!(w, "\n")
    }

    fn short<W>(&self, w: &mut W) -> io::Result<()>
        where W: io::Write
    {
        debugln!("fn=short;");
        try!(write!(w, "{}", TAB));
        if let Some(s) = self.a.short() {
            write!(w, "-{}", s)
        } else if self.a.has_switch() {
            write!(w, "{}", TAB)
        } else {
            Ok(())
        }
    }

    fn long<W>(&self, w: &mut W) -> io::Result<()>
        where W: io::Write
    {
        debugln!("fn=long;");
        if !self.a.has_switch() {
            return Ok(());
        }
        if self.a.takes_value() {
            if let Some(l) = self.a.long() {
                try!(write!(w, "{}--{}", if self.a.short().is_some() { ", " } else { "" }, l));
            }
            try!(write!(w, " "));
        } else {
            if let Some(l) = self.a.long() {
                try!(write!(w, "{}--{}", if self.a.short().is_some() { ", " } else { "" }, l));
                if !self.nlh || !self.a.is_set(ArgSettings::NextLineHelp) {
                    write_spaces!((self.l + 4) - (l.len() + 2), w);
                }
            } else {
                if !self.nlh || !self.a.is_set(ArgSettings::NextLineHelp) {
                    // 6 is tab (4) + -- (2)
                    write_spaces!((self.l + 6), w);
                }
            }
        }
        Ok(())
    }

    fn val<W>(&self, w: &mut W) -> io::Result<()>
        where W: io::Write
    {
        debugln!("fn=val;");
        if !self.a.takes_value() {
            return Ok(());
        }
        if let Some(ref vec) = self.a.val_names() {
            let mut it = vec.iter().peekable();
            while let Some((_, val)) = it.next() {
                try!(write!(w, "<{}>", val));
                if it.peek().is_some() { try!(write!(w, " ")); }
            }
            let num = vec.len();
            if self.a.is_set(ArgSettings::Multiple) && num == 1 {
                try!(write!(w, "..."));
            }
        } else if let Some(num) = self.a.num_vals() {
            let mut it = (0..num).peekable();
            while let Some(_) = it.next() {
                try!(write!(w, "<{}>", self.a.name()));
                if it.peek().is_some() { try!(write!(w, " ")); }
            }
        } else {
            try!(write!(w, "{}", self.a));
        }
        if self.a.has_switch() {
            if !(self.nlh || self.a.is_set(ArgSettings::NextLineHelp)) {
                let self_len = self.a.to_string().len();
                // subtract ourself
                let mut spcs = self.l - self_len;
                // Since we're writing spaces from the tab point we first need to know if we
                // had a long and short, or just short
                if self.a.long().is_some() {
                    // Only account 4 after the val
                    spcs += 4;
                } else {
                    // Only account for ', --' + 4 after the val
                    spcs += 8;
                }
                write_spaces!(spcs, w);
            }
        } else if !(self.nlh || self.a.is_set(ArgSettings::NextLineHelp)) {
            write_spaces!(self.l + 4 - (self.a.to_string().len()), w);
        }
        Ok(())
    }

    fn help<W>(&self, w: &mut W) -> io::Result<()>
        where W: io::Write
    {
        debugln!("fn=help;");
        let spec_vals = self.spec_vals();
        let mut help = String::new();
        let h = self.a.help().unwrap_or("");
        let spcs = if self.nlh || self.a.is_set(ArgSettings::NextLineHelp) {
            8 // "tab" + "tab"
        } else {
            self.l + 12
        };
        // determine if our help fits or needs to wrap
        let width = self.term_w.unwrap_or(0);
        debugln!("Term width...{}", width);
        let too_long = self.term_w.is_some() && (spcs + str_width(h) + str_width(&*spec_vals) >= width);
        debugln!("Too long...{:?}", too_long);

        // Is help on next line, if so newline + 2x tab
        if self.nlh || self.a.is_set(ArgSettings::NextLineHelp) {
            try!(write!(w, "\n{}{}", TAB, TAB));
        }

        debug!("Too long...");
        if too_long {
            sdebugln!("Yes");
            help.push_str(h);
            help.push_str(&*spec_vals);
            debugln!("help: {}", help);
            debugln!("help width: {}", str_width(&*help));
            // Determine how many newlines we need to insert
            let avail_chars = width - spcs;
            debugln!("Usable space: {}", avail_chars);
            let longest_w = {
                let mut lw = 0;
                for l in help.split(' ').map(|s| str_width(s)) {
                    if l > lw {
                        lw = l;
                    }
                }
                lw
            };
            debugln!("Longest word...{}", longest_w);
            debug!("Enough space to wrap...");
            if longest_w < avail_chars {
                sdebugln!("Yes");
                let mut indices = vec![];
                let mut idx = 0;
                loop {
                    idx += avail_chars - 1;
                    if idx >= help.len() { break; }
                    // 'a' arbitrary non space char
                    if help.chars().nth(idx).unwrap_or('a') != ' ' {
                        idx = find_idx_of_space(&*help, idx);
                    }
                    debugln!("Adding idx: {}", idx);
                    debugln!("At {}: {:?}", idx, help.chars().nth(idx));
                    indices.push(idx);
                    if str_width(&help[idx..]) <= avail_chars {
                        break;
                    }
                }
                for (i, idx) in indices.iter().enumerate() {
                    debugln!("iter;i={},idx={}", i, idx);
                    let j = idx + (2 * i);
                    debugln!("removing: {}", j);
                    debugln!("at {}: {:?}", j, help.chars().nth(j));
                    help.remove(j);
                    help.insert(j, '{');
                    help.insert(j + 1 , 'n');
                    help.insert(j + 2, '}');
                }
            } else {
                sdebugln!("No");
            }
        } else { sdebugln!("No"); }
        let help = if !help.is_empty() {
            &*help
        } else if !spec_vals.is_empty() {
            help.push_str(h);
            help.push_str(&*spec_vals);
            &*help
        } else {
            h
        };
        if help.contains("{n}") {
            if let Some(part) = help.split("{n}").next() {
                try!(write!(w, "{}", part));
            }
            for part in help.split("{n}").skip(1) {
                try!(write!(w, "\n"));
                if self.nlh || self.a.is_set(ArgSettings::NextLineHelp) {
                    try!(write!(w, "{}{}", TAB, TAB));
                } else if self.a.has_switch() {
                    write_spaces!(self.l + 12, w);
                } else {
                    write_spaces!(self.l + 8, w);
                }
                try!(write!(w, "{}", part));
            }
        } else {
            try!(write!(w, "{}", help));
        }
        Ok(())
    }

    fn spec_vals(&self) -> String {
        debugln!("fn=spec_vals;");
        if let Some(ref pv) = self.a.default_val() {
            debugln!("Writing defaults");
            return format!(" [default: {}] {}", pv,
                if !self.skip_pv {
                    if let Some(ref pv) = self.a.possible_vals() {
                        format!(" [values: {}]", pv.join(", "))
                    } else { "".into() }
                } else { "".into() }
            );
        } else if !self.skip_pv {
            debugln!("Writing values");
            if let Some(ref pv) = self.a.possible_vals() {
                debugln!("Possible vals...{:?}", pv);
                return format!(" [values: {}]", pv.join(", "));
            }
        }
        String::new()
    }
}

fn find_idx_of_space(full: &str, mut start: usize) -> usize {
    debugln!("fn=find_idx_of_space;");
    let haystack = if full._is_char_boundary(start) {
        &full[..start]
    } else {
        while !full._is_char_boundary(start) { start -= 1; }
        &full[..start]
    };
    debugln!("haystack: {}", haystack);
    for (i, c) in haystack.chars().rev().enumerate() {
        debugln!("iter;c={},i={}", c, i);
        if c == ' ' {
            debugln!("Found space returning start-i...{}", start - (i + 1));
            return start - (i + 1);
        }
    }
    0
}
<|endoftext|>


<commit_before>extern crate x11;
extern crate libc;

use std::collections::HashMap;
// use super::Window;
use x11::xlib::{Window};
use super::Container;
use super::super::libx;
use super::super::libx::{ Context };

pub struct Workspaces {
    current: char,
    context: Context,
    pub spaces: HashMap<char, Container>,
}

impl Workspaces {
    pub fn new(context: Context) -> Workspaces {
        Workspaces {
            current: '0',
            context: context,
            spaces: HashMap::new(),
        }
    }

    pub fn contain(&self, key: char) -> bool {
        self.spaces.contains_key(&key)
    }

    pub fn create(&mut self, key: char) {
        let mut space = Container::new(self.context);
        self.spaces.insert(key, space);
    }

    pub fn delete(&mut self, key: char) {
        self.spaces.remove(&key);
    }

    pub fn get(&mut self, key: char) -> Option<&mut Container>{
        self.spaces.get_mut(&key)
    }

    pub fn current(&mut self) -> &mut Container {
        self.spaces.get_mut(&self.current).unwrap()
    }

    pub fn current_name(&self) -> char {
        self.current
    }

    pub fn switch_workspace(&mut self, new: char, context: Context){
        if new == self.current {
            return
        }

        if !self.contain(new) {
            // let s = libx::default_screen(context);
            self.create(new);
        }

        let old = self.current;
        match self.get(old) {
            Some(v) => {
                v.unmap();
                v.update_layout();
            }
            None => {}
        }

        self.current = new;
        match self.get(new) {
            Some(v) => {
                println!("workspace {}", v.id);
                v.map();
                v.focus();
                v.update_layout();
            }
            None => {}
        }
    }

    pub fn can_manage(context: libx::Context, id: Window) -> bool {
        let attrs = libx::get_window_attributes(context, id);
        let transientfor_hint = libx::get_transient_for_hint(context, id);
        attrs.override_redirect == 0 && transientfor_hint == 0
    }

    pub fn move_window(&mut self, window: Window, from: char, to: char){
        if from == to {
            return
        }

        if !self.contain(from) {
            self.create(from);
        }
        if !self.contain(to) {
            self.create(to);
        }

        let res = match self.get(from) {
            Some(w) => { w.remove(window) }
            None => { None }
        };

        match self.get(to) {
            Some(w) => {
                if res.is_some(){
                    w.add(res.unwrap());
                }
            }
            None => {}
        }
    }

    pub fn add_window(&mut self, container: Container, workspace: Option<char>) {
        let w = match workspace {
            Some(k) => {
                match self.get(k) {
                    Some(v) => {
                        v
                    }
                    None => {
                        return
                    }
                }
            }
            None => {
                self.current()
            }
        };
        w.add(container);
        w.update_layout();
        w.print_tree(0);
    }

    // insert window just next to old focus
    pub fn insert_window(&mut self, container: Container) {
        match self.get_focus() {
            Some(c) => {
                match c.get_parent(){
                    Some(p) => {
                        let index = p.contain(c.id).unwrap();
                        p.insert(index+1, container);
                        p.update_layout();
                        p.print_tree(0);
                        return
                    }
                    None => {}
                }
            }
            None => {}
        }
        self.add_window(container, None);
    }

    pub fn remove_window(&mut self, window: Window) -> Option<Container>{
        for (k, workspace) in self.spaces.iter_mut() {
            let res =  workspace.remove(window);
            match res {
                Some(w) => {
                    if w.pid.is_some() {
                        let res = workspace.remove(w.pid.unwrap());
                        if res.is_some(){
                            res.unwrap().destroy();
                        };
                    }

                    workspace.update_layout();
                    workspace.print_tree(0);
                    return Some(w)
                }
                None => {

                }
            }
        }
        None
    }

    pub fn set_focus(&mut self, window: Window) {
        match self.get_focus() {
            Some(w) => {
                w.unfocus();
            }
            None => {}
        }

        match self.get_container(window) {
            Some((_, c)) => {
                c.focus()
            }
            None => {}
        }
    }

    pub fn get_focus(&mut self) -> Option<&mut Container> {
        let (w, _) = libx::get_input_focus(self.context);
        let res = self.get_container(w);
        match res {
            Some((k, c)) => {
                Some(c)
            }
            None => { None }
        }
    }

    pub fn get_container(&mut self, id: Window) -> Option<(char, &mut Container)>{
        for (k, w) in self.spaces.iter_mut() {
            let r = w.tree_search(id);
            if r.is_some(){
                return Some((*k, r.unwrap()))
            }
        }
        None
    }

    // pub fn find_window(&self, window: Window) -> Option<(char, usize)> {
    //     for (k, w) in self.spaces.iter() {
    //         let index = w.contain(window);
    //         match index {
    //             Some(i) => {
    //                 return Some((*k, i))
    //             }
    //             None => {}
    //         }
    //     }
    //     None
    // }
}
<commit_msg>try remove empty container<commit_after>extern crate x11;
extern crate libc;

use std::collections::HashMap;
// use super::Window;
use x11::xlib::{Window};
use super::Container;
use super::super::libx;
use super::super::libx::{ Context };

pub struct Workspaces {
    current: char,
    context: Context,
    pub spaces: HashMap<char, Container>,
}

impl Workspaces {
    pub fn new(context: Context) -> Workspaces {
        Workspaces {
            current: '0',
            context: context,
            spaces: HashMap::new(),
        }
    }

    pub fn contain(&self, key: char) -> bool {
        self.spaces.contains_key(&key)
    }

    pub fn create(&mut self, key: char) {
        let mut space = Container::new(self.context);
        self.spaces.insert(key, space);
    }

    pub fn delete(&mut self, key: char) {
        self.spaces.remove(&key);
    }

    pub fn get(&mut self, key: char) -> Option<&mut Container>{
        self.spaces.get_mut(&key)
    }

    pub fn current(&mut self) -> &mut Container {
        self.spaces.get_mut(&self.current).unwrap()
    }

    pub fn current_name(&self) -> char {
        self.current
    }

    pub fn switch_workspace(&mut self, new: char, context: Context){
        if new == self.current {
            return
        }

        if !self.contain(new) {
            // let s = libx::default_screen(context);
            self.create(new);
        }

        let old = self.current;
        match self.get(old) {
            Some(v) => {
                v.unmap();
                v.update_layout();
            }
            None => {}
        }

        self.current = new;
        match self.get(new) {
            Some(v) => {
                println!("workspace {}", v.id);
                v.map();
                v.focus();
                v.update_layout();
            }
            None => {}
        }
    }

    pub fn can_manage(context: libx::Context, id: Window) -> bool {
        let attrs = libx::get_window_attributes(context, id);
        let transientfor_hint = libx::get_transient_for_hint(context, id);
        attrs.override_redirect == 0 && transientfor_hint == 0
    }

    pub fn move_window(&mut self, window: Window, from: char, to: char){
        if from == to {
            return
        }

        if !self.contain(from) {
            self.create(from);
        }
        if !self.contain(to) {
            self.create(to);
        }

        let res = match self.get(from) {
            Some(w) => { w.remove(window) }
            None => { None }
        };

        match self.get(to) {
            Some(w) => {
                if res.is_some(){
                    w.add(res.unwrap());
                }
            }
            None => {}
        }
    }

    pub fn add_window(&mut self, container: Container, workspace: Option<char>) {
        let w = match workspace {
            Some(k) => {
                match self.get(k) {
                    Some(v) => {
                        v
                    }
                    None => {
                        return
                    }
                }
            }
            None => {
                self.current()
            }
        };
        w.add(container);
        w.update_layout();
        w.print_tree(0);
    }

    // insert window just next to old focus
    pub fn insert_window(&mut self, container: Container) {
        match self.get_focus() {
            Some(c) => {
                match c.get_parent(){
                    Some(p) => {
                        let index = p.contain(c.id).unwrap();
                        p.insert(index+1, container);
                        p.update_layout();
                        p.print_tree(0);
                        return
                    }
                    None => {}
                }
            }
            None => {}
        }
        self.add_window(container, None);
    }

    pub fn remove_window(&mut self, window: Window) -> Option<Container>{
        for (k, workspace) in self.spaces.iter_mut() {
            let res =  workspace.remove(window);
            match res {
                Some(w) => {
                    match w.get_parent() {
                        Some(p) => {
                            let res = workspace.remove(p.id);
                            if res.is_some(){
                                res.unwrap().destroy();
                            };
                        }
                        None => {}
                    }
                    // if w.get_parent().is_some() {
                    //     let res = workspace.remove(w.pid.unwrap());
                    //     if res.is_some(){
                    //         res.unwrap().destroy();
                    //     };
                    // }

                    workspace.update_layout();
                    workspace.print_tree(0);
                    return Some(w)
                }
                None => {

                }
            }
        }
        None
    }

    pub fn set_focus(&mut self, window: Window) {
        match self.get_focus() {
            Some(w) => {
                w.unfocus();
            }
            None => {}
        }

        match self.get_container(window) {
            Some((_, c)) => {
                c.focus()
            }
            None => {}
        }
    }

    pub fn get_focus(&mut self) -> Option<&mut Container> {
        let (w, _) = libx::get_input_focus(self.context);
        let res = self.get_container(w);
        match res {
            Some((k, c)) => {
                Some(c)
            }
            None => { None }
        }
    }

    pub fn get_container(&mut self, id: Window) -> Option<(char, &mut Container)>{
        for (k, w) in self.spaces.iter_mut() {
            let r = w.tree_search(id);
            if r.is_some(){
                return Some((*k, r.unwrap()))
            }
        }
        None
    }

    // pub fn find_window(&self, window: Window) -> Option<(char, usize)> {
    //     for (k, w) in self.spaces.iter() {
    //         let index = w.contain(window);
    //         match index {
    //             Some(i) => {
    //                 return Some((*k, i))
    //             }
    //             None => {}
    //         }
    //     }
    //     None
    // }
}
<|endoftext|>


<commit_before>// Copyright 2018 The Grin Developers
//
// 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.

//! Builds the blinded output and related signature proof for the block
//! reward.
use crate::consensus::reward;
use crate::core::transaction::kernel_sig_msg;
use crate::core::{KernelFeatures, Output, OutputFeatures, TxKernel};
use crate::keychain::{Identifier, Keychain};
use crate::libtx::error::Error;
use crate::libtx::{aggsig, proof};
use crate::util::{secp, static_secp_instance};

/// output a reward output
pub fn output<K>(
	keychain: &K,
	key_id: &Identifier,
	fees: u64,
	test_mode: bool,
) -> Result<(Output, TxKernel), Error>
where
	K: Keychain,
{
	let value = reward(fees);
	let commit = keychain.commit(value, key_id)?;

	trace!("Block reward - Pedersen Commit is: {:?}", commit,);

	let rproof = proof::create(keychain, value, key_id, commit, None)?;

	let output = Output {
		features: OutputFeatures::Coinbase,
		commit: commit,
		proof: rproof,
	};

	let secp = static_secp_instance();
	let secp = secp.lock();
	let over_commit = secp.commit_value(reward(fees))?;
	let out_commit = output.commitment();
	let excess = secp.commit_sum(vec![out_commit], vec![over_commit])?;
	let pubkey = excess.to_pubkey(&secp)?;

	// NOTE: Remember we sign the fee *and* the lock_height.
	// For a coinbase output the fee is 0 and the lock_height is 0
	let msg = kernel_sig_msg(0, 0, KernelFeatures::Coinbase)?;
	let sig = match test_mode {
		true => {
			let test_nonce = secp::key::SecretKey::from_slice(&secp, &[0u8; 32])?;
			aggsig::sign_from_key_id(
				&secp,
				keychain,
				&msg,
				value,
				&key_id,
				Some(&test_nonce),
				Some(&pubkey),
			)?
		}
		false => {
			aggsig::sign_from_key_id(&secp, keychain, &msg, value, &key_id, None, Some(&pubkey))?
		}
	};

	let proof = TxKernel {
		features: KernelFeatures::Coinbase,
		excess: excess,
		excess_sig: sig,
		fee: 0,
		// lock_height here is 0
		// *not* the maturity of the coinbase output (only spendable 1,440 blocks later)
		lock_height: 0,
	};
	Ok((output, proof))
}
<commit_msg>0 the 0 that should have been a 1 (#2711)<commit_after>// Copyright 2018 The Grin Developers
//
// 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.

//! Builds the blinded output and related signature proof for the block
//! reward.
use crate::consensus::reward;
use crate::core::transaction::kernel_sig_msg;
use crate::core::{KernelFeatures, Output, OutputFeatures, TxKernel};
use crate::keychain::{Identifier, Keychain};
use crate::libtx::error::Error;
use crate::libtx::{aggsig, proof};
use crate::util::{secp, static_secp_instance};

/// output a reward output
pub fn output<K>(
	keychain: &K,
	key_id: &Identifier,
	fees: u64,
	test_mode: bool,
) -> Result<(Output, TxKernel), Error>
where
	K: Keychain,
{
	let value = reward(fees);
	let commit = keychain.commit(value, key_id)?;

	trace!("Block reward - Pedersen Commit is: {:?}", commit,);

	let rproof = proof::create(keychain, value, key_id, commit, None)?;

	let output = Output {
		features: OutputFeatures::Coinbase,
		commit: commit,
		proof: rproof,
	};

	let secp = static_secp_instance();
	let secp = secp.lock();
	let over_commit = secp.commit_value(reward(fees))?;
	let out_commit = output.commitment();
	let excess = secp.commit_sum(vec![out_commit], vec![over_commit])?;
	let pubkey = excess.to_pubkey(&secp)?;

	// NOTE: Remember we sign the fee *and* the lock_height.
	// For a coinbase output the fee is 0 and the lock_height is 0
	let msg = kernel_sig_msg(0, 0, KernelFeatures::Coinbase)?;
	let sig = match test_mode {
		true => {
			let test_nonce = secp::key::SecretKey::from_slice(&secp, &[1; 32])?;
			aggsig::sign_from_key_id(
				&secp,
				keychain,
				&msg,
				value,
				&key_id,
				Some(&test_nonce),
				Some(&pubkey),
			)?
		}
		false => {
			aggsig::sign_from_key_id(&secp, keychain, &msg, value, &key_id, None, Some(&pubkey))?
		}
	};

	let proof = TxKernel {
		features: KernelFeatures::Coinbase,
		excess: excess,
		excess_sig: sig,
		fee: 0,
		// lock_height here is 0
		// *not* the maturity of the coinbase output (only spendable 1,440 blocks later)
		lock_height: 0,
	};
	Ok((output, proof))
}
<|endoftext|>


<commit_before>//
// Mnemosyne: a functional systems programming language.
// (c) 2015 Hawk Weisman
//
// Mnemosyne is released under the MIT License. Please refer to
// the LICENSE file at the top-level directory of this distribution
// or at https://github.com/hawkw/mnemosyne/.
//

use std::iter;
use std::fmt::Write;
use std::rc::Rc;

use ::forktable::ForkTable;

use ast::*;
use self::annotations::*;
use self::types::Type;

/// A symbol table is a `ForkTable` mapping `String`s to `Type` annotations.
///
/// As of `84f1de8`, symbol annotations can be type annotations for values
/// or annotations for type definitions. This is provisional; type defs
/// may be moved to a separate  table later.
///
/// This table should be forked upon entering a new scope.
pub type SymbolTable<'a> = ForkTable<'a, String, SymbolAnnotation<'a>>;

#[macro_use]
macro_rules! indent {
    ($to:expr) => ( iter::repeat('\t')
                        .take($to)
                        .collect::<String>() )
}

pub mod ast;
pub mod types;
pub mod annotations;

impl<'a> AnnotateTypes<'a> for Unscoped<'a, Form<'a, UnscopedState>> {
    #[allow(unused_variables)]
    fn annotate_types(self, scope: SymbolTable) -> Scoped<'a, Self> {
        unimplemented!() //TODO: implement
    }
}

impl<'a> AnnotateTypes<'a> for Unscoped<'a, DefForm<'a, UnscopedState>> {
    #[allow(unused_variables)]
    fn annotate_types(self, scope: SymbolTable) -> Scoped<'a, Self>{
        unimplemented!() //TODO: implement
    }
}

#[derive(Clone,Debug,PartialEq)]
pub enum SymbolAnnotation<'a> {
    TypeDef(Type)
  , Value { /// The type of the symbol
            ty: Type,
            /// An optional proven value for the symbol.
            ///
            /// This should be defined iff the symbol signifies a constant value
            /// or a constant expression, or if we were able to prove that the value
            /// remains constant within the current scope.
            proven_value: Option<Rc<Expr<'a, ScopedState>>>
          }
}
<commit_msg>Some (bad) first passes on type analysis functions<commit_after>//
// Mnemosyne: a functional systems programming language.
// (c) 2015 Hawk Weisman
//
// Mnemosyne is released under the MIT License. Please refer to
// the LICENSE file at the top-level directory of this distribution
// or at https://github.com/hawkw/mnemosyne/.
//

use std::iter;
use std::fmt::Write;
use std::rc::Rc;

use ::forktable::ForkTable;

use ast::*;
use self::annotations::*;
use self::types::Type;

/// A symbol table is a `ForkTable` mapping `String`s to `Type` annotations.
///
/// As of `84f1de8`, symbol annotations can be type annotations for values
/// or annotations for type definitions. This is provisional; type defs
/// may be moved to a separate table later.
///
/// This table should be forked upon entering a new scope.
pub type SymbolTable<'a> = ForkTable<'a, String, SymbolAnnotation<'a>>;

#[macro_use]
macro_rules! indent {
    ($to:expr) => ( iter::repeat('\t')
                        .take($to)
                        .collect::<String>() )
}

pub mod ast;
pub mod types;
pub mod annotations;

impl<'a> AnnotateTypes<'a> for Unscoped<'a, Form<'a, UnscopedState>> {
    #[allow(unused_variables)]
    fn annotate_types(self, scope: SymbolTable) -> Scoped<'a, Self> {
        unimplemented!() //TODO: implement
    }
}

impl<'a> AnnotateTypes<'a> for Unscoped<'a, DefForm<'a, UnscopedState>> {
    #[allow(unused_variables)]
    fn annotate_types(self, scope: SymbolTable) -> Scoped<'a, Self>{
        unimplemented!() //TODO: implement
    }
}

#[derive(Clone,Debug,PartialEq)]
pub enum SymbolAnnotation<'a> {
    TypeDef(Type)
  , Value { /// The type of the symbol
            ty: Type,
            /// An optional proven value for the symbol.
            ///
            /// This should be defined iff the symbol signifies a constant value
            /// or a constant expression, or if we were able to prove that the value
            /// remains constant within the current scope.
            proven_value: Option<Rc<Expr<'a, ScopedState>>>
          }
}

impl<'a> SymbolAnnotation<'a> {
    // TODO: possibly this should recurse into references? IDK
    pub fn is_fn_type(&self) -> bool {
        match *self {
            SymbolAnnotation::TypeDef(ref ty) =>
                match *ty { Type::Function(_) => true
                          , _                => false
                          }
          , SymbolAnnotation::Value { ref ty, .. } =>
                match *ty { Type::Function(_) => true
                          , _                => false
                          }
        }
    }

    pub fn is_prim_type(&self) -> bool {
        match *self {
            SymbolAnnotation::TypeDef(ref ty) =>
                match *ty { Type::Prim(_) => true
                          , _             => false
                          }
          , _ => false
        }
    }
}
<|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 cookie_storage::CookieSource;
use resource_task::{Metadata, TargetedLoadResponse, LoadData, start_sending_opt, ResponseSenders};
use resource_task::ControlMsg;
use resource_task::ProgressMsg::{Payload, Done};

use log;
use std::collections::HashSet;
use file_loader;
use hyper::client::Request;
use hyper::header::{ContentLength, ContentType, Host, Location};
use hyper::HttpError;
use hyper::method::Method;
use hyper::mime::{Mime, TopLevel, SubLevel};
use hyper::net::HttpConnector;
use hyper::status::{StatusCode, StatusClass};
use std::error::Error;
use openssl::ssl::{SslContext, SslVerifyMode};
use std::io::{self, Read, Write};
use std::sync::mpsc::{Sender, channel};
use std::thunk::Invoke;
use util::task::spawn_named;
use util::resource_files::resources_dir_path;
use util::opts;
use url::{Url, UrlParser};

use std::borrow::ToOwned;

pub fn factory(cookies_chan: Sender<ControlMsg>)
               -> Box<Invoke<(LoadData, Sender<TargetedLoadResponse>)> + Send> {
    box move |(load_data, start_chan)| {
        spawn_named("http_loader".to_owned(), move || load(load_data, start_chan, cookies_chan))
    }
}

fn send_error(url: Url, err: String, senders: ResponseSenders) {
    let mut metadata = Metadata::default(url);
    metadata.status = None;

    match start_sending_opt(senders, metadata) {
        Ok(p) => p.send(Done(Err(err))).unwrap(),
        _ => {}
    };
}

fn load(mut load_data: LoadData, start_chan: Sender<TargetedLoadResponse>, cookies_chan: Sender<ControlMsg>) {
    // FIXME: At the time of writing this FIXME, servo didn't have any central
    //        location for configuration. If you're reading this and such a
    //        repository DOES exist, please update this constant to use it.
    let max_redirects = 50u;
    let mut iters = 0u;
    let mut url = load_data.url.clone();
    let mut redirected_to = HashSet::new();

    let senders = ResponseSenders {
        immediate_consumer: start_chan,
        eventual_consumer: load_data.consumer
    };

    // If the URL is a view-source scheme then the scheme data contains the
    // real URL that should be used for which the source is to be viewed.
    // Change our existing URL to that and keep note that we are viewing
    // the source rather than rendering the contents of the URL.
    let viewing_source = if &*url.scheme == "view-source" {
        let inner_url = load_data.url.non_relative_scheme_data().unwrap();
        url = Url::parse(inner_url).unwrap();
        match &*url.scheme {
            "http" | "https" => {}
            _ => {
                let s = format!("The {} scheme with view-source is not supported", url.scheme);
                send_error(url, s, senders);
                return;
            }
        };
        true
    } else {
        false
    };

    // Loop to handle redirects.
    loop {
        iters = iters + 1;

        if iters > max_redirects {
            send_error(url, "too many redirects".to_string(), senders);
            return;
        }

        match &*url.scheme {
            "http" | "https" => {}
            _ => {
                let s = format!("{} request, but we don't support that scheme", url.scheme);
                send_error(url, s, senders);
                return;
            }
        }

        info!("requesting {}", url.serialize());

        fn verifier(ssl: &mut SslContext) {
            ssl.set_verify(SslVerifyMode::SslVerifyPeer, None);
            let mut certs = resources_dir_path();
            certs.push("certs");
            ssl.set_CA_file(&certs);
        };

        let ssl_err_string = "[UnknownError { library: \"SSL routines\", \
function: \"SSL3_GET_SERVER_CERTIFICATE\", \
reason: \"certificate verify failed\" }]";

        let mut connector = if opts::get().nossl {
            HttpConnector(None)
        } else {
            HttpConnector(Some(box verifier as Box<FnMut(&mut SslContext)>))
        };

        let mut req = match Request::with_connector(load_data.method.clone(), url.clone(), &mut connector) {
            Ok(req) => req,
            Err(HttpError::HttpIoError(ref io_error)) if (
                io_error.kind() == io::ErrorKind::Other &&
                io_error.description() == "Error in OpenSSL" &&
                io_error.detail() == Some(ssl_err_string.to_owned())
            ) => {
                let mut image = resources_dir_path();
                image.push("badcert.html");
                let load_data = LoadData::new(Url::from_file_path(&*image).unwrap(), senders.eventual_consumer);
                file_loader::factory(load_data, senders.immediate_consumer);
                return;
            },
            Err(e) => {
                println!("{:?}", e);
                send_error(url, e.description().to_string(), senders);
                return;
            }
        };

        // Preserve the `host` header set automatically by Request.
        let host = req.headers().get::<Host>().unwrap().clone();

        // Avoid automatically preserving request headers when redirects occur.
        // See https://bugzilla.mozilla.org/show_bug.cgi?id=401564 and
        // https://bugzilla.mozilla.org/show_bug.cgi?id=216828 .
        // Only preserve ones which have been explicitly marked as such.
        if iters == 1 {
            let mut combined_headers = load_data.headers.clone();
            combined_headers.extend(load_data.preserved_headers.iter());
            *req.headers_mut() = combined_headers;
        } else {
            *req.headers_mut() = load_data.preserved_headers.clone();
        }

        req.headers_mut().set(host);

        let (tx, rx) = channel();
        cookies_chan.send(ControlMsg::GetCookiesForUrl(url.clone(), tx, CookieSource::HTTP)).unwrap();
        if let Some(cookie_list) = rx.recv().unwrap() {
            let mut v = Vec::new();
            v.push(cookie_list.into_bytes());
            req.headers_mut().set_raw("Cookie".to_owned(), v);
        }

        // FIXME(seanmonstar): use AcceptEncoding from Hyper once available
        //if !req.headers.has::<AcceptEncoding>() {
            // We currently don't support HTTP Compression (FIXME #2587)
            req.headers_mut().set_raw("Accept-Encoding".to_owned(), vec![b"identity".to_vec()]);
        //}
        if log_enabled!(log::INFO) {
            info!("{}", load_data.method);
            for header in req.headers().iter() {
                info!(" - {}", header);
            }
            info!("{:?}", load_data.data);
        }

        // Avoid automatically sending request body if a redirect has occurred.
        let writer = match load_data.data {
            Some(ref data) if iters == 1 => {
                req.headers_mut().set(ContentLength(data.len() as u64));
                let mut writer = match req.start() {
                    Ok(w) => w,
                    Err(e) => {
                        send_error(url, e.description().to_string(), senders);
                        return;
                    }
                };
                match writer.write_all(&*data) {
                    Err(e) => {
                        send_error(url, e.description().to_string(), senders);
                        return;
                    }
                    _ => {}
                };
                writer
            },
            _ => {
                match load_data.method {
                    Method::Get | Method::Head => (),
                    _ => req.headers_mut().set(ContentLength(0))
                }
                match req.start() {
                    Ok(w) => w,
                    Err(e) => {
                        send_error(url, e.description().to_string(), senders);
                        return;
                    }
                }
            }
        };
        let mut response = match writer.send() {
            Ok(r) => r,
            Err(e) => {
                send_error(url, e.description().to_string(), senders);
                return;
            }
        };

        // Dump headers, but only do the iteration if info!() is enabled.
        info!("got HTTP response {}, headers:", response.status);
        if log_enabled!(log::INFO) {
            for header in response.headers.iter() {
                info!(" - {}", header);
            }
        }

        if let Some(cookies) = response.headers.get_raw("set-cookie") {
            for cookie in cookies.iter() {
                if let Ok(cookies) = String::from_utf8(cookie.clone()) {
                    cookies_chan.send(ControlMsg::SetCookiesForUrl(url.clone(),
                                                                   cookies,
                                                                   CookieSource::HTTP)).unwrap();
                }
            }
        }

        if response.status.class() == StatusClass::Redirection {
            match response.headers.get::<Location>() {
                Some(&Location(ref new_url)) => {
                    // CORS (http://fetch.spec.whatwg.org/#http-fetch, status section, point 9, 10)
                    match load_data.cors {
                        Some(ref c) => {
                            if c.preflight {
                                // The preflight lied
                                send_error(url, "Preflight fetch inconsistent with main fetch".to_string(), senders);
                                return;
                            } else {
                                // XXXManishearth There are some CORS-related steps here,
                                // but they don't seem necessary until credentials are implemented
                            }
                        }
                        _ => {}
                    }
                    let new_url = match UrlParser::new().base_url(&url).parse(&new_url) {
                        Ok(u) => u,
                        Err(e) => {
                            send_error(url, e.to_string(), senders);
                            return;
                        }
                    };
                    info!("redirecting to {}", new_url);
                    url = new_url;

                    // According to https://tools.ietf.org/html/rfc7231#section-6.4.2,
                    // historically UAs have rewritten POST->GET on 301 and 302 responses.
                    if load_data.method == Method::Post &&
                        (response.status == StatusCode::MovedPermanently ||
                         response.status == StatusCode::Found) {
                        load_data.method = Method::Get;
                    }

                    if redirected_to.contains(&url) {
                        send_error(url, "redirect loop".to_string(), senders);
                        return;
                    }

                    redirected_to.insert(url.clone());
                    continue;
                }
                None => ()
            }
        }

        let mut adjusted_headers = response.headers.clone();
        if viewing_source {
            adjusted_headers.set(ContentType(Mime(TopLevel::Text, SubLevel::Plain, vec![])));
        }
        let mut metadata = Metadata::default(url);
        metadata.set_content_type(match adjusted_headers.get() {
            Some(&ContentType(ref mime)) => Some(mime),
            None => None
        });
        metadata.headers = Some(adjusted_headers);
        metadata.status = Some(response.status_raw().clone());

        let progress_chan = match start_sending_opt(senders, metadata) {
            Ok(p) => p,
            _ => return
        };
        loop {
            let mut buf = Vec::with_capacity(1024);

            unsafe { buf.set_len(1024); }
            match response.read(buf.as_mut_slice()) {
                Ok(len) if len > 0 => {
                    unsafe { buf.set_len(len); }
                    if progress_chan.send(Payload(buf)).is_err() {
                        // The send errors when the receiver is out of scope,
                        // which will happen if the fetch has timed out (or has been aborted)
                        // so we don't need to continue with the loading of the file here.
                        return;
                    }
                }
                Ok(_) | Err(_) => {
                    let _ = progress_chan.send(Done(Ok(())));
                    break;
                }
            }
        }

        // We didn't get redirected.
        break;
    }
}
<commit_msg>auto merge of #5312 : frewsxcv/servo/cleanup-view-source, r=jdm<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 cookie_storage::CookieSource;
use resource_task::{Metadata, TargetedLoadResponse, LoadData, start_sending_opt, ResponseSenders};
use resource_task::ControlMsg;
use resource_task::ProgressMsg::{Payload, Done};

use log;
use std::collections::HashSet;
use file_loader;
use hyper::client::Request;
use hyper::header::{ContentLength, ContentType, Host, Location};
use hyper::HttpError;
use hyper::method::Method;
use hyper::mime::{Mime, TopLevel, SubLevel};
use hyper::net::HttpConnector;
use hyper::status::{StatusCode, StatusClass};
use std::error::Error;
use openssl::ssl::{SslContext, SslVerifyMode};
use std::io::{self, Read, Write};
use std::sync::mpsc::{Sender, channel};
use std::thunk::Invoke;
use util::task::spawn_named;
use util::resource_files::resources_dir_path;
use util::opts;
use url::{Url, UrlParser};

use std::borrow::ToOwned;

pub fn factory(cookies_chan: Sender<ControlMsg>)
               -> Box<Invoke<(LoadData, Sender<TargetedLoadResponse>)> + Send> {
    box move |(load_data, start_chan)| {
        spawn_named("http_loader".to_owned(), move || load(load_data, start_chan, cookies_chan))
    }
}

fn send_error(url: Url, err: String, senders: ResponseSenders) {
    let mut metadata = Metadata::default(url);
    metadata.status = None;

    match start_sending_opt(senders, metadata) {
        Ok(p) => p.send(Done(Err(err))).unwrap(),
        _ => {}
    };
}

fn load(mut load_data: LoadData, start_chan: Sender<TargetedLoadResponse>, cookies_chan: Sender<ControlMsg>) {
    // FIXME: At the time of writing this FIXME, servo didn't have any central
    //        location for configuration. If you're reading this and such a
    //        repository DOES exist, please update this constant to use it.
    let max_redirects = 50u;
    let mut iters = 0u;
    let mut url = load_data.url.clone();
    let mut redirected_to = HashSet::new();

    let senders = ResponseSenders {
        immediate_consumer: start_chan,
        eventual_consumer: load_data.consumer
    };

    // If the URL is a view-source scheme then the scheme data contains the
    // real URL that should be used for which the source is to be viewed.
    // Change our existing URL to that and keep note that we are viewing
    // the source rather than rendering the contents of the URL.
    let viewing_source = url.scheme == "view-source";
    if viewing_source {
        let inner_url = load_data.url.non_relative_scheme_data().unwrap();
        url = Url::parse(inner_url).unwrap();
        match &*url.scheme {
            "http" | "https" => {}
            _ => {
                let s = format!("The {} scheme with view-source is not supported", url.scheme);
                send_error(url, s, senders);
                return;
            }
        };
    }

    // Loop to handle redirects.
    loop {
        iters = iters + 1;

        if iters > max_redirects {
            send_error(url, "too many redirects".to_string(), senders);
            return;
        }

        match &*url.scheme {
            "http" | "https" => {}
            _ => {
                let s = format!("{} request, but we don't support that scheme", url.scheme);
                send_error(url, s, senders);
                return;
            }
        }

        info!("requesting {}", url.serialize());

        fn verifier(ssl: &mut SslContext) {
            ssl.set_verify(SslVerifyMode::SslVerifyPeer, None);
            let mut certs = resources_dir_path();
            certs.push("certs");
            ssl.set_CA_file(&certs);
        };

        let ssl_err_string = "[UnknownError { library: \"SSL routines\", \
function: \"SSL3_GET_SERVER_CERTIFICATE\", \
reason: \"certificate verify failed\" }]";

        let mut connector = if opts::get().nossl {
            HttpConnector(None)
        } else {
            HttpConnector(Some(box verifier as Box<FnMut(&mut SslContext)>))
        };

        let mut req = match Request::with_connector(load_data.method.clone(), url.clone(), &mut connector) {
            Ok(req) => req,
            Err(HttpError::HttpIoError(ref io_error)) if (
                io_error.kind() == io::ErrorKind::Other &&
                io_error.description() == "Error in OpenSSL" &&
                io_error.detail() == Some(ssl_err_string.to_owned())
            ) => {
                let mut image = resources_dir_path();
                image.push("badcert.html");
                let load_data = LoadData::new(Url::from_file_path(&*image).unwrap(), senders.eventual_consumer);
                file_loader::factory(load_data, senders.immediate_consumer);
                return;
            },
            Err(e) => {
                println!("{:?}", e);
                send_error(url, e.description().to_string(), senders);
                return;
            }
        };

        // Preserve the `host` header set automatically by Request.
        let host = req.headers().get::<Host>().unwrap().clone();

        // Avoid automatically preserving request headers when redirects occur.
        // See https://bugzilla.mozilla.org/show_bug.cgi?id=401564 and
        // https://bugzilla.mozilla.org/show_bug.cgi?id=216828 .
        // Only preserve ones which have been explicitly marked as such.
        if iters == 1 {
            let mut combined_headers = load_data.headers.clone();
            combined_headers.extend(load_data.preserved_headers.iter());
            *req.headers_mut() = combined_headers;
        } else {
            *req.headers_mut() = load_data.preserved_headers.clone();
        }

        req.headers_mut().set(host);

        let (tx, rx) = channel();
        cookies_chan.send(ControlMsg::GetCookiesForUrl(url.clone(), tx, CookieSource::HTTP)).unwrap();
        if let Some(cookie_list) = rx.recv().unwrap() {
            let mut v = Vec::new();
            v.push(cookie_list.into_bytes());
            req.headers_mut().set_raw("Cookie".to_owned(), v);
        }

        // FIXME(seanmonstar): use AcceptEncoding from Hyper once available
        //if !req.headers.has::<AcceptEncoding>() {
            // We currently don't support HTTP Compression (FIXME #2587)
            req.headers_mut().set_raw("Accept-Encoding".to_owned(), vec![b"identity".to_vec()]);
        //}
        if log_enabled!(log::INFO) {
            info!("{}", load_data.method);
            for header in req.headers().iter() {
                info!(" - {}", header);
            }
            info!("{:?}", load_data.data);
        }

        // Avoid automatically sending request body if a redirect has occurred.
        let writer = match load_data.data {
            Some(ref data) if iters == 1 => {
                req.headers_mut().set(ContentLength(data.len() as u64));
                let mut writer = match req.start() {
                    Ok(w) => w,
                    Err(e) => {
                        send_error(url, e.description().to_string(), senders);
                        return;
                    }
                };
                match writer.write_all(&*data) {
                    Err(e) => {
                        send_error(url, e.description().to_string(), senders);
                        return;
                    }
                    _ => {}
                };
                writer
            },
            _ => {
                match load_data.method {
                    Method::Get | Method::Head => (),
                    _ => req.headers_mut().set(ContentLength(0))
                }
                match req.start() {
                    Ok(w) => w,
                    Err(e) => {
                        send_error(url, e.description().to_string(), senders);
                        return;
                    }
                }
            }
        };
        let mut response = match writer.send() {
            Ok(r) => r,
            Err(e) => {
                send_error(url, e.description().to_string(), senders);
                return;
            }
        };

        // Dump headers, but only do the iteration if info!() is enabled.
        info!("got HTTP response {}, headers:", response.status);
        if log_enabled!(log::INFO) {
            for header in response.headers.iter() {
                info!(" - {}", header);
            }
        }

        if let Some(cookies) = response.headers.get_raw("set-cookie") {
            for cookie in cookies.iter() {
                if let Ok(cookies) = String::from_utf8(cookie.clone()) {
                    cookies_chan.send(ControlMsg::SetCookiesForUrl(url.clone(),
                                                                   cookies,
                                                                   CookieSource::HTTP)).unwrap();
                }
            }
        }

        if response.status.class() == StatusClass::Redirection {
            match response.headers.get::<Location>() {
                Some(&Location(ref new_url)) => {
                    // CORS (http://fetch.spec.whatwg.org/#http-fetch, status section, point 9, 10)
                    match load_data.cors {
                        Some(ref c) => {
                            if c.preflight {
                                // The preflight lied
                                send_error(url, "Preflight fetch inconsistent with main fetch".to_string(), senders);
                                return;
                            } else {
                                // XXXManishearth There are some CORS-related steps here,
                                // but they don't seem necessary until credentials are implemented
                            }
                        }
                        _ => {}
                    }
                    let new_url = match UrlParser::new().base_url(&url).parse(&new_url) {
                        Ok(u) => u,
                        Err(e) => {
                            send_error(url, e.to_string(), senders);
                            return;
                        }
                    };
                    info!("redirecting to {}", new_url);
                    url = new_url;

                    // According to https://tools.ietf.org/html/rfc7231#section-6.4.2,
                    // historically UAs have rewritten POST->GET on 301 and 302 responses.
                    if load_data.method == Method::Post &&
                        (response.status == StatusCode::MovedPermanently ||
                         response.status == StatusCode::Found) {
                        load_data.method = Method::Get;
                    }

                    if redirected_to.contains(&url) {
                        send_error(url, "redirect loop".to_string(), senders);
                        return;
                    }

                    redirected_to.insert(url.clone());
                    continue;
                }
                None => ()
            }
        }

        let mut adjusted_headers = response.headers.clone();
        if viewing_source {
            adjusted_headers.set(ContentType(Mime(TopLevel::Text, SubLevel::Plain, vec![])));
        }
        let mut metadata = Metadata::default(url);
        metadata.set_content_type(match adjusted_headers.get() {
            Some(&ContentType(ref mime)) => Some(mime),
            None => None
        });
        metadata.headers = Some(adjusted_headers);
        metadata.status = Some(response.status_raw().clone());

        let progress_chan = match start_sending_opt(senders, metadata) {
            Ok(p) => p,
            _ => return
        };
        loop {
            let mut buf = Vec::with_capacity(1024);

            unsafe { buf.set_len(1024); }
            match response.read(buf.as_mut_slice()) {
                Ok(len) if len > 0 => {
                    unsafe { buf.set_len(len); }
                    if progress_chan.send(Payload(buf)).is_err() {
                        // The send errors when the receiver is out of scope,
                        // which will happen if the fetch has timed out (or has been aborted)
                        // so we don't need to continue with the loading of the file here.
                        return;
                    }
                }
                Ok(_) | Err(_) => {
                    let _ = progress_chan.send(Done(Ok(())));
                    break;
                }
            }
        }

        // We didn't get redirected.
        break;
    }
}
<|endoftext|>


<commit_before>use std::collections::{HashMap,HashSet};

use std::iter::FromIterator;
use openssl::x509::X509;
use openssl::hash::MessageDigest;

use sozu::messages::{CertFingerprint,CertificateAndKey,Order,HttpFront,TlsFront,Instance};

pub type AppId = String;

#[derive(Debug,Clone,PartialEq,Eq, Serialize, Deserialize)]
pub struct HttpProxy {
  ip_address: String,
  port:       u16,
  fronts:     HashMap<AppId, Vec<HttpFront>>,
  instances:  HashMap<AppId, Vec<Instance>>,
}

#[derive(Debug,Clone,PartialEq,Eq,Serialize, Deserialize)]
pub struct TlsProxy {
  ip_address:   String,
  port:         u16,
  certificates: HashMap<CertFingerprint, CertificateAndKey>,
  fronts:       HashMap<AppId, Vec<TlsFront>>,
  instances:    HashMap<AppId, Vec<Instance>>,
}

#[derive(Debug,Default,Clone,PartialEq,Eq,Serialize,Deserialize)]
pub struct ConfigState {
  instances: HashMap<AppId, Vec<Instance>>,
  http_fronts: HashMap<AppId, Vec<HttpFront>>,
  https_fronts: HashMap<AppId, Vec<TlsFront>>,
  certificates: HashMap<CertFingerprint, CertificateAndKey>,
  //ip, port
  http_addresses: Vec<(String, u16)>,
  https_addresses: Vec<(String, u16)>,
  //tcp:
}

impl ConfigState {
  pub fn add_http_address(&mut self, ip_address: String, port: u16) {
    self.http_addresses.push((ip_address, port))
  }

  pub fn add_https_address(&mut self, ip_address: String, port: u16) {
    self.https_addresses.push((ip_address, port))
  }

  pub fn handle_order(&mut self, order: &Order) {
    match order {
      &Order::AddHttpFront(ref front) => {
        let f = HttpFront {
          app_id:     front.app_id.clone(),
          hostname:   front.hostname.clone(),
          path_begin: front.path_begin.clone(),
        };
        if self.http_fronts.contains_key(&front.app_id) {
          self.http_fronts.get_mut(&front.app_id).map(|front| {
            if !front.contains(&f) {
              front.push(f);
            }
          });
        } else {
          self.http_fronts.insert(front.app_id.clone(), vec!(f));
        }
      },
      &Order::RemoveHttpFront(ref front) => {
        if let Some(front_list) = self.http_fronts.get_mut(&front.app_id) {
          front_list.retain(|el| el.hostname != front.hostname || el.path_begin != front.path_begin);
        }
      },
      &Order::AddCertificate(ref certificate_and_key) => {
        let f = CertificateAndKey {
          certificate:       certificate_and_key.certificate.clone(),
          certificate_chain: certificate_and_key.certificate_chain.clone(),
          key:               certificate_and_key.key.clone(),
        };
        let fingerprint = match X509::from_pem(&certificate_and_key.certificate.as_bytes()[..]).and_then(|cert|
                                  cert.fingerprint(MessageDigest::sha256())) {
          Ok(f)  => f,
          Err(e) => {
            error!("cannot obtain the certificate's fingerprint: {:?}", e);
            return;
          }
        };

        if !self.certificates.contains_key(&fingerprint) {
          self.certificates.insert(fingerprint.clone(), f);
        }
      },
      &Order::RemoveCertificate(ref fingerprint) => {
        self.certificates.remove(fingerprint);
      },
      &Order::AddTlsFront(ref front) => {
        let f = TlsFront {
          app_id:      front.app_id.clone(),
          hostname:    front.hostname.clone(),
          path_begin:  front.path_begin.clone(),
          fingerprint: front.fingerprint.clone(),
        };
        if self.https_fronts.contains_key(&front.app_id) {
          self.https_fronts.get_mut(&front.app_id).map(|front| {
            if !front.contains(&f) {
              front.push(f);
            }
          });
        } else {
          self.https_fronts.insert(front.app_id.clone(), vec!(f));
        }
      },
      &Order::RemoveTlsFront(ref front) => {
        self.https_fronts.remove(&front.app_id);
      },
      &Order::AddInstance(ref instance)  => {
        if self.instances.contains_key(&instance.app_id) {
          self.instances.get_mut(&instance.app_id).map(|instance_vec| {
            if !instance_vec.contains(&instance) {
              instance_vec.push(instance.clone());
            }
          });
        } else {
          self.instances.insert(instance.app_id.clone(), vec!(instance.clone()));
        }
      },
      &Order::RemoveInstance(ref instance) => {
        if let Some(instance_list) = self.instances.get_mut(&instance.app_id) {
          instance_list.retain(|el| el.ip_address != instance.ip_address || el.port != instance.port);
        }
      }
      _ => {}
    }
  }

  pub fn generate_orders(&self) -> Vec<Order> {
    let mut v = Vec::new();
    for (app_id, front_list) in self.http_fronts.iter() {
      for front in front_list {
        v.push(Order::AddHttpFront(HttpFront {
          app_id:     app_id.clone(),
          hostname:   front.hostname.clone(),
          path_begin: front.path_begin.clone(),
        }));
      }
    }

    for certificate_and_key in (&self.certificates).values() {
      v.push(Order::AddCertificate(certificate_and_key.clone()));
    }

    for (app_id, front_list) in &self.https_fronts {
      for front in front_list {
        v.push(Order::AddTlsFront(TlsFront {
          app_id:      app_id.clone(),
          hostname:    front.hostname.clone(),
          path_begin:  front.path_begin.clone(),
          fingerprint: front.fingerprint.clone(),
        }));
      }
    }

    for instance_list in self.instances.values() {
      for instance in instance_list {
        v.push(Order::AddInstance(instance.clone()));
      }
    }

    v
  }

  pub fn diff(&self, other:&ConfigState) -> Vec<Order> {
    let mut my_fronts: HashSet<(&AppId, &HttpFront)> = HashSet::new();
    for (ref app_id, ref front_list) in self.http_fronts.iter() {
      for ref front in front_list.iter() {
        my_fronts.insert((&app_id, &front));
      }
    }
    let mut their_fronts: HashSet<(&AppId, &HttpFront)> = HashSet::new();
    for (ref app_id, ref front_list) in other.http_fronts.iter() {
      for ref front in front_list.iter() {
        their_fronts.insert((&app_id, &front));
      }
    }

    let removed_http_fronts = my_fronts.difference(&their_fronts);
    let added_http_fronts   = their_fronts.difference(&my_fronts);

    let mut my_fronts: HashSet<(&AppId, &TlsFront)> = HashSet::new();
    for (ref app_id, ref front_list) in self.https_fronts.iter() {
      for ref front in front_list.iter() {
        my_fronts.insert((&app_id, &front));
      }
    }
    let mut their_fronts: HashSet<(&AppId, &TlsFront)> = HashSet::new();
    for (ref app_id, ref front_list) in other.https_fronts.iter() {
      for ref front in front_list.iter() {
        their_fronts.insert((&app_id, &front));
      }
    }

    let removed_https_fronts = my_fronts.difference(&their_fronts);
    let added_https_fronts   = their_fronts.difference(&my_fronts);

    let mut my_instances: HashSet<(&AppId, &Instance)> = HashSet::new();
    for (ref app_id, ref instance_list) in self.instances.iter() {
      for ref instance in instance_list.iter() {
        my_instances.insert((&app_id, &instance));
      }
    }
    let mut their_instances: HashSet<(&AppId, &Instance)> = HashSet::new();
    for (ref app_id, ref instance_list) in other.instances.iter() {
      for ref instance in instance_list.iter() {
        their_instances.insert((&app_id, &instance));
      }
    }

    let removed_instances = my_instances.difference(&their_instances);
    let added_instances   = their_instances.difference(&my_instances);

    let my_certificates:    HashSet<(&CertFingerprint, &CertificateAndKey)> = HashSet::from_iter(self.certificates.iter());
    let their_certificates: HashSet<(&CertFingerprint, &CertificateAndKey)> = HashSet::from_iter(other.certificates.iter());

    let removed_certificates = my_certificates.difference(&their_certificates);
    let added_certificates   = their_certificates.difference(&my_certificates);

    let mut v = vec!();

    for &(_, certificate_and_key) in added_certificates {
      v.push(Order::AddCertificate(certificate_and_key.clone()));
    }

    for &(app_id, front) in removed_http_fronts {
     v.push(Order::RemoveHttpFront(HttpFront {
       app_id:     app_id.clone(),
       hostname:   front.hostname.clone(),
       path_begin: front.path_begin.clone(),
      }));
    }

    for &(app_id, front) in removed_https_fronts {
     v.push(Order::RemoveTlsFront(TlsFront {
       app_id:      app_id.clone(),
       hostname:    front.hostname.clone(),
       path_begin:  front.path_begin.clone(),
       fingerprint: front.fingerprint.clone(),
      }));
    }

    for &(_, instance) in added_instances {
      v.push(Order::AddInstance(instance.clone()));
    }

    for &(_, instance) in removed_instances {
      v.push(Order::RemoveInstance(instance.clone()));
    }

    for &(app_id, front) in added_http_fronts {
      v.push(Order::AddHttpFront(HttpFront {
        app_id:     app_id.clone(),
        hostname:   front.hostname.clone(),
        path_begin: front.path_begin.clone(),
      }));
    }

    for &(app_id, front) in added_https_fronts {
      v.push(Order::AddTlsFront(TlsFront {
        app_id:      app_id.clone(),
        hostname:    front.hostname.clone(),
        path_begin:  front.path_begin.clone(),
        fingerprint: front.fingerprint.clone(),
      }));
    }

    for  &(fingerprint, _) in removed_certificates {
      v.push(Order::RemoveCertificate(fingerprint.clone()));
    }
    v
  }
}

#[cfg(test)]
mod tests {
  use super::*;
  use sozu::messages::{Order,HttpFront,Instance};

  #[test]
  fn serialize() {
    let mut state:ConfigState = Default::default();
    state.handle_order(&Order::AddHttpFront(HttpFront { app_id: String::from("app_1"), hostname: String::from("lolcatho.st:8080"), path_begin: String::from("/") }));
    state.handle_order(&Order::AddHttpFront(HttpFront { app_id: String::from("app_2"), hostname: String::from("test.local"), path_begin: String::from("/abc") }));
    state.handle_order(&Order::AddInstance(Instance { app_id: String::from("app_1"), ip_address: String::from("127.0.0.1"), port: 1026 }));
    state.handle_order(&Order::AddInstance(Instance { app_id: String::from("app_1"), ip_address: String::from("127.0.0.2"), port: 1027 }));
    state.handle_order(&Order::AddInstance(Instance { app_id: String::from("app_2"), ip_address: String::from("192.167.1.2"), port: 1026 }));
    state.handle_order(&Order::AddInstance(Instance { app_id: String::from("app_1"), ip_address: String::from("192.168.1.3"), port: 1027 }));
    state.handle_order(&Order::RemoveInstance(Instance { app_id: String::from("app_1"), ip_address: String::from("192.168.1.3"), port: 1027 }));

    /*
    let encoded = state.encode();
    println!("serialized:\n{}", encoded);

    let new_state: Option<HttpProxy> = decode_str(&encoded);
    println!("deserialized:\n{:?}", new_state);
    assert_eq!(new_state, Some(state));
    */
    //assert!(false);
  }

  #[test]
  fn diff() {
    let mut state:ConfigState = Default::default();
    state.handle_order(&Order::AddHttpFront(HttpFront { app_id: String::from("app_1"), hostname: String::from("lolcatho.st:8080"), path_begin: String::from("/") }));
    state.handle_order(&Order::AddHttpFront(HttpFront { app_id: String::from("app_2"), hostname: String::from("test.local"), path_begin: String::from("/abc") }));
    state.handle_order(&Order::AddInstance(Instance { app_id: String::from("app_1"), ip_address: String::from("127.0.0.1"), port: 1026 }));
    state.handle_order(&Order::AddInstance(Instance { app_id: String::from("app_1"), ip_address: String::from("127.0.0.2"), port: 1027 }));
    state.handle_order(&Order::AddInstance(Instance { app_id: String::from("app_2"), ip_address: String::from("192.167.1.2"), port: 1026 }));

    let mut state2:ConfigState = Default::default();
    state2.handle_order(&Order::AddHttpFront(HttpFront { app_id: String::from("app_1"), hostname: String::from("lolcatho.st:8080"), path_begin: String::from("/") }));
    state2.handle_order(&Order::AddInstance(Instance { app_id: String::from("app_1"), ip_address: String::from("127.0.0.1"), port: 1026 }));
    state2.handle_order(&Order::AddInstance(Instance { app_id: String::from("app_1"), ip_address: String::from("127.0.0.2"), port: 1027 }));
    state2.handle_order(&Order::AddInstance(Instance { app_id: String::from("app_1"), ip_address: String::from("127.0.0.2"), port: 1028 }));

   let e = vec!(
     Order::RemoveHttpFront(HttpFront { app_id: String::from("app_2"), hostname: String::from("test.local"), path_begin: String::from("/abc") }),
     Order::RemoveInstance(Instance { app_id: String::from("app_2"), ip_address: String::from("192.167.1.2"), port: 1026 }),
     Order::AddInstance(Instance { app_id: String::from("app_1"), ip_address: String::from("127.0.0.2"), port: 1028 }),
   );
   let expected_diff:HashSet<&Order> = HashSet::from_iter(e.iter());

   let d = state.diff(&state2);
   let diff = HashSet::from_iter(d.iter());
   println!("diff orders:\n{:?}\n", diff);
   println!("expected diff orders:\n{:?}\n", expected_diff);

   assert_eq!(diff, expected_diff);
  }
}
<commit_msg>method new for ConfigState<commit_after>use std::collections::{HashMap,HashSet};

use std::iter::FromIterator;
use openssl::x509::X509;
use openssl::hash::MessageDigest;

use sozu::messages::{CertFingerprint,CertificateAndKey,Order,HttpFront,TlsFront,Instance};

pub type AppId = String;

#[derive(Debug,Clone,PartialEq,Eq, Serialize, Deserialize)]
pub struct HttpProxy {
  ip_address: String,
  port:       u16,
  fronts:     HashMap<AppId, Vec<HttpFront>>,
  instances:  HashMap<AppId, Vec<Instance>>,
}

#[derive(Debug,Clone,PartialEq,Eq,Serialize, Deserialize)]
pub struct TlsProxy {
  ip_address:   String,
  port:         u16,
  certificates: HashMap<CertFingerprint, CertificateAndKey>,
  fronts:       HashMap<AppId, Vec<TlsFront>>,
  instances:    HashMap<AppId, Vec<Instance>>,
}

#[derive(Debug,Default,Clone,PartialEq,Eq,Serialize,Deserialize)]
pub struct ConfigState {
  instances:       HashMap<AppId, Vec<Instance>>,
  http_fronts:     HashMap<AppId, Vec<HttpFront>>,
  https_fronts:    HashMap<AppId, Vec<TlsFront>>,
  certificates:    HashMap<CertFingerprint, CertificateAndKey>,
  //ip, port
  http_addresses:  Vec<(String, u16)>,
  https_addresses: Vec<(String, u16)>,
  //tcp:
}

impl ConfigState {
  pub fn new() -> ConfigState {
    ConfigState {
      instances:       HashMap::new(),
      http_fronts:     HashMap::new(),
      https_fronts:    HashMap::new(),
      certificates:    HashMap::new(),
      http_addresses:  Vec::new(),
      https_addresses: Vec::new(),
    }
  }

  pub fn add_http_address(&mut self, ip_address: String, port: u16) {
    self.http_addresses.push((ip_address, port))
  }

  pub fn add_https_address(&mut self, ip_address: String, port: u16) {
    self.https_addresses.push((ip_address, port))
  }

  pub fn handle_order(&mut self, order: &Order) {
    match order {
      &Order::AddHttpFront(ref front) => {
        let f = HttpFront {
          app_id:     front.app_id.clone(),
          hostname:   front.hostname.clone(),
          path_begin: front.path_begin.clone(),
        };
        if self.http_fronts.contains_key(&front.app_id) {
          self.http_fronts.get_mut(&front.app_id).map(|front| {
            if !front.contains(&f) {
              front.push(f);
            }
          });
        } else {
          self.http_fronts.insert(front.app_id.clone(), vec!(f));
        }
      },
      &Order::RemoveHttpFront(ref front) => {
        if let Some(front_list) = self.http_fronts.get_mut(&front.app_id) {
          front_list.retain(|el| el.hostname != front.hostname || el.path_begin != front.path_begin);
        }
      },
      &Order::AddCertificate(ref certificate_and_key) => {
        let f = CertificateAndKey {
          certificate:       certificate_and_key.certificate.clone(),
          certificate_chain: certificate_and_key.certificate_chain.clone(),
          key:               certificate_and_key.key.clone(),
        };
        let fingerprint = match X509::from_pem(&certificate_and_key.certificate.as_bytes()[..]).and_then(|cert|
                                  cert.fingerprint(MessageDigest::sha256())) {
          Ok(f)  => f,
          Err(e) => {
            error!("cannot obtain the certificate's fingerprint: {:?}", e);
            return;
          }
        };

        if !self.certificates.contains_key(&fingerprint) {
          self.certificates.insert(fingerprint.clone(), f);
        }
      },
      &Order::RemoveCertificate(ref fingerprint) => {
        self.certificates.remove(fingerprint);
      },
      &Order::AddTlsFront(ref front) => {
        let f = TlsFront {
          app_id:      front.app_id.clone(),
          hostname:    front.hostname.clone(),
          path_begin:  front.path_begin.clone(),
          fingerprint: front.fingerprint.clone(),
        };
        if self.https_fronts.contains_key(&front.app_id) {
          self.https_fronts.get_mut(&front.app_id).map(|front| {
            if !front.contains(&f) {
              front.push(f);
            }
          });
        } else {
          self.https_fronts.insert(front.app_id.clone(), vec!(f));
        }
      },
      &Order::RemoveTlsFront(ref front) => {
        self.https_fronts.remove(&front.app_id);
      },
      &Order::AddInstance(ref instance)  => {
        if self.instances.contains_key(&instance.app_id) {
          self.instances.get_mut(&instance.app_id).map(|instance_vec| {
            if !instance_vec.contains(&instance) {
              instance_vec.push(instance.clone());
            }
          });
        } else {
          self.instances.insert(instance.app_id.clone(), vec!(instance.clone()));
        }
      },
      &Order::RemoveInstance(ref instance) => {
        if let Some(instance_list) = self.instances.get_mut(&instance.app_id) {
          instance_list.retain(|el| el.ip_address != instance.ip_address || el.port != instance.port);
        }
      }
      _ => {}
    }
  }

  pub fn generate_orders(&self) -> Vec<Order> {
    let mut v = Vec::new();
    for (app_id, front_list) in self.http_fronts.iter() {
      for front in front_list {
        v.push(Order::AddHttpFront(HttpFront {
          app_id:     app_id.clone(),
          hostname:   front.hostname.clone(),
          path_begin: front.path_begin.clone(),
        }));
      }
    }

    for certificate_and_key in (&self.certificates).values() {
      v.push(Order::AddCertificate(certificate_and_key.clone()));
    }

    for (app_id, front_list) in &self.https_fronts {
      for front in front_list {
        v.push(Order::AddTlsFront(TlsFront {
          app_id:      app_id.clone(),
          hostname:    front.hostname.clone(),
          path_begin:  front.path_begin.clone(),
          fingerprint: front.fingerprint.clone(),
        }));
      }
    }

    for instance_list in self.instances.values() {
      for instance in instance_list {
        v.push(Order::AddInstance(instance.clone()));
      }
    }

    v
  }

  pub fn diff(&self, other:&ConfigState) -> Vec<Order> {
    let mut my_fronts: HashSet<(&AppId, &HttpFront)> = HashSet::new();
    for (ref app_id, ref front_list) in self.http_fronts.iter() {
      for ref front in front_list.iter() {
        my_fronts.insert((&app_id, &front));
      }
    }
    let mut their_fronts: HashSet<(&AppId, &HttpFront)> = HashSet::new();
    for (ref app_id, ref front_list) in other.http_fronts.iter() {
      for ref front in front_list.iter() {
        their_fronts.insert((&app_id, &front));
      }
    }

    let removed_http_fronts = my_fronts.difference(&their_fronts);
    let added_http_fronts   = their_fronts.difference(&my_fronts);

    let mut my_fronts: HashSet<(&AppId, &TlsFront)> = HashSet::new();
    for (ref app_id, ref front_list) in self.https_fronts.iter() {
      for ref front in front_list.iter() {
        my_fronts.insert((&app_id, &front));
      }
    }
    let mut their_fronts: HashSet<(&AppId, &TlsFront)> = HashSet::new();
    for (ref app_id, ref front_list) in other.https_fronts.iter() {
      for ref front in front_list.iter() {
        their_fronts.insert((&app_id, &front));
      }
    }

    let removed_https_fronts = my_fronts.difference(&their_fronts);
    let added_https_fronts   = their_fronts.difference(&my_fronts);

    let mut my_instances: HashSet<(&AppId, &Instance)> = HashSet::new();
    for (ref app_id, ref instance_list) in self.instances.iter() {
      for ref instance in instance_list.iter() {
        my_instances.insert((&app_id, &instance));
      }
    }
    let mut their_instances: HashSet<(&AppId, &Instance)> = HashSet::new();
    for (ref app_id, ref instance_list) in other.instances.iter() {
      for ref instance in instance_list.iter() {
        their_instances.insert((&app_id, &instance));
      }
    }

    let removed_instances = my_instances.difference(&their_instances);
    let added_instances   = their_instances.difference(&my_instances);

    let my_certificates:    HashSet<(&CertFingerprint, &CertificateAndKey)> = HashSet::from_iter(self.certificates.iter());
    let their_certificates: HashSet<(&CertFingerprint, &CertificateAndKey)> = HashSet::from_iter(other.certificates.iter());

    let removed_certificates = my_certificates.difference(&their_certificates);
    let added_certificates   = their_certificates.difference(&my_certificates);

    let mut v = vec!();

    for &(_, certificate_and_key) in added_certificates {
      v.push(Order::AddCertificate(certificate_and_key.clone()));
    }

    for &(app_id, front) in removed_http_fronts {
     v.push(Order::RemoveHttpFront(HttpFront {
       app_id:     app_id.clone(),
       hostname:   front.hostname.clone(),
       path_begin: front.path_begin.clone(),
      }));
    }

    for &(app_id, front) in removed_https_fronts {
     v.push(Order::RemoveTlsFront(TlsFront {
       app_id:      app_id.clone(),
       hostname:    front.hostname.clone(),
       path_begin:  front.path_begin.clone(),
       fingerprint: front.fingerprint.clone(),
      }));
    }

    for &(_, instance) in added_instances {
      v.push(Order::AddInstance(instance.clone()));
    }

    for &(_, instance) in removed_instances {
      v.push(Order::RemoveInstance(instance.clone()));
    }

    for &(app_id, front) in added_http_fronts {
      v.push(Order::AddHttpFront(HttpFront {
        app_id:     app_id.clone(),
        hostname:   front.hostname.clone(),
        path_begin: front.path_begin.clone(),
      }));
    }

    for &(app_id, front) in added_https_fronts {
      v.push(Order::AddTlsFront(TlsFront {
        app_id:      app_id.clone(),
        hostname:    front.hostname.clone(),
        path_begin:  front.path_begin.clone(),
        fingerprint: front.fingerprint.clone(),
      }));
    }

    for  &(fingerprint, _) in removed_certificates {
      v.push(Order::RemoveCertificate(fingerprint.clone()));
    }
    v
  }
}

#[cfg(test)]
mod tests {
  use super::*;
  use sozu::messages::{Order,HttpFront,Instance};

  #[test]
  fn serialize() {
    let mut state:ConfigState = Default::default();
    state.handle_order(&Order::AddHttpFront(HttpFront { app_id: String::from("app_1"), hostname: String::from("lolcatho.st:8080"), path_begin: String::from("/") }));
    state.handle_order(&Order::AddHttpFront(HttpFront { app_id: String::from("app_2"), hostname: String::from("test.local"), path_begin: String::from("/abc") }));
    state.handle_order(&Order::AddInstance(Instance { app_id: String::from("app_1"), ip_address: String::from("127.0.0.1"), port: 1026 }));
    state.handle_order(&Order::AddInstance(Instance { app_id: String::from("app_1"), ip_address: String::from("127.0.0.2"), port: 1027 }));
    state.handle_order(&Order::AddInstance(Instance { app_id: String::from("app_2"), ip_address: String::from("192.167.1.2"), port: 1026 }));
    state.handle_order(&Order::AddInstance(Instance { app_id: String::from("app_1"), ip_address: String::from("192.168.1.3"), port: 1027 }));
    state.handle_order(&Order::RemoveInstance(Instance { app_id: String::from("app_1"), ip_address: String::from("192.168.1.3"), port: 1027 }));

    /*
    let encoded = state.encode();
    println!("serialized:\n{}", encoded);

    let new_state: Option<HttpProxy> = decode_str(&encoded);
    println!("deserialized:\n{:?}", new_state);
    assert_eq!(new_state, Some(state));
    */
    //assert!(false);
  }

  #[test]
  fn diff() {
    let mut state:ConfigState = Default::default();
    state.handle_order(&Order::AddHttpFront(HttpFront { app_id: String::from("app_1"), hostname: String::from("lolcatho.st:8080"), path_begin: String::from("/") }));
    state.handle_order(&Order::AddHttpFront(HttpFront { app_id: String::from("app_2"), hostname: String::from("test.local"), path_begin: String::from("/abc") }));
    state.handle_order(&Order::AddInstance(Instance { app_id: String::from("app_1"), ip_address: String::from("127.0.0.1"), port: 1026 }));
    state.handle_order(&Order::AddInstance(Instance { app_id: String::from("app_1"), ip_address: String::from("127.0.0.2"), port: 1027 }));
    state.handle_order(&Order::AddInstance(Instance { app_id: String::from("app_2"), ip_address: String::from("192.167.1.2"), port: 1026 }));

    let mut state2:ConfigState = Default::default();
    state2.handle_order(&Order::AddHttpFront(HttpFront { app_id: String::from("app_1"), hostname: String::from("lolcatho.st:8080"), path_begin: String::from("/") }));
    state2.handle_order(&Order::AddInstance(Instance { app_id: String::from("app_1"), ip_address: String::from("127.0.0.1"), port: 1026 }));
    state2.handle_order(&Order::AddInstance(Instance { app_id: String::from("app_1"), ip_address: String::from("127.0.0.2"), port: 1027 }));
    state2.handle_order(&Order::AddInstance(Instance { app_id: String::from("app_1"), ip_address: String::from("127.0.0.2"), port: 1028 }));

   let e = vec!(
     Order::RemoveHttpFront(HttpFront { app_id: String::from("app_2"), hostname: String::from("test.local"), path_begin: String::from("/abc") }),
     Order::RemoveInstance(Instance { app_id: String::from("app_2"), ip_address: String::from("192.167.1.2"), port: 1026 }),
     Order::AddInstance(Instance { app_id: String::from("app_1"), ip_address: String::from("127.0.0.2"), port: 1028 }),
   );
   let expected_diff:HashSet<&Order> = HashSet::from_iter(e.iter());

   let d = state.diff(&state2);
   let diff = HashSet::from_iter(d.iter());
   println!("diff orders:\n{:?}\n", diff);
   println!("expected diff orders:\n{:?}\n", expected_diff);

   assert_eq!(diff, expected_diff);
  }
}
<|endoftext|>