1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
use std::fmt;
use std::str;
use ber::{BerObject, BerObjectContent, BerTag};
use ber::BitStringObject;

use rusticata_macros::debug;

#[derive(Clone,PartialEq)]
pub enum PrettyPrinterFlag {
    ShowHeader,
}

pub struct PrettyBer<'a> {
    obj:     &'a BerObject<'a>,
    indent:  usize,
    inc:     usize,

    flags:   Vec<PrettyPrinterFlag>,
}

impl<'a> BerObject<'a> {
    pub fn as_pretty(&'a self, indent:usize, increment:usize) -> PrettyBer<'a> {
        PrettyBer{
            obj:     self,
            indent:  indent,
            inc:     increment,

            flags:   Vec::new(),
        }
    }
}

impl<'a> PrettyBer<'a> {
    pub fn set_flag(&mut self, flag: PrettyPrinterFlag) {
        if ! self.flags.contains(&flag) {
            self.flags.push(flag);
        }
    }

    pub fn next_indent<'b>(&self, obj: &'b BerObject) -> PrettyBer<'b> {
        PrettyBer{
            obj:     obj,
            indent:  self.indent + self.inc,
            inc:     self.inc,
            flags:   self.flags.to_vec(),
        }
    }
}

impl<'a> fmt::Debug for PrettyBer<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if self.indent > 0 {
            try!(write!(f, "{:1$}", " ", self.indent));
        };
        if self.flags.contains(&PrettyPrinterFlag::ShowHeader) {
            try!(write!(f, "[c:{}, s:{}, t:{}] ", self.obj.class, self.obj.structured, self.obj.tag));
        };
        fn print_utf8_string_with_type(f: &mut fmt::Formatter, s: &[u8], ty: &str) -> fmt::Result {
            match str::from_utf8(s) {
                Ok(b)  => writeln!(f, "{}(\"{}\")", ty, b),
                Err(e) => writeln!(f, "{}({:?}) <error decoding utf8 string: {:?}>", ty, s, e),
            }
        }
        match self.obj.content {
            BerObjectContent::EndOfContent           => writeln!(f, "EndOfContent"),
            BerObjectContent::Boolean(b)             => writeln!(f, "Boolean({:?})", b),
            BerObjectContent::Integer(i)             => writeln!(f, "Integer({:?})", debug::HexSlice{d:i}),
            BerObjectContent::Enum(i)                => writeln!(f, "Enum({})", i),
            BerObjectContent::OID(ref v)             => writeln!(f, "OID({:?})", v),
            BerObjectContent::RelativeOID(ref v)     => writeln!(f, "RelativeOID({:?})", v),
            BerObjectContent::Null                   => writeln!(f, "Null"),
            BerObjectContent::OctetString(v)         => writeln!(f, "OctetString({:?})", debug::HexSlice{d:v}),
            BerObjectContent::BitString(u,BitStringObject{data:v})
                                                     => writeln!(f, "BitString({},{:?})", u, debug::HexSlice{d:v}),
            BerObjectContent::GeneralizedTime(s)     => print_utf8_string_with_type(f, s, "GeneralizedTime"),
            BerObjectContent::UTCTime(s)             => print_utf8_string_with_type(f, s, "UTCTime"),
            BerObjectContent::PrintableString(s)     => print_utf8_string_with_type(f, s, "PrintableString"),
            BerObjectContent::NumericString(s)       => print_utf8_string_with_type(f, s, "NumericString"),
            BerObjectContent::UTF8String(s)          => print_utf8_string_with_type(f, s, "UTF8String"),
            BerObjectContent::IA5String(s)           => print_utf8_string_with_type(f, s, "IA5String"),
            BerObjectContent::T61String(s)           => print_utf8_string_with_type(f, s, "T61String"),
            BerObjectContent::BmpString(s)           => print_utf8_string_with_type(f, s, "BmpString"),
            BerObjectContent::GeneralString(s)       => print_utf8_string_with_type(f, s, "GeneralString"),
            BerObjectContent::ContextSpecific(n,ref o) => {
                let new_indent = self.indent + self.inc;
                writeln!(f, "ContextSpecific [{}] {{", n)?;
                match *o {
                    Some(ref obj) => write!(f, "{:?}", self.next_indent(obj))?,
                    None          => writeln!(f, "{:1$}None", " ", new_indent)?,
                };
                if self.indent > 0 {
                    write!(f, "{:1$}", " ", self.indent)?;
                };
                writeln!(f, "}}")?;
                Ok(())
            },
            BerObjectContent::Set(ref v) |
            BerObjectContent::Sequence(ref v)        => {
                let ty = if self.obj.tag == BerTag::Sequence { "Sequence" } else { "Set" };
                writeln!(f, "{}[", ty)?;
                for o in v {
                    write!(f, "{:?}", self.next_indent(o))?;
                };
                if self.indent > 0 {
                    write!(f, "{:1$}", " ", self.indent)?;
                };
                writeln!(f, "]")?;
                Ok(())
            },
            BerObjectContent::Unknown(tag,o)         => writeln!(f, "Unknown({:?},{:x?})", tag, o),
        }
    }
}

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

#[test]
fn test_pretty_print() {
    let d = BerObject::from_obj(BerObjectContent::Sequence(vec![
        BerObject::from_int_slice(b"\x01\x00\x01"),
        BerObject::from_int_slice(b"\x01\x00\x01"),
        BerObject::from_obj(BerObjectContent::Set(vec![
            BerObject::from_int_slice(b"\x01"),
            BerObject::from_int_slice(b"\x02"),
        ]))
    ]));

    println!("{:?}", d.as_pretty(0,2));

    let mut pp = d.as_pretty(0,4);
    pp.set_flag(PrettyPrinterFlag::ShowHeader);
    println!("{:?}", pp);
}

}