<commit_before>use std::ops;

use crate::document::Document;
use crate::key::Key;
use crate::table::TableKeyValue;
use crate::{value, InlineTable, InternalString, Item, Table, Value};

// copied from
// https://github.com/serde-rs/json/blob/master/src/value/index.rs

pub trait Index: crate::private::Sealed {
    #[doc(hidden)]
    fn index<'v>(&self, val: &'v Item) -> Option<&'v Item>;
    #[doc(hidden)]
    fn index_mut<'v>(&self, val: &'v mut Item) -> Option<&'v mut Item>;
}

impl Index for usize {
    fn index<'v>(&self, v: &'v Item) -> Option<&'v Item> {
        match *v {
            Item::ArrayOfTables(ref aot) => aot.values.get(*self),
            Item::Value(ref a) if a.is_array() => a.as_array().and_then(|a| a.values.get(*self)),
            _ => None,
        }
    }
    fn index_mut<'v>(&self, v: &'v mut Item) -> Option<&'v mut Item> {
        match *v {
            Item::ArrayOfTables(ref mut vec) => vec.values.get_mut(*self),
            Item::Value(ref mut a) if a.is_array() => {
                a.as_array_mut().and_then(|a| a.values.get_mut(*self))
            }
            _ => None,
        }
    }
}

impl Index for str {
    fn index<'v>(&self, v: &'v Item) -> Option<&'v Item> {
        match *v {
            Item::Table(ref t) => t.get(self),
            Item::Value(ref v) if v.is_inline_table() => v
                .as_inline_table()
                .and_then(|t| t.items.get(self).map(|kv| &kv.value)),
            _ => None,
        }
    }
    fn index_mut<'v>(&self, v: &'v mut Item) -> Option<&'v mut Item> {
        if let Item::None = *v {
            let mut t = InlineTable::default();
            t.items.insert(
                InternalString::from(self),
                TableKeyValue::new(Key::new(self), Item::None),
            );
            *v = value(Value::InlineTable(t));
        }
        match *v {
            Item::Table(ref mut t) => Some(t.entry(self).or_insert(Item::None)),
            Item::Value(ref mut v) if v.is_inline_table() => v.as_inline_table_mut().map(|t| {
                &mut t
                    .items
                    .entry(InternalString::from(self))
                    .or_insert_with(|| TableKeyValue::new(Key::new(self), Item::None))
                    .value
            }),
            _ => None,
        }
    }
}

impl Index for String {
    fn index<'v>(&self, v: &'v Item) -> Option<&'v Item> {
        self[..].index(v)
    }
    fn index_mut<'v>(&self, v: &'v mut Item) -> Option<&'v mut Item> {
        self[..].index_mut(v)
    }
}

impl<'a, T: ?Sized> Index for &'a T
where
    T: Index,
{
    fn index<'v>(&self, v: &'v Item) -> Option<&'v Item> {
        (**self).index(v)
    }
    fn index_mut<'v>(&self, v: &'v mut Item) -> Option<&'v mut Item> {
        (**self).index_mut(v)
    }
}

impl<I> ops::Index<I> for Item
where
    I: Index,
{
    type Output = Item;

    fn index(&self, index: I) -> &Item {
        index.index(self).expect("index not found")
    }
}

impl<I> ops::IndexMut<I> for Item
where
    I: Index,
{
    fn index_mut(&mut self, index: I) -> &mut Item {
        index.index_mut(self).expect("index not found")
    }
}

impl<'s> ops::Index<&'s str> for Table {
    type Output = Item;

    fn index(&self, key: &'s str) -> &Item {
        self.get(key).expect("index not found")
    }
}

impl<'s> ops::IndexMut<&'s str> for Table {
    fn index_mut(&mut self, key: &'s str) -> &mut Item {
        self.entry(key).or_insert(Item::None)
    }
}

impl<'s> ops::Index<&'s str> for Document {
    type Output = Item;

    fn index(&self, key: &'s str) -> &Item {
        self.root.index(key)
    }
}

impl<'s> ops::IndexMut<&'s str> for Document {
    fn index_mut(&mut self, key: &'s str) -> &mut Item {
        self.root.index_mut(key)
    }
}
<commit_msg>feat: Allow indexing InlineTable (#247)<commit_after>use std::ops;

use crate::document::Document;
use crate::key::Key;
use crate::table::TableKeyValue;
use crate::{value, InlineTable, InternalString, Item, Table, Value};

// copied from
// https://github.com/serde-rs/json/blob/master/src/value/index.rs

pub trait Index: crate::private::Sealed {
    #[doc(hidden)]
    fn index<'v>(&self, val: &'v Item) -> Option<&'v Item>;
    #[doc(hidden)]
    fn index_mut<'v>(&self, val: &'v mut Item) -> Option<&'v mut Item>;
}

impl Index for usize {
    fn index<'v>(&self, v: &'v Item) -> Option<&'v Item> {
        match *v {
            Item::ArrayOfTables(ref aot) => aot.values.get(*self),
            Item::Value(ref a) if a.is_array() => a.as_array().and_then(|a| a.values.get(*self)),
            _ => None,
        }
    }
    fn index_mut<'v>(&self, v: &'v mut Item) -> Option<&'v mut Item> {
        match *v {
            Item::ArrayOfTables(ref mut vec) => vec.values.get_mut(*self),
            Item::Value(ref mut a) if a.is_array() => {
                a.as_array_mut().and_then(|a| a.values.get_mut(*self))
            }
            _ => None,
        }
    }
}

impl Index for str {
    fn index<'v>(&self, v: &'v Item) -> Option<&'v Item> {
        match *v {
            Item::Table(ref t) => t.get(self),
            Item::Value(ref v) if v.is_inline_table() => v
                .as_inline_table()
                .and_then(|t| t.items.get(self).map(|kv| &kv.value)),
            _ => None,
        }
    }
    fn index_mut<'v>(&self, v: &'v mut Item) -> Option<&'v mut Item> {
        if let Item::None = *v {
            let mut t = InlineTable::default();
            t.items.insert(
                InternalString::from(self),
                TableKeyValue::new(Key::new(self), Item::None),
            );
            *v = value(Value::InlineTable(t));
        }
        match *v {
            Item::Table(ref mut t) => Some(t.entry(self).or_insert(Item::None)),
            Item::Value(ref mut v) if v.is_inline_table() => v.as_inline_table_mut().map(|t| {
                &mut t
                    .items
                    .entry(InternalString::from(self))
                    .or_insert_with(|| TableKeyValue::new(Key::new(self), Item::None))
                    .value
            }),
            _ => None,
        }
    }
}

impl Index for String {
    fn index<'v>(&self, v: &'v Item) -> Option<&'v Item> {
        self[..].index(v)
    }
    fn index_mut<'v>(&self, v: &'v mut Item) -> Option<&'v mut Item> {
        self[..].index_mut(v)
    }
}

impl<'a, T: ?Sized> Index for &'a T
where
    T: Index,
{
    fn index<'v>(&self, v: &'v Item) -> Option<&'v Item> {
        (**self).index(v)
    }
    fn index_mut<'v>(&self, v: &'v mut Item) -> Option<&'v mut Item> {
        (**self).index_mut(v)
    }
}

impl<I> ops::Index<I> for Item
where
    I: Index,
{
    type Output = Item;

    fn index(&self, index: I) -> &Item {
        index.index(self).expect("index not found")
    }
}

impl<I> ops::IndexMut<I> for Item
where
    I: Index,
{
    fn index_mut(&mut self, index: I) -> &mut Item {
        index.index_mut(self).expect("index not found")
    }
}

impl<'s> ops::Index<&'s str> for Table {
    type Output = Item;

    fn index(&self, key: &'s str) -> &Item {
        self.get(key).expect("index not found")
    }
}

impl<'s> ops::IndexMut<&'s str> for Table {
    fn index_mut(&mut self, key: &'s str) -> &mut Item {
        self.entry(key).or_insert(Item::None)
    }
}

impl<'s> ops::Index<&'s str> for InlineTable {
    type Output = Value;

    fn index(&self, key: &'s str) -> &Value {
        self.get(key).expect("index not found")
    }
}

impl<'s> ops::IndexMut<&'s str> for InlineTable {
    fn index_mut(&mut self, key: &'s str) -> &mut Value {
        self.get_mut(key).expect("index not found")
    }
}

impl<'s> ops::Index<&'s str> for Document {
    type Output = Item;

    fn index(&self, key: &'s str) -> &Item {
        self.root.index(key)
    }
}

impl<'s> ops::IndexMut<&'s str> for Document {
    fn index_mut(&mut self, key: &'s str) -> &mut Item {
        self.root.index_mut(key)
    }
}
<|endoftext|>


<commit_before>//! Glyph caching

use std::collections::hash_map::{ HashMap, Entry };
use { freetype, graphics };
use gfx_lib as gfx;

/// The type alias for the font size.
pub type FontSize = u32;

/// The type alias for font characters.
pub type Character<D> = graphics::character::Character<::Texture<D>>;

/// An enum to represent various possible run-time errors that may occur.
#[derive(Copy, Debug, PartialEq)]
pub enum Error {
    /// An error happened when creating a gfx texture.
    Texture(gfx::tex::TextureError),
    /// An error happened with the FreeType library.
    Freetype(freetype::error::Error),
}

/// A struct used for caching rendered font.
pub struct GlyphCache<R: gfx::Resources> {
    /// The font face.
    pub face: freetype::Face,
    empty_texture: ::Texture<R>,
    data: HashMap<(FontSize, char), Character<R>>,
}

impl<R> GlyphCache<R> where R: gfx::Resources {
     /// Constructor for a GlyphCache.
     pub fn new<D: gfx::Factory<R>>(font: &Path, device: &mut D)
                -> Result<Self, Error> {
        let freetype = match freetype::Library::init() {
            Ok(freetype) => freetype,
            Err(why) => return Err(Error::Freetype(why)),
        };
        let face = match freetype.new_face(font, 0) {
            Ok(face) => face,
            Err(why) => return Err(Error::Freetype(why)),
        };
        let texture = {
            let tinfo = gfx::tex::TextureInfo {
                width: 1,
                height: 1,
                depth: 1,
                levels: 1,
                format: gfx::tex::RGBA8,
                kind: gfx::tex::TextureKind::Texture2D,
            };
            let image_info = tinfo.to_image_info();
            match device.create_texture(tinfo) {
                Ok(t) => match device.update_texture(&t, &image_info, &[0u8; 4]) {
                    Ok(()) => t,
                    Err(e) => return Err(Error::Texture(e)),
                },
                Err(e) => return Err(Error::Texture(e)),
            }
        };
        Ok(GlyphCache {
            face: face,
            empty_texture: ::Texture {
                handle: texture
            },
            data: HashMap::new(),
        })
    }

