blob: b3ae22137837eef14fc1c9733ff201eb6b498fd8 [file] [log] [blame]
use crate::ber::BitStringObject;
use crate::ber::{BerObject, BerObjectContent, BerTag};
use std::fmt;
use std::iter::FromIterator;
use std::str;
use rusticata_macros::debug;
#[derive(Clone, Debug, 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,
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,
indent: self.indent + self.inc,
inc: self.inc,
flags: self.flags.to_vec(),
}
}
}
impl<'a> fmt::Debug for PrettyBer<'a> {
#[rustfmt::skip]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.indent > 0 {
write!(f, "{:1$}", " ", self.indent)?;
};
if self.flags.contains(&PrettyPrinterFlag::ShowHeader) {
write!(f, "[c:{:?}, s:{}, t:{}] ", self.obj.header.class, self.obj.header.structured, self.obj.header.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),
}
}
fn print_utf16_string_with_type(f: &mut fmt::Formatter, s: &[u8], ty: &str) -> fmt::Result {
let chars: Vec<u16> = s
.chunks_exact(2)
.map(|a| u16::from_be_bytes([a[0], a[1]]))
.collect();
match String::from_utf16(&chars) {
Ok(b) => writeln!(f, "{}(\"{}\")", ty, b),
Err(e) => writeln!(f, "{}({:?}) <error decoding utf16 string: {:?}>", ty, s, e),
}
}
fn print_utf32_string_with_type(f: &mut fmt::Formatter, s: &[u8], ty: &str) -> fmt::Result {
let chars: Option<Vec<char>> = s
.chunks_exact(4)
.map(|a| std::char::from_u32(u32::from_be_bytes([a[0], a[1], a[2], a[3]])))
.collect();
match chars {
Some(b) => writeln!(f, "{}(\"{}\")", ty, String::from_iter(b)),
None => writeln!(f, "{}({:?}) <error decoding utf32 string>", ty, s),
}
}
match self.obj.content {
BerObjectContent::EndOfContent => writeln!(f, "EndOfContent"),
BerObjectContent::Boolean(b) => writeln!(f, "Boolean({:?})", b),
BerObjectContent::Integer(i) => writeln!(f, "Integer({:?})", debug::HexSlice(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(v)),
BerObjectContent::BitString(u,BitStringObject{data:v})
=> writeln!(f, "BitString({},{:?})", u, debug::HexSlice(v)),
BerObjectContent::GeneralizedTime(s) => writeln!(f, "GeneralizedTime(\"{}\")", s),
BerObjectContent::UTCTime(s) => writeln!(f, "UTCTime(\"{}\")", s),
BerObjectContent::VisibleString(s) => writeln!(f, "VisibleString(\"{}\")", s),
BerObjectContent::PrintableString(s) => writeln!(f, "PrintableString(\"{}\")", s),
BerObjectContent::NumericString(s) => writeln!(f, "NumericString(\"{}\")", s),
BerObjectContent::UTF8String(s) => writeln!(f, "UTF8String(\"{}\")", s),
BerObjectContent::IA5String(s) => writeln!(f, "IA5String(\"{}\")", s),
BerObjectContent::T61String(v) => writeln!(f, "T61String({:?})", debug::HexSlice(v)),
BerObjectContent::VideotexString(v) => writeln!(f, "VideotexString({:?})", debug::HexSlice(v)),
BerObjectContent::BmpString(s) => print_utf16_string_with_type(f, s, "BmpString"),
BerObjectContent::UniversalString(s) => print_utf32_string_with_type(f, s, "UniversalString"),
BerObjectContent::ObjectDescriptor(s) => print_utf8_string_with_type(f, s, "ObjectDescriptor"),
BerObjectContent::GraphicString(s) => print_utf8_string_with_type(f, s, "GraphicString"),
BerObjectContent::GeneralString(s) => print_utf8_string_with_type(f, s, "GeneralString"),
BerObjectContent::Optional(ref o) => {
match o {
Some(obj) => writeln!(f, "OPTION {:?}", obj),
None => writeln!(f, "NONE"),
}
}
BerObjectContent::Tagged(class, tag, ref obj) => {
writeln!(f, "ContextSpecific [{} {}] {{", class, tag)?;
write!(f, "{:?}", self.next_indent(obj))?;
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.header.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 super::PrettyPrinterFlag;
use crate::ber::*;
#[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);
}
}