1 use base64;
2 use line_wrap;
3 use std::borrow::Cow;
4 use std::io::Write;
5 use xml_rs::name::Name;
6 use xml_rs::namespace::Namespace;
7 use xml_rs::writer::{EmitterConfig, Error as XmlWriterError, EventWriter, XmlEvent};
8 
9 use stream::{Event, Writer};
10 use Error;
11 
12 static XML_PROLOGUE: &str = r#"<?xml version="1.0" encoding="UTF-8"?>
13 <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
14 <plist version="1.0">
15 "#;
16 
17 impl From<XmlWriterError> for Error {
from(err: XmlWriterError) -> Error18     fn from(err: XmlWriterError) -> Error {
19         match err {
20             XmlWriterError::Io(err) => Error::Io(err),
21             _ => Error::InvalidData,
22         }
23     }
24 }
25 
26 #[derive(PartialEq)]
27 enum Element {
28     Dictionary,
29     Array,
30 }
31 
32 pub struct XmlWriter<W: Write> {
33     xml_writer: EventWriter<W>,
34     stack: Vec<Element>,
35     expecting_key: bool,
36     written_prologue: bool,
37     // Not very nice
38     empty_namespace: Namespace,
39 }
40 
41 impl<W: Write> XmlWriter<W> {
new(writer: W) -> XmlWriter<W>42     pub fn new(writer: W) -> XmlWriter<W> {
43         let config = EmitterConfig::new()
44             .line_separator("\n")
45             .indent_string("\t")
46             .perform_indent(true)
47             .write_document_declaration(false)
48             .normalize_empty_elements(true)
49             .cdata_to_characters(true)
50             .keep_element_names_stack(false)
51             .autopad_comments(true);
52 
53         XmlWriter {
54             xml_writer: EventWriter::new_with_config(writer, config),
55             stack: Vec::new(),
56             expecting_key: false,
57             written_prologue: false,
58             empty_namespace: Namespace::empty(),
59         }
60     }
61 
write_element_and_value(&mut self, name: &str, value: &str) -> Result<(), Error>62     fn write_element_and_value(&mut self, name: &str, value: &str) -> Result<(), Error> {
63         self.start_element(name)?;
64         self.write_value(value)?;
65         self.end_element(name)?;
66         Ok(())
67     }
68 
start_element(&mut self, name: &str) -> Result<(), Error>69     fn start_element(&mut self, name: &str) -> Result<(), Error> {
70         self.xml_writer.write(XmlEvent::StartElement {
71             name: Name::local(name),
72             attributes: Cow::Borrowed(&[]),
73             namespace: Cow::Borrowed(&self.empty_namespace),
74         })?;
75         Ok(())
76     }
77 
end_element(&mut self, name: &str) -> Result<(), Error>78     fn end_element(&mut self, name: &str) -> Result<(), Error> {
79         self.xml_writer.write(XmlEvent::EndElement {
80             name: Some(Name::local(name)),
81         })?;
82         Ok(())
83     }
84 
write_value(&mut self, value: &str) -> Result<(), Error>85     fn write_value(&mut self, value: &str) -> Result<(), Error> {
86         self.xml_writer.write(XmlEvent::Characters(value))?;
87         Ok(())
88     }
89 
write(&mut self, event: &Event) -> Result<(), Error>90     pub fn write(&mut self, event: &Event) -> Result<(), Error> {
91         <Self as Writer>::write(self, event)
92     }
93 
into_inner(self) -> W94     pub fn into_inner(self) -> W {
95         self.xml_writer.into_inner()
96     }
97 }
98 
99 impl<W: Write> Writer for XmlWriter<W> {
write(&mut self, event: &Event) -> Result<(), Error>100     fn write(&mut self, event: &Event) -> Result<(), Error> {
101         if !self.written_prologue {
102             self.xml_writer
103                 .inner_mut()
104                 .write_all(XML_PROLOGUE.as_bytes())?;
105 
106             self.written_prologue = true;
107         }
108 
109         if self.expecting_key {
110             match *event {
111                 Event::EndDictionary => match self.stack.pop() {
112                     Some(Element::Dictionary) => {
113                         self.end_element("dict")?;
114                         self.expecting_key = self.stack.last() == Some(&Element::Dictionary);
115                     }
116                     _ => return Err(Error::InvalidData),
117                 },
118                 Event::StringValue(ref value) => {
119                     self.write_element_and_value("key", &*value)?;
120                     self.expecting_key = false;
121                 }
122                 _ => return Err(Error::InvalidData),
123             }
124         } else {
125             match *event {
126                 Event::StartArray(_) => {
127                     self.start_element("array")?;
128                     self.stack.push(Element::Array);
129                 }
130                 Event::EndArray => match self.stack.pop() {
131                     Some(Element::Array) => self.end_element("array")?,
132                     _ => return Err(Error::InvalidData),
133                 },
134 
135                 Event::StartDictionary(_) => {
136                     self.start_element("dict")?;
137                     self.stack.push(Element::Dictionary);
138                 }
139                 Event::EndDictionary => return Err(Error::InvalidData),
140 
141                 Event::BooleanValue(true) => {
142                     self.start_element("true")?;
143                     self.end_element("true")?;
144                 }
145                 Event::BooleanValue(false) => {
146                     self.start_element("false")?;
147                     self.end_element("false")?;
148                 }
149                 Event::DataValue(ref value) => {
150                     let base64_data = base64_encode_plist(&value, self.stack.len());
151                     self.write_element_and_value("data", &base64_data)?;
152                 }
153                 Event::DateValue(ref value) => {
154                     self.write_element_and_value("date", &value.to_rfc3339())?
155                 }
156                 Event::IntegerValue(ref value) => {
157                     self.write_element_and_value("integer", &value.to_string())?
158                 }
159                 Event::RealValue(ref value) => {
160                     self.write_element_and_value("real", &value.to_string())?
161                 }
162                 Event::StringValue(ref value) => self.write_element_and_value("string", &*value)?,
163             };
164 
165             self.expecting_key = self.stack.last() == Some(&Element::Dictionary);
166         }
167 
168         // If there are no more open tags then write the </plist> element
169         if self.stack.len() == 0 {
170             // We didn't tell the xml_writer about the <plist> tag so we'll skip telling it
171             // about the </plist> tag as well.
172             self.xml_writer.inner_mut().write_all(b"\n</plist>")?;
173         }
174 
175         Ok(())
176     }
177 }
178 
base64_encode_plist(data: &[u8], indent: usize) -> String179 fn base64_encode_plist(data: &[u8], indent: usize) -> String {
180     // XML plist data elements are always formatted by apple tools as
181     // <data>
182     // AAAA..AA (68 characters per line)
183     // </data>
184     // Allocate space for base 64 string and line endings up front
185     const LINE_LEN: usize = 68;
186     let mut line_ending = Vec::with_capacity(1 + indent);
187     line_ending.push(b'\n');
188     (0..indent).for_each(|_| line_ending.push(b'\t'));
189 
190     // Find the max length of `data` encoded as a base 64 string with padding
191     let base64_max_string_len = data.len() * 4 / 3 + 4;
192 
193     // Find the max length of the formatted base 64 string as: max length of the base 64 string
194     // + line endings and indents at the start of the string and after every line
195     let base64_max_string_len_with_formatting =
196         base64_max_string_len + (2 + base64_max_string_len / LINE_LEN) * line_ending.len();
197 
198     let mut output = vec![0; base64_max_string_len_with_formatting];
199 
200     // Start output with a line ending and indent
201     &mut output[..line_ending.len()].copy_from_slice(&line_ending);
202 
203     // Encode `data` as a base 64 string
204     let base64_string_len =
205         base64::encode_config_slice(data, base64::STANDARD, &mut output[line_ending.len()..]);
206 
207     // Line wrap the base 64 encoded string
208     let line_wrap_len = line_wrap::line_wrap(
209         &mut output[line_ending.len()..],
210         base64_string_len,
211         LINE_LEN,
212         &line_wrap::SliceLineEnding::new(&line_ending),
213     );
214 
215     // Add the final line ending and indent
216     &mut output[line_ending.len() + base64_string_len + line_wrap_len..][..line_ending.len()]
217         .copy_from_slice(&line_ending);
218 
219     // Ensure output is the correct length
220     output.truncate(base64_string_len + line_wrap_len + 2 * line_ending.len());
221     String::from_utf8(output).expect("base 64 string must be valid utf8")
222 }
223 
224 #[cfg(test)]
225 mod tests {
226     use humantime::parse_rfc3339_weak;
227     use std::io::Cursor;
228 
229     use super::*;
230     use stream::Event::*;
231 
232     #[test]
streaming_parser()233     fn streaming_parser() {
234         let plist = &[
235             StartDictionary(None),
236             StringValue("Author".to_owned()),
237             StringValue("William Shakespeare".to_owned()),
238             StringValue("Lines".to_owned()),
239             StartArray(None),
240             StringValue("It is a tale told by an idiot,".to_owned()),
241             StringValue("Full of sound and fury, signifying nothing.".to_owned()),
242             DataValue((0..128).collect::<Vec<_>>()),
243             EndArray,
244             StringValue("Death".to_owned()),
245             IntegerValue(1564),
246             StringValue("Height".to_owned()),
247             RealValue(1.60),
248             StringValue("Data".to_owned()),
249             DataValue(vec![0, 0, 0, 190, 0, 0, 0, 3, 0, 0, 0, 30, 0, 0, 0]),
250             StringValue("Birthdate".to_owned()),
251             DateValue(parse_rfc3339_weak("1981-05-16 11:32:06").unwrap().into()),
252             StringValue("Comment".to_owned()),
253             StringValue("2 < 3".to_owned()), // make sure characters are escaped
254             EndDictionary,
255         ];
256 
257         let mut cursor = Cursor::new(Vec::new());
258 
259         {
260             let mut plist_w = XmlWriter::new(&mut cursor);
261 
262             for item in plist {
263                 plist_w.write(item).unwrap();
264             }
265         }
266 
267         let comparison = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
268 <!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">
269 <plist version=\"1.0\">
270 <dict>
271 \t<key>Author</key>
272 \t<string>William Shakespeare</string>
273 \t<key>Lines</key>
274 \t<array>
275 \t\t<string>It is a tale told by an idiot,</string>
276 \t\t<string>Full of sound and fury, signifying nothing.</string>
277 \t\t<data>
278 \t\tAAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEy
279 \t\tMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2Rl
280 \t\tZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn8=
281 \t\t</data>
282 \t</array>
283 \t<key>Death</key>
284 \t<integer>1564</integer>
285 \t<key>Height</key>
286 \t<real>1.6</real>
287 \t<key>Data</key>
288 \t<data>
289 \tAAAAvgAAAAMAAAAeAAAA
290 \t</data>
291 \t<key>Birthdate</key>
292 \t<date>1981-05-16T11:32:06Z</date>
293 \t<key>Comment</key>
294 \t<string>2 &lt; 3</string>
295 </dict>
296 </plist>";
297 
298         let s = String::from_utf8(cursor.into_inner()).unwrap();
299 
300         assert_eq!(s, comparison);
301     }
302 }
303