    /// Generate all pending characters.
    pub fn update<D: gfx::Factory<R>>(&mut self, device: &mut D) {
        let empty_handle = self.empty_texture.handle.clone();
        for (&(size, ch), value) in self.data.iter_mut()
                .filter(|&(_, ref c)| c.texture.handle == empty_handle) {
            self.face.set_pixel_sizes(0, size).unwrap();
            self.face.load_char(ch as usize, freetype::face::DEFAULT).unwrap();
            let glyph = self.face.glyph().get_glyph().unwrap();
            let bitmap_glyph = glyph.to_bitmap(
                freetype::render_mode::RenderMode::Normal, None)
                .unwrap();
            let bitmap = bitmap_glyph.bitmap();
            let texture = ::Texture::from_memory_alpha(
                device,
                bitmap.buffer(),
                bitmap.width() as u32,
                bitmap.rows() as u32);
            let glyph_size = glyph.advance();
            *value = Character {
                offset: [
                    bitmap_glyph.left() as f64,
                    bitmap_glyph.top() as f64
                ],
                size: [
                    (glyph_size.x >> 16) as f64,
                    (glyph_size.y >> 16) as f64
                ],
                texture: texture,
            };
        }
    }
}

impl<R: gfx::Resources> graphics::character::CharacterCache for GlyphCache<R> {
    type Texture = ::Texture<R>;

    fn character(&mut self, size: FontSize, ch: char) -> &Character<R> {
        match self.data.entry((size, ch)) {
            //returning `into_mut()' to work around lifetime issues
            Entry::Occupied(v) => v.into_mut(),
            Entry::Vacant(v) => {
                v.insert(graphics::character::Character {
                    offset: [0.0; 2],
                    size: [1.0; 2],
                    texture: ::Texture {
                        handle: self.empty_texture.handle.clone()
                    },
                })
            },
        }
    }
}
<commit_msg>Fix old_path import in glyph<commit_after>//! Glyph caching

use std::old_path::*;

use std::collections::hash_map::{ HashMap, Entry };
use { freetype, graphics };
use gfx_lib as gfx;

/// The type alias for the font size.
pub type FontSize = u32;

/// The type alias for font characters.
pub type Character<D> = graphics::character::Character<::Texture<D>>;

/// An enum to represent various possible run-time errors that may occur.
#[derive(Copy, Debug, PartialEq)]
pub enum Error {
    /// An error happened when creating a gfx texture.
    Texture(gfx::tex::TextureError),
    /// An error happened with the FreeType library.
    Freetype(freetype::error::Error),
}

/// A struct used for caching rendered font.
pub struct GlyphCache<R: gfx::Resources> {
    /// The font face.
    pub face: freetype::Face,
    empty_texture: ::Texture<R>,
    data: HashMap<(FontSize, char), Character<R>>,
}

impl<R> GlyphCache<R> where R: gfx::Resources {
     /// Constructor for a GlyphCache.
     pub fn new<D: gfx::Factory<R>>(font: &Path, device: &mut D)
                -> Result<Self, Error> {
        let freetype = match freetype::Library::init() {
            Ok(freetype) => freetype,
            Err(why) => return Err(Error::Freetype(why)),
        };
        let face = match freetype.new_face(font, 0) {
            Ok(face) => face,
            Err(why) => return Err(Error::Freetype(why)),
        };
        let texture = {
            let tinfo = gfx::tex::TextureInfo {
                width: 1,
                height: 1,
                depth: 1,
                levels: 1,
                format: gfx::tex::RGBA8,
                kind: gfx::tex::TextureKind::Texture2D,
            };
            let image_info = tinfo.to_image_info();
            match device.create_texture(tinfo) {
                Ok(t) => match device.update_texture(&t, &image_info, &[0u8; 4]) {
                    Ok(()) => t,
                    Err(e) => return Err(Error::Texture(e)),
                },
                Err(e) => return Err(Error::Texture(e)),
            }
        };
        Ok(GlyphCache {
            face: face,
            empty_texture: ::Texture {
                handle: texture
            },
            data: HashMap::new(),
        })
    }

    /// Generate all pending characters.
    pub fn update<D: gfx::Factory<R>>(&mut self, device: &mut D) {
        let empty_handle = self.empty_texture.handle.clone();
        for (&(size, ch), value) in self.data.iter_mut()
                .filter(|&(_, ref c)| c.texture.handle == empty_handle) {
            self.face.set_pixel_sizes(0, size).unwrap();
            self.face.load_char(ch as usize, freetype::face::DEFAULT).unwrap();
            let glyph = self.face.glyph().get_glyph().unwrap();
            let bitmap_glyph = glyph.to_bitmap(
                freetype::render_mode::RenderMode::Normal, None)
                .unwrap();
            let bitmap = bitmap_glyph.bitmap();
            let texture = ::Texture::from_memory_alpha(
                device,
                bitmap.buffer(),
                bitmap.width() as u32,
                bitmap.rows() as u32);
            let glyph_size = glyph.advance();
            *value = Character {
                offset: [
                    bitmap_glyph.left() as f64,
                    bitmap_glyph.top() as f64
                ],
                size: [
                    (glyph_size.x >> 16) as f64,
                    (glyph_size.y >> 16) as f64
                ],
                texture: texture,
            };
        }
    }
}

impl<R: gfx::Resources> graphics::character::CharacterCache for GlyphCache<R> {
    type Texture = ::Texture<R>;

    fn character(&mut self, size: FontSize, ch: char) -> &Character<R> {
        match self.data.entry((size, ch)) {
            //returning `into_mut()' to work around lifetime issues
            Entry::Occupied(v) => v.into_mut(),
            Entry::Vacant(v) => {
                v.insert(graphics::character::Character {
                    offset: [0.0; 2],
                    size: [1.0; 2],
                    texture: ::Texture {
                        handle: self.empty_texture.handle.clone()
                    },
                })
            },
        }
    }
}
<|endoftext|>


<commit_before>extern crate quick_xml;

use quick_xml::reader::Reader;
use quick_xml::events::Event::*;
use quick_xml::events::attributes::Attribute;
use std::io::Cursor;
use std::borrow::Cow;

#[test]
fn test_sample() {
    let src: &[u8] = include_bytes!("sample_rss.xml");
    let mut buf = Vec::new();
    let mut r = Reader::from_reader(src);
    let mut count = 0;
    loop {
        match r.read_event(&mut buf).unwrap() {
            Start(_) => count += 1,
            Decl(e) => println!("{:?}", e.version()),
            Eof => break,
            _ => (),
        }
        buf.clear();
    }
    println!("{}", count);
}

#[test]
fn test_attributes_empty() {
    let src = b"<a att1='a' att2='b'/>";
    let mut r = Reader::from_reader(src as &[u8]);
    r.trim_text(true).expand_empty_elements(false);
    let mut buf = Vec::new();
    match r.read_event(&mut buf) {
        Ok(Empty(e)) => {
            let mut atts = e.attributes();
            match atts.next() {
                Some(
                    Ok(Attribute {
                        key: b"att1",
                        value: Cow::Borrowed(b"a"),
                    }),
                ) => (),
                e => panic!("Expecting att1='a' attribute, found {:?}", e),
            }
            match atts.next() {
                Some(
                    Ok(Attribute {
                        key: b"att2",
                        value: Cow::Borrowed(b"b"),
                    }),
                ) => (),
                e => panic!("Expecting att2='b' attribute, found {:?}", e),
            }
            match atts.next() {
                None => (),
                e => panic!("Expecting None, found {:?}", e),
            }
        }
        e => panic!("Expecting Empty event, got {:?}", e),
    }
}

#[test]
fn test_attribute_equal() {
    let src = b"<a att1=\"a=b\"/>";
    let mut r = Reader::from_reader(src as &[u8]);
    r.trim_text(true).expand_empty_elements(false);
    let mut buf = Vec::new();
    match r.read_event(&mut buf) {
        Ok(Empty(e)) => {
            let mut atts = e.attributes();
            match atts.next() {
                Some(
                    Ok(Attribute {
                        key: b"att1",
                        value: Cow::Borrowed(b"a=b"),
                    }),
                ) => (),
                e => panic!("Expecting att1=\"a=b\" attribute, found {:?}", e),
            }
            match atts.next() {
                None => (),
                e => panic!("Expecting None, found {:?}", e),
            }
        }
        e => panic!("Expecting Empty event, got {:?}", e),
    }
}

/// Single empty element with qualified attributes.
/// Empty element expansion: disabled
/// The code path for namespace handling is slightly different for `Empty` vs. `Start+End`.
#[test]
fn test_attributes_empty_ns() {
    let src = b"<a att1='a' r:att2='b' xmlns:r='urn:example:r' />";

    let mut r = Reader::from_reader(src as &[u8]);
    r.trim_text(true).expand_empty_elements(false);
    let mut buf = Vec::new();
    let mut ns_buf = Vec::new();

    let e = match r.read_namespaced_event(&mut buf, &mut ns_buf) {
        Ok((None, Empty(e))) => e,
        e => panic!("Expecting Empty event, got {:?}", e),
    };

    let mut atts = e.attributes()
            .map(|ar| ar.expect("Expecting attribute parsing to succeed."))
            // we don't care about xmlns attributes for this test
            .filter(|kv| !kv.key.starts_with(b"xmlns"))
            .map(|Attribute { key: name, value }| {
                let (opt_ns, local_name) = r.resolve_namespace(name, &ns_buf);
                (opt_ns, local_name, value)
            });
    match atts.next() {
        Some((None, b"att1", Cow::Borrowed(b"a"))) => (),
        e => panic!("Expecting att1='a' attribute, found {:?}", e),
    }
    match atts.next() {
        Some((Some(ns), b"att2", Cow::Borrowed(b"b"))) => {
            assert_eq!(&ns[..], b"urn:example:r");
        }
        e => panic!(
            "Expecting {{urn:example:r}}att2='b' attribute, found {:?}",
            e
        ),
    }
    match atts.next() {
        None => (),
        e => panic!("Expecting None, found {:?}", e),
    }
}

