diff --git a/src/element.rs b/src/element.rs index ee248508..db41fd55 100644 --- a/src/element.rs +++ b/src/element.rs @@ -18,6 +18,22 @@ use std::slice; use convert::{IntoElements, IntoAttributeValue, ElementEmitter}; +/// Escape XML text +pub fn write_escaped(writer: &mut W, input: &str) -> Result<()> { + for c in input.chars() { + match c { + '&' => write!(writer, "&")?, + '<' => write!(writer, "<")?, + '>' => write!(writer, ">")?, + '\'' => write!(writer, "'")?, + '"' => write!(writer, """)?, + _ => write!(writer, "{}", c)?, + } + } + + Ok(()) +} + /// A node in an element tree. #[derive(Clone, Debug, PartialEq, Eq)] pub enum Node { @@ -71,7 +87,7 @@ impl Node { fn write_to_inner(&self, writer: &mut W, last_namespace: &mut Option) -> Result<()>{ match *self { Node::Element(ref elmt) => elmt.write_to_inner(writer, last_namespace)?, - Node::Text(ref s) => write!(writer, "{}", s)?, + Node::Text(ref s) => write_escaped(writer, s)?, } Ok(()) @@ -323,13 +339,17 @@ impl Element { if let Some(ref ns) = self.namespace { if *last_namespace != self.namespace { - write!(writer, " xmlns=\"{}\"", ns)?; + write!(writer, " xmlns=\"")?; + write_escaped(writer, ns)?; + write!(writer, "\"")?; *last_namespace = Some(ns.clone()); } } for (key, value) in &self.attributes { - write!(writer, " {}=\"{}\"", key, value)?; + write!(writer, " {}=\"", key)?; + write_escaped(writer, value)?; + write!(writer, "\"")?; } if self.children.is_empty() { diff --git a/src/tests.rs b/src/tests.rs index bc71ca22..6dd6a2ba 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -43,6 +43,34 @@ fn writer_works() { assert_eq!(String::from_utf8(writer).unwrap(), TEST_STRING); } +#[test] +fn writer_escapes_attributes() { + let root = Element::builder("root") + .attr("a", "\"Air\" quotes") + .build(); + let mut writer = Vec::new(); + { + root.write_to(&mut writer).unwrap(); + } + assert_eq!(String::from_utf8(writer).unwrap(), + r#""# + ); +} + +#[test] +fn writer_escapes_text() { + let root = Element::builder("root") + .append("<3") + .build(); + let mut writer = Vec::new(); + { + root.write_to(&mut writer).unwrap(); + } + assert_eq!(String::from_utf8(writer).unwrap(), + r#"<3"# + ); +} + #[test] fn builder_works() { let elem = Element::builder("a")