/// Single empty element with qualified attributes.
/// Empty element expansion: enabled
/// The code path for namespace handling is slightly different for `Empty` vs. `Start+End`.
#[test]
fn test_attributes_empty_ns_expanded() {
    let src = b"<a att1='a' r:att2='b' xmlns:r='urn:example:r' />";

    let mut r = Reader::from_reader(src as &[u8]);
    r.trim_text(true).expand_empty_elements(true);
    let mut buf = Vec::new();
    let mut ns_buf = Vec::new();
    {
        let e = match r.read_namespaced_event(&mut buf, &mut ns_buf) {
            Ok((None, Start(e))) => e,
            e => panic!("Expecting Empty event, got {:?}", e),
        };

        let mut atts = e.attributes()
            .map(|ar| ar.expect("Expecting attribute parsing to succeed."))
            // we don't care about xmlns attributes for this test
            .filter(|kv| !kv.key.starts_with(b"xmlns"))
            .map(|Attribute { key: name, value }| {
                let (opt_ns, local_name) = r.resolve_namespace(name, &ns_buf);
                (opt_ns, local_name, value)
            });
        match atts.next() {
            Some((None, b"att1", Cow::Borrowed(b"a"))) => (),
            e => panic!("Expecting att1='a' attribute, found {:?}", e),
        }
        match atts.next() {
            Some((Some(ns), b"att2", Cow::Borrowed(b"b"))) => {
                assert_eq!(&ns[..], b"urn:example:r");
            }
            e => panic!(
                "Expecting {{urn:example:r}}att2='b' attribute, found {:?}",
                e
            ),
        }
        match atts.next() {
            None => (),
            e => panic!("Expecting None, found {:?}", e),
        }
    }

    match r.read_namespaced_event(&mut buf, &mut ns_buf) {
        Ok((None, End(e))) => assert_eq!(b"a", e.name()),
        e => panic!("Expecting End event, got {:?}", e),
    }
}

#[test]
fn test_default_ns_shadowing_empty() {
    let src = b"<e xmlns='urn:example:o'><e att1='a' xmlns='urn:example:i' /></e>";

    let mut r = Reader::from_reader(src as &[u8]);
    r.trim_text(true).expand_empty_elements(false);
    let mut buf = Vec::new();
    let mut ns_buf = Vec::new();

    // <outer xmlns='urn:example:o'>
    {
        match r.read_namespaced_event(&mut buf, &mut ns_buf) {
            Ok((Some(ns), Start(e))) => {
                assert_eq!(&ns[..], b"urn:example:o");
                assert_eq!(e.name(), b"e");
            }
            e => panic!("Expected Start event (<outer>), got {:?}", e),
        }
    }

    // <inner att1='a' xmlns='urn:example:i' />
    {
        let e = match r.read_namespaced_event(&mut buf, &mut ns_buf) {
            Ok((Some(ns), Empty(e))) => {
                assert_eq!(::std::str::from_utf8(ns).unwrap(), "urn:example:i");
                assert_eq!(e.name(), b"e");
                e
            }
            e => panic!("Expecting Empty event, got {:?}", e),
        };

        let mut atts = e.attributes()
            .map(|ar| ar.expect("Expecting attribute parsing to succeed."))
            // we don't care about xmlns attributes for this test
            .filter(|kv| !kv.key.starts_with(b"xmlns"))
            .map(|Attribute { key: name, value }| {
                let (opt_ns, local_name) = r.resolve_namespace(name, &ns_buf);
                (opt_ns, local_name, value)
            });
        // the attribute should _not_ have a namespace name. The default namespace does not
        // apply to attributes.
        match atts.next() {
            Some((None, b"att1", Cow::Borrowed(b"a"))) => (),
            e => panic!("Expecting att1='a' attribute, found {:?}", e),
        }
        match atts.next() {
            None => (),
            e => panic!("Expecting None, found {:?}", e),
        }
    }

    // </outer>
    match r.read_namespaced_event(&mut buf, &mut ns_buf) {
        Ok((Some(ns), End(e))) => {
            assert_eq!(&ns[..], b"urn:example:o");
            assert_eq!(e.name(), b"e");
        }
        e => panic!("Expected End event (<outer>), got {:?}", e),
    }
}

#[test]
fn test_default_ns_shadowing_expanded() {
    let src = b"<e xmlns='urn:example:o'><e att1='a' xmlns='urn:example:i' /></e>";

    let mut r = Reader::from_reader(src as &[u8]);
    r.trim_text(true).expand_empty_elements(true);
    let mut buf = Vec::new();
    let mut ns_buf = Vec::new();

    // <outer xmlns='urn:example:o'>
    {
        match r.read_namespaced_event(&mut buf, &mut ns_buf) {
            Ok((Some(ns), Start(e))) => {
                assert_eq!(&ns[..], b"urn:example:o");
                assert_eq!(e.name(), b"e");
            }
            e => panic!("Expected Start event (<outer>), got {:?}", e),
        }
    }
    buf.clear();

    // <inner att1='a' xmlns='urn:example:i' />
    {
        let e = match r.read_namespaced_event(&mut buf, &mut ns_buf) {
            Ok((Some(ns), Start(e))) => {
                assert_eq!(&ns[..], b"urn:example:i");
                assert_eq!(e.name(), b"e");
                e
            }
            e => panic!("Expecting Start event (<inner>), got {:?}", e),
        };
        let mut atts = e.attributes()
            .map(|ar| ar.expect("Expecting attribute parsing to succeed."))
            // we don't care about xmlns attributes for this test
            .filter(|kv| !kv.key.starts_with(b"xmlns"))
            .map(|Attribute { key: name, value }| {
                let (opt_ns, local_name) = r.resolve_namespace(name, &ns_buf);
                (opt_ns, local_name, value)
            });
        // the attribute should _not_ have a namespace name. The default namespace does not
        // apply to attributes.
        match atts.next() {
            Some((None, b"att1", Cow::Borrowed(b"a"))) => (),
            e => panic!("Expecting att1='a' attribute, found {:?}", e),
        }
        match atts.next() {
            None => (),
            e => panic!("Expecting None, found {:?}", e),
        }
    }

    // virtual </inner>
    match r.read_namespaced_event(&mut buf, &mut ns_buf) {
        Ok((Some(ns), End(e))) => {
            assert_eq!(&ns[..], b"urn:example:i");
            assert_eq!(e.name(), b"e");
        }
        e => panic!("Expected End event (</inner>), got {:?}", e),
    }
    // </outer>
    match r.read_namespaced_event(&mut buf, &mut ns_buf) {
        Ok((Some(ns), End(e))) => {
            assert_eq!(&ns[..], b"urn:example:o");
            assert_eq!(e.name(), b"e");
        }
        e => panic!("Expected End event (</outer>), got {:?}", e),
    }
}

#[test]
fn test_koi8_r_encoding() {
    let src: &[u8] = include_bytes!("documents/opennews_all.rss");
    let mut r = Reader::from_reader(src as &[u8]);
    r.trim_text(true).expand_empty_elements(false);
    let mut buf = Vec::new();
    loop {
        match r.read_event(&mut buf) {
            Ok(Text(e)) => {
                e.unescape_and_decode(&r).unwrap();
            }
            Ok(Eof) => break,
            _ => (),
        }
    }
}

#[test]
fn fuzz_53() {
    let data: &[u8] = b"\xe9\x00\x00\x00\x00\x00\x00\x00\x00\
\x00\x00\x00\x00\n(\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\
\x00<>\x00\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00<<\x00\x00\x00";
    let cursor = Cursor::new(data);
    let mut reader = Reader::from_reader(cursor);
    let mut buf = vec![];
    loop {
        match reader.read_event(&mut buf) {
            Ok(quick_xml::events::Event::Eof) | Err(..) => break,
            _ => buf.clear(),
        }
    }
}

#[test]
fn test_issue94() {
    let data = br#"<Run>
<!B>
</Run>"#;
    let mut reader = Reader::from_reader(&data[..]);
    reader.trim_text(true);
    let mut buf = vec![];
    loop {
        match reader.read_event(&mut buf) {
            Ok(quick_xml::events::Event::Eof) | Err(..) => break,
            _ => buf.clear(),
        }
        buf.clear();
    }
}
<commit_msg>add failing test<commit_after>extern crate quick_xml;

use quick_xml::reader::Reader;
use quick_xml::events::Event::*;
use quick_xml::events::attributes::Attribute;
use std::io::Cursor;
use std::borrow::Cow;

#[test]
fn test_sample() {
    let src: &[u8] = include_bytes!("sample_rss.xml");
    let mut buf = Vec::new();
    let mut r = Reader::from_reader(src);
    let mut count = 0;
    loop {
        match r.read_event(&mut buf).unwrap() {
            Start(_) => count += 1,
            Decl(e) => println!("{:?}", e.version()),
            Eof => break,
            _ => (),
        }
        buf.clear();
    }
    println!("{}", count);
}

#[test]
fn test_attributes_empty() {
    let src = b"<a att1='a' att2='b'/>";
    let mut r = Reader::from_reader(src as &[u8]);
    r.trim_text(true).expand_empty_elements(false);
    let mut buf = Vec::new();
    match r.read_event(&mut buf) {
        Ok(Empty(e)) => {
            let mut atts = e.attributes();
            match atts.next() {
                Some(
                    Ok(Attribute {
                        key: b"att1",
                        value: Cow::Borrowed(b"a"),
                    }),
                ) => (),
                e => panic!("Expecting att1='a' attribute, found {:?}", e),
            }
            match atts.next() {
                Some(
                    Ok(Attribute {
                        key: b"att2",
                        value: Cow::Borrowed(b"b"),
                    }),
                ) => (),
                e => panic!("Expecting att2='b' attribute, found {:?}", e),
            }
            match atts.next() {
                None => (),
                e => panic!("Expecting None, found {:?}", e),
            }
        }
        e => panic!("Expecting Empty event, got {:?}", e),
    }
}

#[test]
fn test_attribute_equal() {
    let src = b"<a att1=\"a=b\"/>";
    let mut r = Reader::from_reader(src as &[u8]);
    r.trim_text(true).expand_empty_elements(false);
    let mut buf = Vec::new();
    match r.read_event(&mut buf) {
        Ok(Empty(e)) => {
            let mut atts = e.attributes();
            match atts.next() {
                Some(
                    Ok(Attribute {
                        key: b"att1",
                        value: Cow::Borrowed(b"a=b"),
                    }),
                ) => (),
                e => panic!("Expecting att1=\"a=b\" attribute, found {:?}", e),
            }
            match atts.next() {
                None => (),
                e => panic!("Expecting None, found {:?}", e),
            }
        }
        e => panic!("Expecting Empty event, got {:?}", e),
    }
}

/// Single empty element with qualified attributes.
/// Empty element expansion: disabled
/// The code path for namespace handling is slightly different for `Empty` vs. `Start+End`.
#[test]
fn test_attributes_empty_ns() {
    let src = b"<a att1='a' r:att2='b' xmlns:r='urn:example:r' />";

    let mut r = Reader::from_reader(src as &[u8]);
    r.trim_text(true).expand_empty_elements(false);
    let mut buf = Vec::new();
    let mut ns_buf = Vec::new();

    let e = match r.read_namespaced_event(&mut buf, &mut ns_buf) {
        Ok((None, Empty(e))) => e,
        e => panic!("Expecting Empty event, got {:?}", e),
    };

    let mut atts = e.attributes()
            .map(|ar| ar.expect("Expecting attribute parsing to succeed."))
            // we don't care about xmlns attributes for this test
            .filter(|kv| !kv.key.starts_with(b"xmlns"))
            .map(|Attribute { key: name, value }| {
                let (opt_ns, local_name) = r.resolve_namespace(name, &ns_buf);
                (opt_ns, local_name, value)
            });
    match atts.next() {
        Some((None, b"att1", Cow::Borrowed(b"a"))) => (),
        e => panic!("Expecting att1='a' attribute, found {:?}", e),
    }
    match atts.next() {
        Some((Some(ns), b"att2", Cow::Borrowed(b"b"))) => {
            assert_eq!(&ns[..], b"urn:example:r");
        }
        e => panic!(
            "Expecting {{urn:example:r}}att2='b' attribute, found {:?}",
            e
        ),
    }
    match atts.next() {
        None => (),
        e => panic!("Expecting None, found {:?}", e),
    }
}

/// Single empty element with qualified attributes.
/// Empty element expansion: enabled
/// The code path for namespace handling is slightly different for `Empty` vs. `Start+End`.
#[test]
fn test_attributes_empty_ns_expanded() {
    let src = b"<a att1='a' r:att2='b' xmlns:r='urn:example:r' />";

    let mut r = Reader::from_reader(src as &[u8]);
    r.trim_text(true).expand_empty_elements(true);
    let mut buf = Vec::new();
    let mut ns_buf = Vec::new();
    {
        let e = match r.read_namespaced_event(&mut buf, &mut ns_buf) {
            Ok((None, Start(e))) => e,
            e => panic!("Expecting Empty event, got {:?}", e),
        };

        let mut atts = e.attributes()
            .map(|ar| ar.expect("Expecting attribute parsing to succeed."))
            // we don't care about xmlns attributes for this test
            .filter(|kv| !kv.key.starts_with(b"xmlns"))
            .map(|Attribute { key: name, value }| {
                let (opt_ns, local_name) = r.resolve_namespace(name, &ns_buf);
                (opt_ns, local_name, value)
            });
        match atts.next() {
            Some((None, b"att1", Cow::Borrowed(b"a"))) => (),
            e => panic!("Expecting att1='a' attribute, found {:?}", e),
        }
        match atts.next() {
            Some((Some(ns), b"att2", Cow::Borrowed(b"b"))) => {
                assert_eq!(&ns[..], b"urn:example:r");
            }
            e => panic!(
                "Expecting {{urn:example:r}}att2='b' attribute, found {:?}",
                e
            ),
        }
        match atts.next() {
            None => (),
            e => panic!("Expecting None, found {:?}", e),
        }
    }

    match r.read_namespaced_event(&mut buf, &mut ns_buf) {
        Ok((None, End(e))) => assert_eq!(b"a", e.name()),
        e => panic!("Expecting End event, got {:?}", e),
    }
}

#[test]
fn test_default_ns_shadowing_empty() {
    let src = b"<e xmlns='urn:example:o'><e att1='a' xmlns='urn:example:i' /></e>";

    let mut r = Reader::from_reader(src as &[u8]);
    r.trim_text(true).expand_empty_elements(false);
    let mut buf = Vec::new();
    let mut ns_buf = Vec::new();

    // <outer xmlns='urn:example:o'>
    {
        match r.read_namespaced_event(&mut buf, &mut ns_buf) {
            Ok((Some(ns), Start(e))) => {
                assert_eq!(&ns[..], b"urn:example:o");
                assert_eq!(e.name(), b"e");
            }
            e => panic!("Expected Start event (<outer>), got {:?}", e),
        }
    }

    // <inner att1='a' xmlns='urn:example:i' />
    {
        let e = match r.read_namespaced_event(&mut buf, &mut ns_buf) {
            Ok((Some(ns), Empty(e))) => {
                assert_eq!(::std::str::from_utf8(ns).unwrap(), "urn:example:i");
                assert_eq!(e.name(), b"e");
                e
            }
            e => panic!("Expecting Empty event, got {:?}", e),
        };

        let mut atts = e.attributes()
            .map(|ar| ar.expect("Expecting attribute parsing to succeed."))
            // we don't care about xmlns attributes for this test
            .filter(|kv| !kv.key.starts_with(b"xmlns"))
            .map(|Attribute { key: name, value }| {
                let (opt_ns, local_name) = r.resolve_namespace(name, &ns_buf);
                (opt_ns, local_name, value)
            });
        // the attribute should _not_ have a namespace name. The default namespace does not
        // apply to attributes.
        match atts.next() {
            Some((None, b"att1", Cow::Borrowed(b"a"))) => (),
            e => panic!("Expecting att1='a' attribute, found {:?}", e),
        }
        match atts.next() {
            None => (),
            e => panic!("Expecting None, found {:?}", e),
        }
    }

    // </outer>
    match r.read_namespaced_event(&mut buf, &mut ns_buf) {
        Ok((Some(ns), End(e))) => {
            assert_eq!(&ns[..], b"urn:example:o");
            assert_eq!(e.name(), b"e");
        }
        e => panic!("Expected End event (<outer>), got {:?}", e),
    }
}

#[test]
fn test_default_ns_shadowing_expanded() {
    let src = b"<e xmlns='urn:example:o'><e att1='a' xmlns='urn:example:i' /></e>";

    let mut r = Reader::from_reader(src as &[u8]);
    r.trim_text(true).expand_empty_elements(true);
    let mut buf = Vec::new();
    let mut ns_buf = Vec::new();

    // <outer xmlns='urn:example:o'>
    {
        match r.read_namespaced_event(&mut buf, &mut ns_buf) {
            Ok((Some(ns), Start(e))) => {
                assert_eq!(&ns[..], b"urn:example:o");
                assert_eq!(e.name(), b"e");
            }
            e => panic!("Expected Start event (<outer>), got {:?}", e),
        }
    }
    buf.clear();

    // <inner att1='a' xmlns='urn:example:i' />
    {
        let e = match r.read_namespaced_event(&mut buf, &mut ns_buf) {
            Ok((Some(ns), Start(e))) => {
                assert_eq!(&ns[..], b"urn:example:i");
                assert_eq!(e.name(), b"e");
                e
            }
            e => panic!("Expecting Start event (<inner>), got {:?}", e),
        };
        let mut atts = e.attributes()
            .map(|ar| ar.expect("Expecting attribute parsing to succeed."))
            // we don't care about xmlns attributes for this test
            .filter(|kv| !kv.key.starts_with(b"xmlns"))
            .map(|Attribute { key: name, value }| {
                let (opt_ns, local_name) = r.resolve_namespace(name, &ns_buf);
                (opt_ns, local_name, value)
            });
        // the attribute should _not_ have a namespace name. The default namespace does not
        // apply to attributes.
        match atts.next() {
            Some((None, b"att1", Cow::Borrowed(b"a"))) => (),
            e => panic!("Expecting att1='a' attribute, found {:?}", e),
        }
        match atts.next() {
            None => (),
            e => panic!("Expecting None, found {:?}", e),
        }
    }

    // virtual </inner>
    match r.read_namespaced_event(&mut buf, &mut ns_buf) {
        Ok((Some(ns), End(e))) => {
            assert_eq!(&ns[..], b"urn:example:i");
            assert_eq!(e.name(), b"e");
        }
        e => panic!("Expected End event (</inner>), got {:?}", e),
    }
    // </outer>
    match r.read_namespaced_event(&mut buf, &mut ns_buf) {
        Ok((Some(ns), End(e))) => {
            assert_eq!(&ns[..], b"urn:example:o");
            assert_eq!(e.name(), b"e");
        }
        e => panic!("Expected End event (</outer>), got {:?}", e),
    }
}

#[test]
fn test_koi8_r_encoding() {
    let src: &[u8] = include_bytes!("documents/opennews_all.rss");
    let mut r = Reader::from_reader(src as &[u8]);
    r.trim_text(true).expand_empty_elements(false);
    let mut buf = Vec::new();
    loop {
        match r.read_event(&mut buf) {
            Ok(Text(e)) => {
                e.unescape_and_decode(&r).unwrap();
            }
            Ok(Eof) => break,
            _ => (),
        }
    }
}

#[test]
fn fuzz_53() {
    let data: &[u8] = b"\xe9\x00\x00\x00\x00\x00\x00\x00\x00\
\x00\x00\x00\x00\n(\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\
\x00<>\x00\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00<<\x00\x00\x00";
    let cursor = Cursor::new(data);
    let mut reader = Reader::from_reader(cursor);
    let mut buf = vec![];
    loop {
        match reader.read_event(&mut buf) {
            Ok(quick_xml::events::Event::Eof) | Err(..) => break,
            _ => buf.clear(),
        }
    }
}

#[test]
fn test_issue94() {
    let data = br#"<Run>
<!B>
</Run>"#;
    let mut reader = Reader::from_reader(&data[..]);
    reader.trim_text(true);
    let mut buf = vec![];
    loop {
        match reader.read_event(&mut buf) {
            Ok(quick_xml::events::Event::Eof) | Err(..) => break,
            _ => buf.clear(),
        }
        buf.clear();
    }
}

#[test]
fn fuzz_101() {
    let data: &[u8] = b"\x00\x00<\x00\x00\x0a>&#44444444401?#\x0a413518\
                       #\x0a\x0a\x0a;<:<)(<:\x0a\x0a\x0a\x0a;<:\x0a\x0a\
                       <:\x0a\x0a\x0a\x0a\x0a<\x00*\x00\x00\x00\x00";
    let cursor = Cursor::new(data);
    let mut reader = Reader::from_reader(cursor);
    let mut buf = vec![];
    loop {
        match reader.read_event(&mut buf) {
            Ok(Start(ref e)) | Ok(Empty(ref e))=> {
                if e.unescaped().is_err() {
                    break;
                }
                for a in e.attributes() {
                    if a.ok().map_or(true, |a| a.unescaped_value().is_err()) {
                        break;
                    }
                }
            }
            Ok(Text(ref e)) => {
                if e.unescaped().is_err() {
                    break;
                }
            }
            Ok(Eof) | Err(..) => break,
            _ => (),
        }
        buf.clear();
    }
}
<|endoftext|>


<commit_before>extern crate lichen;

use lichen::parse::{Parser,Block,SrcBlock,Map};
use lichen::logic::{Logic,Expect};
use lichen::var::Var;
use lichen::source::{Src,Next};
use lichen::eval::{Eval,Evaluator};

use std::collections::HashMap;

struct Data;
impl Eval for Data {
    #[allow(unused_variables)]
    fn get (&self, path: Option<Vec<&str>>, lookup: &str) -> Option<Var> {
        match lookup {
            "some_item" => {
                Some(false.into())
            },
            "other_item" => {
                Some(true.into())
            },
            "some_weight" => {
                Some(4.0 .into())
            },
            "other_weight" => {
                Some(5.0 .into())
            },
            "name" => {
                Some("Io".into())
            }
            _ => None
        }
    }

    #[allow(unused_variables)]
    fn set (&mut self, path: Option<Vec<&str>>, lookup: &str, var: Var) {}

    #[allow(unused_variables)]
    fn call (&mut self, var: Var, fun: &str, vars: &Vec<Var>) -> Option<Var> {
        None
    }
}

#[test]
fn parse_block() {
    let src = "root\n
    unequipped !some_item\n
    has_weight some_weight < 5.0\n
    some_comp:any [\nunequipped \nhas_weight\n]\n
\n
    if unequipped \"you're looking for something?\"\n
\n
    if some_comp \"welcome, \nlook around\"\n
    next:now end\n
;";

    let block = Parser::parse_blocks(src).expect("ERROR: Unable to parse source");

    let block_ = [Block::Src(
        SrcBlock {
            idx: 0,
            visited: false,
            or_valid: false,
            name: "root".to_owned(),
            src: vec![Src::Logic("unequipped".to_owned(),
                                 Logic::IsNot("some_item".to_owned())),

                      Src::Logic("has_weight".to_owned(),
                                 Logic::LT(Var::Sym("some_weight".to_owned()), 5.0 .into())),
                      Src::Composite("some_comp".to_owned(),
                                     Expect::Any,
                                     vec!["unequipped".to_owned(),"has_weight".to_owned()]),
                      Src::If("unequipped".to_owned(),
                              vec!["you're looking for something?".into()],
                              None),
                      Src::If("some_comp".to_owned(),
                              vec!["welcome, \nlook around".into()],
                              None),
                      Src::Next(Next::Now("end".to_owned()))],
            logic: HashMap::new(),
        })];

    assert_eq!(block[0],block_[0]);
}

#[test]
fn parse_qsym_block() {
    let src = "root\n
    if !some_item \"you're looking for something?\"\n
;";
    let block = Parser::parse_blocks(src).expect("ERROR: Unable to parse source");
    match &block[0] {
        &Block::Src(ref b) => {
            let r;
            match b.src[0] {
                Src::Logic(ref qsym,_) => { r = qsym; },
                _ => panic!("unknown source found")
            }

            match b.src[1] {
                Src::If(ref r_,_,_) => {
                    assert_eq!(r,r_);
                },
                _ => panic!("unknown source found")
            }
        },
        _ => panic!("unknown block found")
    }
}

#[test]
fn parse_qsym_comp_block() {
    let src =  "root\n
    has_weight some_weight < 5.0\n
    some_comp:any [has_weight !some_item ]\n
    ;";

    let block = Parser::parse_blocks(src).expect("ERROR: Unable to parse source");

    match &block[0] {
        &Block::Src(ref b) => {
            let r;
            match b.src[1] {
                Src::Logic(ref qsym,_) => { r = qsym; },
                _ => panic!("unknown source found")
            }

            match b.src[2] {
                Src::Composite(_,_,ref x) => {
                    assert_eq!(r,&x[1]);
                },
                _ => panic!("unknown source found")
            }
        },
        _ => panic!("unknown block found")
    }
}

#[test]
fn validate_qsym_block() {
    let src =  "root\n
    if other_item next:await store\n
    ;";

    let mut env = Parser::parse_blocks(src).expect("ERROR: Unable to parse source").into_env();
    let mut data = Data;

    let mut ev = Evaluator::new(&mut env);
    let (_,nn) = ev.next().unwrap();

    assert_eq!(nn, Some(Next::Await("store".into())));
}

#[test]
fn validate_reflection_block() {
    let src =  "root\n
    has other_item\n
    hasnt some_item\n
    hasnt-too !hasnt\n
    comp:all has hasnt-too\n
    if comp next:await store\n
    ;";

    let mut env = Parser::parse_blocks(src).expect("ERROR: Unable to parse source").into_env();
    let mut data = Data;

    let ev = Evaluator::new(&mut env);
    let (_,nn) = ev.last().unwrap();

    assert_eq!(nn, Some(Next::Await("store".into())));
}

#[test]
fn parse_if_vec_block() {
    let src = "root\n
    if !some_item [\n
        \"you're looking for something?\"\n
        \"welcome, \nlook around\"\n
        next:now store]\n
;";

    let block = Parser::parse_blocks(src).expect("ERROR: Unable to parse source");

    match &block[0] {
        &Block::Src(ref b) => {
            match b.src[1] {
                Src::If(_,_, ref next) => {
                    assert_eq!(next,&Some(Next::Now("store".to_owned())));
                },
                _ => panic!("unknown source found")
            }
        },
        _ => panic!("unknown block found")
    }
}

#[test]
fn parse_eval_str_block() {
    let src = "root\n
        has_weight some_weight < 5.0\n
        some_comp:all [has_weight !some_item ]\n
    if some_comp \"looks like you are `some_weight kgs heavy, `name\"\n
;";

    let mut env = Parser::parse_blocks(src).expect("ERROR: Unable to parse source").into_env();
    let mut data = Data;

    let ev = Evaluator::new(&mut env);
    let (vars,_node) = ev.last().unwrap();

    assert_eq!(vars, ["looks like you are 4 kgs heavy, Io".into()]);
}

#[test]
fn parse_compare_env_block() {
    let src = "root\n
    weight some_weight < other_weight\n
    if weight next:now store\n
;";
    let mut env = Parser::parse_blocks(src).expect("ERROR: Unable to parse source").into_env();
    let mut data = Data;

    let ev = Evaluator::new(&mut env);
    let (_vars,node) = ev.last().unwrap();

    assert_eq!(node, Some(Next::Now("store".to_string())));
}

#[test]
fn parse_return_varkind() {
    let src = "root\n
    weight some_weight < other_weight\n
    if weight some_weight \"hi `name\"\n
;";

    let mut env = Parser::parse_blocks(src).expect("ERROR: Unable to parse source").into_env();
    let mut data = Data;

    let ev = Evaluator::new(&mut env);
    let (vars,_) = ev.last().unwrap();

    assert_eq!(vars[0], 4.0 .into());
    assert_eq!(vars[1], "hi Io" .into());
}

#[test]
fn parse_follow_nodes() {
    let src = "root\n
    weight some_weight < other_weight\n
    if weight next:now store\n
;\n
\n
store\n
    if !some_item \"welcome, \nlook around\"\n
;";

    let mut env = Parser::parse_blocks(src).expect("ERROR: Unable to parse source").into_env();
    let mut data = Data;

    let ev = Evaluator::new(&mut env);
    let (vars,_) = ev.last().unwrap();

    assert_eq!(vars[0], "welcome, \nlook around".into());
}

#[test]
fn parse_select_nodes() {
    let src = "root\n
    next:select {\"Head to Store?\" store,\n
                \"Leave the town?\" exit-town}\n
\n
    if !some_item [\"Some choices\"
        next:select {\"Head to Store?\" store,\n
                    \"Leave the town?\" exit-town}]\n
\n
    next:select {\"Head to town?\" store bakery tanner,\n
                5 hike,
                \"Leave the town?\" exit-town}\n
\n
    emit \"A dustball blows by\"\n
;";
    let mut env = Parser::parse_blocks(src).expect("ERROR: Unable to parse source").into_env();
    let mut data = Data;

    let mut ev = Evaluator::new(&mut env);
    let (_vars,select1) = ev.next().unwrap();
    let (_vars,select2) = ev.next().unwrap();

    assert_eq!(select1,select2);

    let mut map: Map = HashMap::new();
    map.insert("Head to Store?".to_owned(), vec![Var::Sym("store".to_owned())]);
    map.insert("Leave the town?".to_owned(), vec![Var::Sym("exit-town".to_owned())]);

    assert_eq!(select1, Some(Next::Select(map)));

    let (_,select) = ev.next().unwrap();
    match select.expect("Unable to parse map") {
        Next::Select(map) => {
            println!("Map: {:?}",map);
            assert!(map.contains_key("5"));
            assert_eq!(map.get("5"), Some(&vec![Var::Sym("hike".to_owned())]));
        },
        _ => { panic!("Invalid Next type found") }
    }
}

#[test]
fn parse_next_back_restart() {
    let src = "root\n
    next:now step2
    next:restart\n
;\n
step2
    next:back\n
;\n";

    let p = Parser::parse_blocks(src).expect("ERROR: Unable to parse source");
    let mut env = p.into_env();

    let mut data = Data;

    let mut ev = Evaluator::new(&mut env);

    let (_,next) = ev.next().unwrap();
    assert_eq!(next, Some(Next::Now("step2".to_owned())));

    let (_,next) = ev.next().unwrap();
    assert_eq!(next, Some(Next::Back));

    let (_,next) = ev.next().unwrap();
    assert_eq!(next, Some(Next::Restart(None)));
}

#[test]
fn parse_or_logic() {
    let src = "root\n
    weight some_weight < other_weight\n
    if !weight false\n
    or true\n
;\n";

    let mut env = Parser::parse_blocks(src).expect("ERROR: Unable to parse source").into_env();
    let mut data = Data;

    let mut ev = Evaluator::new(&mut env);
    let (vars,_) = ev.next().unwrap();

    assert_eq!(vars[0], true.into());
}
<commit_msg>add unit for inv logic<commit_after>extern crate lichen;

use lichen::parse::{Parser,Block,SrcBlock,Map};
use lichen::logic::{Logic,Expect};
use lichen::var::Var;
use lichen::source::{Src,Next};
use lichen::eval::{Eval,Evaluator};

use std::collections::HashMap;

struct Data;
impl Eval for Data {
    #[allow(unused_variables)]
    fn get (&self, path: Option<Vec<&str>>, lookup: &str) -> Option<Var> {
        match lookup {
            "some_item" => {
                Some(false.into())
            },
            "other_item" => {
                Some(true.into())
            },
            "some_weight" => {
                Some(4.0 .into())
            },
            "other_weight" => {
                Some(5.0 .into())
            },
            "name" => {
                Some("Io".into())
            }
            _ => None
        }
    }

    #[allow(unused_variables)]
    fn set (&mut self, path: Option<Vec<&str>>, lookup: &str, var: Var) {}

    #[allow(unused_variables)]
    fn call (&mut self, var: Var, fun: &str, vars: &Vec<Var>) -> Option<Var> {
        None
    }
}

#[test]
fn parse_block() {
    let src = "root\n
    unequipped !some_item\n
    has_weight some_weight < 5.0\n
    some_comp:any [\nunequipped \nhas_weight\n]\n
\n
    if unequipped \"you're looking for something?\"\n
\n
    if some_comp \"welcome, \nlook around\"\n
    next:now end\n
;";

    let block = Parser::parse_blocks(src).expect("ERROR: Unable to parse source");

    let block_ = [Block::Src(
        SrcBlock {
            idx: 0,
            visited: false,
            or_valid: false,
            name: "root".to_owned(),
            src: vec![Src::Logic("unequipped".to_owned(),
                                 Logic::IsNot("some_item".to_owned())),

                      Src::Logic("has_weight".to_owned(),
                                 Logic::LT(Var::Sym("some_weight".to_owned()), 5.0 .into())),
                      Src::Composite("some_comp".to_owned(),
                                     Expect::Any,
                                     vec!["unequipped".to_owned(),"has_weight".to_owned()]),
                      Src::If("unequipped".to_owned(),
                              vec!["you're looking for something?".into()],
                              None),
                      Src::If("some_comp".to_owned(),
                              vec!["welcome, \nlook around".into()],
                              None),
                      Src::Next(Next::Now("end".to_owned()))],
            logic: HashMap::new(),
        })];

    assert_eq!(block[0],block_[0]);
}

#[test]
fn parse_qsym_block() {
    let src = "root\n
    if !some_item \"you're looking for something?\"\n
;";
    let block = Parser::parse_blocks(src).expect("ERROR: Unable to parse source");
    match &block[0] {
        &Block::Src(ref b) => {
            let r;
            match b.src[0] {
                Src::Logic(ref qsym,_) => { r = qsym; },
                _ => panic!("unknown source found")
            }

            match b.src[1] {
                Src::If(ref r_,_,_) => {
                    assert_eq!(r,r_);
                },
                _ => panic!("unknown source found")
            }
        },
        _ => panic!("unknown block found")
    }
}

#[test]
fn parse_qsym_comp_block() {
    let src =  "root\n
    has_weight some_weight < 5.0\n
    some_comp:any [has_weight !some_item ]\n
    ;";

    let block = Parser::parse_blocks(src).expect("ERROR: Unable to parse source");

    match &block[0] {
        &Block::Src(ref b) => {
            let r;
            match b.src[1] {
                Src::Logic(ref qsym,_) => { r = qsym; },
                _ => panic!("unknown source found")
            }

            match b.src[2] {
                Src::Composite(_,_,ref x) => {
                    assert_eq!(r,&x[1]);
                },
                _ => panic!("unknown source found")
            }
        },
        _ => panic!("unknown block found")
    }
}

#[test]
fn validate_qsym_block() {
    let src =  "root\n
    if other_item next:await store\n
    ;";

    let mut env = Parser::parse_blocks(src).expect("ERROR: Unable to parse source").into_env();

    let mut ev = Evaluator::new(&mut env);
    let (_,nn) = ev.next().unwrap();

    assert_eq!(nn, Some(Next::Await("store".into())));
}

#[test]
fn validate_reflection_block() {
    let src =  "root\n
    has other_item\n
    hasnt some_item\n
    hasnt-too !hasnt\n
    comp:all has hasnt-too\n
    if comp next:await store\n
    ;";

    let mut env = Parser::parse_blocks(src).expect("ERROR: Unable to parse source").into_env();

    let ev = Evaluator::new(&mut env);
    let (_,nn) = ev.last().unwrap();

    assert_eq!(nn, Some(Next::Await("store".into())));
}

#[test]
fn parse_if_vec_block() {
    let src = "root\n
    if !some_item [\n
        \"you're looking for something?\"\n
        \"welcome, \nlook around\"\n
        next:now store]\n
;";

    let block = Parser::parse_blocks(src).expect("ERROR: Unable to parse source");

    match &block[0] {
        &Block::Src(ref b) => {
            match b.src[1] {
                Src::If(_,_, ref next) => {
                    assert_eq!(next,&Some(Next::Now("store".to_owned())));
                },
                _ => panic!("unknown source found")
            }
        },
        _ => panic!("unknown block found")
    }
}

#[test]
fn parse_eval_str_block() {
    let src = "root\n
        has_weight some_weight < 5.0\n
        some_comp:all [has_weight !some_item ]\n
    if some_comp \"looks like you are `some_weight kgs heavy, `name\"\n
;";

    let mut env = Parser::parse_blocks(src).expect("ERROR: Unable to parse source").into_env();

    let ev = Evaluator::new(&mut env);
    let (vars,_node) = ev.last().unwrap();

    assert_eq!(vars, ["looks like you are 4 kgs heavy, Io".into()]);
}

#[test]
fn parse_compare_env_block() {
    let src = "root\n
    weight some_weight < other_weight\n
    if weight next:now store\n
;";
    let mut env = Parser::parse_blocks(src).expect("ERROR: Unable to parse source").into_env();

    let ev = Evaluator::new(&mut env);
    let (_vars,node) = ev.last().unwrap();

    assert_eq!(node, Some(Next::Now("store".to_string())));
}

#[test]
fn parse_return_varkind() {
    let src = "root\n
    weight some_weight < other_weight\n
    if weight some_weight \"hi `name\"\n
;";

    let mut env = Parser::parse_blocks(src).expect("ERROR: Unable to parse source").into_env();

    let ev = Evaluator::new(&mut env);
    let (vars,_) = ev.last().unwrap();

    assert_eq!(vars[0], 4.0 .into());
    assert_eq!(vars[1], "hi Io" .into());
}

#[test]
fn parse_follow_nodes() {
    let src = "root\n
    weight some_weight < other_weight\n
    if weight next:now store\n
;\n
\n
store\n
    if !some_item \"welcome, \nlook around\"\n
;";

    let mut env = Parser::parse_blocks(src).expect("ERROR: Unable to parse source").into_env();

    let ev = Evaluator::new(&mut env);
    let (vars,_) = ev.last().unwrap();

    assert_eq!(vars[0], "welcome, \nlook around".into());
}

#[test]
fn parse_select_nodes() {
    let src = "root\n
    next:select {\"Head to Store?\" store,\n
                \"Leave the town?\" exit-town}\n
\n
    if !some_item [\"Some choices\"
        next:select {\"Head to Store?\" store,\n
                    \"Leave the town?\" exit-town}]\n
\n
    next:select {\"Head to town?\" store bakery tanner,\n
                5 hike,
                \"Leave the town?\" exit-town}\n
\n
    emit \"A dustball blows by\"\n
;";
    let mut env = Parser::parse_blocks(src).expect("ERROR: Unable to parse source").into_env();

    let mut ev = Evaluator::new(&mut env);
    let (_vars,select1) = ev.next().unwrap();
    let (_vars,select2) = ev.next().unwrap();

    assert_eq!(select1,select2);

    let mut map: Map = HashMap::new();
    map.insert("Head to Store?".to_owned(), vec![Var::Sym("store".to_owned())]);
    map.insert("Leave the town?".to_owned(), vec![Var::Sym("exit-town".to_owned())]);

    assert_eq!(select1, Some(Next::Select(map)));

    let (_,select) = ev.next().unwrap();
    match select.expect("Unable to parse map") {
        Next::Select(map) => {
            println!("Map: {:?}",map);
            assert!(map.contains_key("5"));
            assert_eq!(map.get("5"), Some(&vec![Var::Sym("hike".to_owned())]));
        },
        _ => { panic!("Invalid Next type found") }
    }
}

#[test]
fn parse_next_back_restart() {
    let src = "root\n
    next:now step2
    next:restart\n
;\n
step2
    next:back\n
;\n";

    let p = Parser::parse_blocks(src).expect("ERROR: Unable to parse source");
    let mut env = p.into_env();

    let mut ev = Evaluator::new(&mut env);

    let (_,next) = ev.next().unwrap();
    assert_eq!(next, Some(Next::Now("step2".to_owned())));

    let (_,next) = ev.next().unwrap();
    assert_eq!(next, Some(Next::Back));

    let (_,next) = ev.next().unwrap();
    assert_eq!(next, Some(Next::Restart(None)));
}

#[test]
fn parse_or_logic() {
    let src = "root\n
    weight some_weight < other_weight\n
    if !weight false\n
    or true\n
;\n";

    let mut env = Parser::parse_blocks(src).expect("ERROR: Unable to parse source").into_env();

    let mut ev = Evaluator::new(&mut env);
    let (vars,_) = ev.next().unwrap();

    assert_eq!(vars[0], true.into());
}

#[test]
fn validate_inv_logic() {
    let src = "root\n
    has_no_name !global.name\n
    if has_no_name true\n
;\n
def global\n
;\n";

    let mut env = Parser::parse_blocks(src).expect("ERROR: Unable to parse source").into_env();

    let ev = Evaluator::new(&mut env);
    let (vars,_) = ev.last().unwrap();

    assert_eq!(vars[0], true.into());
}
<|endoftext|>


<commit_before>use crate::rparser::*;
use crate::{gen_get_variants, Variant};

use ipsec_parser::*;

impl<'a> From<IkeTransformDHType> for Variant<'a> {
    fn from(input: IkeTransformDHType) -> Self {
        Variant::U16(input.0)
    }
}

pub struct IPsecBuilder {}
impl RBuilder for IPsecBuilder {
    fn build(&self) -> Box<dyn RParser> {
        Box::new(IPsecParser::new(b"IKEv2"))
    }
    fn get_l4_probe(&self) -> Option<ProbeL4> {
        Some(ipsec_probe)
    }
}

pub struct IPsecParser<'a> {
    _name: Option<&'a [u8]>,

    /// The transforms proposed by the initiator
    pub client_proposals: Vec<Vec<IkeV2Transform>>,

    /// The transforms selected by the responder
    pub server_proposals: Vec<Vec<IkeV2Transform>>,

    /// The Diffie-Hellman group from the server KE message, if present.
    pub dh_group: IkeTransformDHType,
}

impl<'a> RParser for IPsecParser<'a> {
    fn parse_l4(&mut self, data: &[u8], direction: Direction) -> ParseResult {
        match parse_ikev2_header(data) {
            Ok((rem, ref hdr)) => {
                debug!("parse_ikev2_header: {:?}", hdr);
                if rem.is_empty() && hdr.length == 28 {
                    return ParseResult::Ok;
                }
                // Rule 0: check version
                if hdr.maj_ver != 2 || hdr.min_ver != 0 {
                    warn!("Unknown header version: {}.{}", hdr.maj_ver, hdr.min_ver);
                }
                match parse_ikev2_payload_list(rem, hdr.next_payload) {
                    Ok((_, Ok(ref p))) => {
                        debug!("parse_ikev2_payload_with_type: {:?}", p);
                        for payload in p {
                            match payload.content {
                                IkeV2PayloadContent::SA(ref prop) => {
                                    let is_initiator = hdr.flags & IKEV2_FLAG_INITIATOR != 0;
                                    self.add_proposals(prop, is_initiator);
                                }
                                IkeV2PayloadContent::KE(ref kex) => {
                                    debug!("KEX {:?}", kex.dh_group);
                                    if direction == Direction::ToClient {
                                        self.dh_group = kex.dh_group;
                                    }
                                }
                                IkeV2PayloadContent::Nonce(ref n) => {
                                    debug!("Nonce: {:?}", n);
                                }
                                IkeV2PayloadContent::Notify(ref n) => {
                                    debug!("Notify: {:?}", n);
                                }
                                _ => {
                                    debug!("Unknown payload content {:?}", payload.content);
                                }
                            }
                        }
                    }
                    e => warn!("parse_ikev2_payload_with_type: {:?}", e),
                };
            }
            e => warn!("parse_ikev2_header: {:?}", e),
        };
        ParseResult::Ok
    }

    gen_get_variants! {IPsecParser, "ikev2.",
        dh_group   => into,
        enc        => |s| { s.get_server_proposal_enc().map(|x| x.into()) },
        prf        => |s| { s.get_server_proposal_prf().map(|x| x.into()) },
        auth       => |s| { s.get_server_proposal_auth().map(|x| x.into()) },
        dh         => |s| { s.get_server_proposal_dh().map(|x| x.into()) },
        esn        => |s| { s.get_server_proposal_esn().map(|x| x.into()) },
    }
}

macro_rules! get_server_proposal {
    ( $t:tt, $n:ident ) => {
        pub fn $n(&self) -> Option<u16> {
            if let Some(xform) = self.server_proposals.first() {
                xform.iter().find_map(|x| {
                    if let IkeV2Transform::$t(e) = x {
                        Some(e.0)
                    } else {
                        None
                    }
                })
            } else {
                None
            }
        }
    };
}

impl<'a> IPsecParser<'a> {
    pub fn new(name: &'a [u8]) -> IPsecParser<'a> {
        IPsecParser {
            _name: Some(name),
            client_proposals: Vec::new(),
            server_proposals: Vec::new(),
            dh_group: IkeTransformDHType::None,
        }
    }

    get_server_proposal! {Encryption, get_server_proposal_enc}
    get_server_proposal! {PRF, get_server_proposal_prf}
    get_server_proposal! {Auth, get_server_proposal_auth}
    get_server_proposal! {DH, get_server_proposal_dh}
    get_server_proposal! {ESN, get_server_proposal_esn}

    #[allow(clippy::cognitive_complexity)]
    fn add_proposals(&mut self, prop: &[IkeV2Proposal], is_initiator: bool) {
        debug!("num_proposals: {}", prop.len());
        for p in prop {
            debug!("proposal: {:?}", p);
            debug!("num_transforms: {}", p.num_transforms);
            for xform in &p.transforms {
                debug!("transform: {:?}", xform);
                debug!("\ttype: {:?}", xform.transform_type);
                match xform.transform_type {
                    IkeTransformType::EncryptionAlgorithm => {
                        debug!(
                            "\tEncryptionAlgorithm: {:?}",
                            IkeTransformEncType(xform.transform_id)
                        );
                    }
                    IkeTransformType::PseudoRandomFunction => {
                        debug!(
                            "\tPseudoRandomFunction: {:?}",
                            IkeTransformPRFType(xform.transform_id)
                        );
                    }
                    IkeTransformType::IntegrityAlgorithm => {
                        debug!(
                            "\tIntegrityAlgorithm: {:?}",
                            IkeTransformAuthType(xform.transform_id)
                        );
                    }
                    IkeTransformType::DiffieHellmanGroup => {
                        debug!(
                            "\tDiffieHellmanGroup: {:?}",
                            IkeTransformDHType(xform.transform_id)
                        );
                    }
                    IkeTransformType::ExtendedSequenceNumbers => {
                        debug!(
                            "\tExtendedSequenceNumbers: {:?}",
                            IkeTransformESNType(xform.transform_id)
                        );
                    }
                    _ => warn!("\tUnknown transform type {:?}", xform.transform_type),
                }
                if xform.transform_id == 0 {
                    warn!("\tTransform ID == 0 (choice left to responder)");
                };
            }
            let proposals: Vec<IkeV2Transform> = p.transforms.iter().map(|x| x.into()).collect();
            debug!("Proposals\n{:?}", proposals);
            // Rule 1: warn on weak or unknown transforms
            for prop in &proposals {
                match *prop {
                    IkeV2Transform::Encryption(ref enc) => match *enc {
                        IkeTransformEncType::ENCR_DES_IV64
                        | IkeTransformEncType::ENCR_DES
                        | IkeTransformEncType::ENCR_3DES
                        | IkeTransformEncType::ENCR_RC5
                        | IkeTransformEncType::ENCR_IDEA
                        | IkeTransformEncType::ENCR_CAST
                        | IkeTransformEncType::ENCR_BLOWFISH
                        | IkeTransformEncType::ENCR_3IDEA
                        | IkeTransformEncType::ENCR_DES_IV32
                        | IkeTransformEncType::ENCR_NULL => {
                            warn!("Weak Encryption: {:?}", enc);
                        }
                        _ => (),
                    },
                    IkeV2Transform::Auth(ref auth) => {
                        match *auth {
                            IkeTransformAuthType::NONE => {
                                // Note: this could be expected with an AEAD encryption alg.
                                // See rule 4
                            }
                            IkeTransformAuthType::AUTH_HMAC_MD5_96
                            | IkeTransformAuthType::AUTH_HMAC_SHA1_96
                            | IkeTransformAuthType::AUTH_DES_MAC
                            | IkeTransformAuthType::AUTH_KPDK_MD5
                            | IkeTransformAuthType::AUTH_AES_XCBC_96
                            | IkeTransformAuthType::AUTH_HMAC_MD5_128
                            | IkeTransformAuthType::AUTH_HMAC_SHA1_160 => {
                                warn!("Weak auth: {:?}", auth);
                            }
                            _ => (),
                        }
                    }
                    IkeV2Transform::DH(ref dh) => match *dh {
                        IkeTransformDHType::None => {
                            warn!("'None' DH transform proposed");
                        }
                        IkeTransformDHType::Modp768
                        | IkeTransformDHType::Modp1024
                        | IkeTransformDHType::Modp1024s160
                        | IkeTransformDHType::Modp1536 => {
                            warn!("Weak DH: {:?}", dh);
                        }
                        _ => (),
                    },
                    IkeV2Transform::Unknown(tx_type, tx_id) => {
                        warn!("Unknown proposal: type={}, id={}", tx_type.0, tx_id);
                    }
                    _ => (),
                }
            }
            // Rule 2: check if no DH was proposed
            fn has_dh(proposals: &[IkeV2Transform]) -> bool {
                proposals.iter().any(|x| matches!(x, IkeV2Transform::DH(_)))
            }
            if !has_dh(&proposals) {
                warn!("No DH transform found");
            }
            // Rule 3: check if proposing AH ([RFC7296] section 3.3.1)
            if p.protocol_id == ProtocolID::AH {
                warn!("Proposal uses protocol AH - no confidentiality");
            }
            // Rule 4: lack of integrity is accepted only if using an AEAD proposal
            // Look if no auth was proposed, including if proposal is Auth::None
            fn has_auth(proposals: &[IkeV2Transform]) -> bool {
                proposals.iter().any(|x| match *x {
                    IkeV2Transform::Auth(IkeTransformAuthType::NONE) => false,
                    IkeV2Transform::Auth(_) => true,
                    _ => false,
                })
            }
            fn has_gcm(proposals: &[IkeV2Transform]) -> bool {
                proposals.iter().any(|x| {
                    if let IkeV2Transform::Encryption(ref enc) = x {
                        enc.is_aead()
                    } else {
                        false
                    }
                })
            }
            if !has_auth(&proposals) && !has_gcm(&proposals) {
                warn!("No integrity transform found");
            }
            // Rule 5: Check if an integrity and no integrity are part of the same proposal ?
            // XXX
            // Finally
            if is_initiator {
                self.client_proposals.push(proposals);
            } else {
                self.server_proposals.push(proposals);
            }
        }
        // The server must accept one proposal, or reject them all (RFC 5996 2.7)
        if self.server_proposals.len() > 1 {
            warn!("more than one server proposals")
        }
        debug!("client_proposals: {:?}", self.client_proposals);
        debug!("server_proposals: {:?}", self.server_proposals);
    }
}

pub fn ipsec_probe(i: &[u8], _l4info: &L4Info) -> ProbeResult {
    if i.len() <= 20 {
        return ProbeResult::Unsure;
    }
    match parse_ikev2_header(i) {
        Ok((_, ref hdr)) => {
            if hdr.maj_ver != 2 || hdr.min_ver != 0 {
                debug!(
                    "ipsec_probe: could be ipsec, but with unsupported/invalid version {}.{}",
                    hdr.maj_ver, hdr.min_ver
                );
                return ProbeResult::NotForUs;
            }
            if hdr.exch_type.0 < 34 || hdr.exch_type.0 > 37 {
                debug!(
                    "ipsec_probe: could be ipsec, but with unsupported/invalid exchange type {}",
                    hdr.exch_type.0
                );
                return ProbeResult::NotForUs;
            }
            ProbeResult::Certain
        }
        _ => ProbeResult::NotForUs,
    }
}
<commit_msg>Add JA3-like fingerprint for IKEv2<commit_after>use crate::rparser::*;
use crate::{gen_get_variants, Variant};
use ipsec_parser::*;
use itertools::Itertools;

impl<'a> From<IkeTransformDHType> for Variant<'a> {
    fn from(input: IkeTransformDHType) -> Self {
        Variant::U16(input.0)
    }
}

pub struct IPsecBuilder {}
impl RBuilder for IPsecBuilder {
    fn build(&self) -> Box<dyn RParser> {
        Box::new(IPsecParser::new(b"IKEv2"))
    }
    fn get_l4_probe(&self) -> Option<ProbeL4> {
        Some(ipsec_probe)
    }
}

pub struct IPsecParser<'a> {
    _name: Option<&'a [u8]>,

    /// The transforms proposed by the initiator
    pub client_proposals: Vec<Vec<IkeV2Transform>>,

    /// The transforms selected by the responder
    pub server_proposals: Vec<Vec<IkeV2Transform>>,

    /// The Diffie-Hellman group from the server KE message, if present.
    pub dh_group: IkeTransformDHType,

    /// JA3-like hash
    pub fingerprint: Option<String>,
}

impl<'a> RParser for IPsecParser<'a> {
    fn parse_l4(&mut self, data: &[u8], direction: Direction) -> ParseResult {
        match parse_ikev2_header(data) {
            Ok((rem, ref hdr)) => {
                debug!("parse_ikev2_header: {:?}", hdr);
                if rem.is_empty() && hdr.length == 28 {
                    return ParseResult::Ok;
                }
                // Rule 0: check version
                if hdr.maj_ver != 2 || hdr.min_ver != 0 {
                    warn!("Unknown header version: {}.{}", hdr.maj_ver, hdr.min_ver);
                }
                match parse_ikev2_payload_list(rem, hdr.next_payload) {
                    Ok((_, Ok(p))) => {
                        debug!("parse_ikev2_payload_with_type: {:?}", p);
                        let fingerprint = build_ipsec_fingerprint(hdr, &p);
                        let digest = md5::compute(&fingerprint);
                        debug!("Fingerprint: {} --> {:x}", fingerprint, digest);
                        self.fingerprint = Some(format!("{:?}", digest));
                        for payload in p {
                            match payload.content {
                                IkeV2PayloadContent::SA(ref prop) => {
                                    let is_initiator = hdr.flags & IKEV2_FLAG_INITIATOR != 0;
                                    self.add_proposals(prop, is_initiator);
                                }
                                IkeV2PayloadContent::KE(ref kex) => {
                                    debug!("KEX {:?}", kex.dh_group);
                                    if direction == Direction::ToClient {
                                        self.dh_group = kex.dh_group;
                                    }
                                }
                                IkeV2PayloadContent::Nonce(ref n) => {
                                    debug!("Nonce: {:?}", n);
                                }
                                IkeV2PayloadContent::Notify(ref n) => {
                                    debug!("Notify: {:?}", n);
                                }
                                _ => {
                                    debug!("Unknown payload content {:?}", payload.content);
                                }
                            }
                        }
                    }
                    e => warn!("parse_ikev2_payload_with_type: {:?}", e),
                };
            }
            e => warn!("parse_ikev2_header: {:?}", e),
        };
        ParseResult::Ok
    }

    gen_get_variants! {IPsecParser, "ikev2.",
        dh_group    => into,
        enc         => |s| { s.get_server_proposal_enc().map(|x| x.into()) },
        prf         => |s| { s.get_server_proposal_prf().map(|x| x.into()) },
        auth        => |s| { s.get_server_proposal_auth().map(|x| x.into()) },
        dh          => |s| { s.get_server_proposal_dh().map(|x| x.into()) },
        esn         => |s| { s.get_server_proposal_esn().map(|x| x.into()) },
        fingerprint => map_as_ref,
    }
}

macro_rules! get_server_proposal {
    ( $t:tt, $n:ident ) => {
        pub fn $n(&self) -> Option<u16> {
            if let Some(xform) = self.server_proposals.first() {
                xform.iter().find_map(|x| {
                    if let IkeV2Transform::$t(e) = x {
                        Some(e.0)
                    } else {
                        None
                    }
                })
            } else {
                None
            }
        }
    };
}

impl<'a> IPsecParser<'a> {
    pub fn new(name: &'a [u8]) -> IPsecParser<'a> {
        IPsecParser {
            _name: Some(name),
            client_proposals: Vec::new(),
            server_proposals: Vec::new(),
            dh_group: IkeTransformDHType::None,
            fingerprint: None,
        }
    }

    get_server_proposal! {Encryption, get_server_proposal_enc}
    get_server_proposal! {PRF, get_server_proposal_prf}
    get_server_proposal! {Auth, get_server_proposal_auth}
    get_server_proposal! {DH, get_server_proposal_dh}
    get_server_proposal! {ESN, get_server_proposal_esn}

    #[allow(clippy::cognitive_complexity)]
    fn add_proposals(&mut self, prop: &[IkeV2Proposal], is_initiator: bool) {
        debug!("num_proposals: {}", prop.len());
        for p in prop {
            debug!("proposal: {:?}", p);
            debug!("num_transforms: {}", p.num_transforms);
            for xform in &p.transforms {
                debug!("transform: {:?}", xform);
                debug!("\ttype: {:?}", xform.transform_type);
                match xform.transform_type {
                    IkeTransformType::EncryptionAlgorithm => {
                        debug!(
                            "\tEncryptionAlgorithm: {:?}",
                            IkeTransformEncType(xform.transform_id)
                        );
                    }
                    IkeTransformType::PseudoRandomFunction => {
                        debug!(
                            "\tPseudoRandomFunction: {:?}",
                            IkeTransformPRFType(xform.transform_id)
                        );
                    }
                    IkeTransformType::IntegrityAlgorithm => {
                        debug!(
                            "\tIntegrityAlgorithm: {:?}",
                            IkeTransformAuthType(xform.transform_id)
                        );
                    }
                    IkeTransformType::DiffieHellmanGroup => {
                        debug!(
                            "\tDiffieHellmanGroup: {:?}",
                            IkeTransformDHType(xform.transform_id)
                        );
                    }
                    IkeTransformType::ExtendedSequenceNumbers => {
                        debug!(
                            "\tExtendedSequenceNumbers: {:?}",
                            IkeTransformESNType(xform.transform_id)
                        );
                    }
                    _ => warn!("\tUnknown transform type {:?}", xform.transform_type),
                }
                if xform.transform_id == 0 {
                    warn!("\tTransform ID == 0 (choice left to responder)");
                };
            }
            let proposals: Vec<IkeV2Transform> = p.transforms.iter().map(|x| x.into()).collect();
            debug!("Proposals\n{:?}", proposals);
            // Rule 1: warn on weak or unknown transforms
            for prop in &proposals {
                match *prop {
                    IkeV2Transform::Encryption(ref enc) => match *enc {
                        IkeTransformEncType::ENCR_DES_IV64
                        | IkeTransformEncType::ENCR_DES
                        | IkeTransformEncType::ENCR_3DES
                        | IkeTransformEncType::ENCR_RC5
                        | IkeTransformEncType::ENCR_IDEA
                        | IkeTransformEncType::ENCR_CAST
                        | IkeTransformEncType::ENCR_BLOWFISH
                        | IkeTransformEncType::ENCR_3IDEA
                        | IkeTransformEncType::ENCR_DES_IV32
                        | IkeTransformEncType::ENCR_NULL => {
                            warn!("Weak Encryption: {:?}", enc);
                        }
                        _ => (),
                    },
                    IkeV2Transform::Auth(ref auth) => {
                        match *auth {
                            IkeTransformAuthType::NONE => {
                                // Note: this could be expected with an AEAD encryption alg.
                                // See rule 4
                            }
                            IkeTransformAuthType::AUTH_HMAC_MD5_96
                            | IkeTransformAuthType::AUTH_HMAC_SHA1_96
                            | IkeTransformAuthType::AUTH_DES_MAC
                            | IkeTransformAuthType::AUTH_KPDK_MD5
                            | IkeTransformAuthType::AUTH_AES_XCBC_96
                            | IkeTransformAuthType::AUTH_HMAC_MD5_128
                            | IkeTransformAuthType::AUTH_HMAC_SHA1_160 => {
                                warn!("Weak auth: {:?}", auth);
                            }
                            _ => (),
                        }
                    }
                    IkeV2Transform::DH(ref dh) => match *dh {
                        IkeTransformDHType::None => {
                            warn!("'None' DH transform proposed");
                        }
                        IkeTransformDHType::Modp768
                        | IkeTransformDHType::Modp1024
                        | IkeTransformDHType::Modp1024s160
                        | IkeTransformDHType::Modp1536 => {
                            warn!("Weak DH: {:?}", dh);
                        }
                        _ => (),
                    },
                    IkeV2Transform::Unknown(tx_type, tx_id) => {
                        warn!("Unknown proposal: type={}, id={}", tx_type.0, tx_id);
                    }
                    _ => (),
                }
            }
            // Rule 2: check if no DH was proposed
            fn has_dh(proposals: &[IkeV2Transform]) -> bool {
                proposals.iter().any(|x| matches!(x, IkeV2Transform::DH(_)))
            }
            if !has_dh(&proposals) {
                warn!("No DH transform found");
            }
            // Rule 3: check if proposing AH ([RFC7296] section 3.3.1)
            if p.protocol_id == ProtocolID::AH {
                warn!("Proposal uses protocol AH - no confidentiality");
            }
            // Rule 4: lack of integrity is accepted only if using an AEAD proposal
            // Look if no auth was proposed, including if proposal is Auth::None
            fn has_auth(proposals: &[IkeV2Transform]) -> bool {
                proposals.iter().any(|x| match *x {
                    IkeV2Transform::Auth(IkeTransformAuthType::NONE) => false,
                    IkeV2Transform::Auth(_) => true,
                    _ => false,
                })
            }
            fn has_gcm(proposals: &[IkeV2Transform]) -> bool {
                proposals.iter().any(|x| {
                    if let IkeV2Transform::Encryption(ref enc) = x {
                        enc.is_aead()
                    } else {
                        false
                    }
                })
            }
            if !has_auth(&proposals) && !has_gcm(&proposals) {
                warn!("No integrity transform found");
            }
            // Rule 5: Check if an integrity and no integrity are part of the same proposal ?
            // XXX
            // Finally
            if is_initiator {
                self.client_proposals.push(proposals);
            } else {
                self.server_proposals.push(proposals);
            }
        }
        // The server must accept one proposal, or reject them all (RFC 5996 2.7)
        if self.server_proposals.len() > 1 {
            warn!("more than one server proposals")
        }
        debug!("client_proposals: {:?}", self.client_proposals);
        debug!("server_proposals: {:?}", self.server_proposals);
    }
}

/// IPsec version,exchnage type,payload types,notify types,transform proposals
pub fn build_ipsec_fingerprint(hdr: &IkeV2Header, payload_list: &[IkeV2Payload]) -> String {
    // version is fix
    let mut fingerprint = String::from("2.0,");
    // exchange type
    fingerprint += &format!("{},", hdr.exch_type.0);
    // payload types
    let payload_types_str = payload_list
        .iter()
        .fold(vec![hdr.next_payload.0], |mut acc, payload| {
            acc.push(payload.hdr.next_payload_type.0);
            acc
        })
        .iter()
        .join("-");
    fingerprint.push_str(&payload_types_str);
    // notify types
    fingerprint.push(',');
    let notify_types_str = payload_list
        .iter()
        .filter_map(|payload| {
            if let IkeV2PayloadContent::Notify(notify) = &payload.content {
                Some(notify.notify_type.0)
            } else {
                None
            }
        })
        .join("-");
    fingerprint.push_str(&notify_types_str);
    // proposals and transforms
    fingerprint.push(',');
    let mut transforms = Vec::new();
    for payload in payload_list {
        if let IkeV2PayloadContent::SA(sa_list) = &payload.content {
            for sa in sa_list {
                for xform in &sa.transforms {
                    transforms.push(format!("{}:{}", xform.transform_type.0, xform.transform_id));
                }
            }
        }
    }
    let transforms_str = transforms.iter().join("-");
    fingerprint.push_str(&transforms_str);
    // finish
    fingerprint
}

pub fn ipsec_probe(i: &[u8], _l4info: &L4Info) -> ProbeResult {
    if i.len() <= 20 {
        return ProbeResult::Unsure;
    }
    match parse_ikev2_header(i) {
        Ok((_, ref hdr)) => {
            if hdr.maj_ver != 2 || hdr.min_ver != 0 {
                debug!(
                    "ipsec_probe: could be ipsec, but with unsupported/invalid version {}.{}",
                    hdr.maj_ver, hdr.min_ver
                );
                return ProbeResult::NotForUs;
            }
            if hdr.exch_type.0 < 34 || hdr.exch_type.0 > 37 {
                debug!(
                    "ipsec_probe: could be ipsec, but with unsupported/invalid exchange type {}",
                    hdr.exch_type.0
                );
                return ProbeResult::NotForUs;
            }
            ProbeResult::Certain
        }
        _ => ProbeResult::NotForUs,
    }
}
<|endoftext|>