4 * @section SEC_TOC Table of Contents
5 * - <a href="#SEC_INTRO">Introduction</a>
6 * - <a href="#SEC_READING">Reading a playlist</a>
7 * - <a href="#SEC_WRITING">Writing a playlist</a>
8 * - <a href="#SEC_MALICIOUS">Handling malicious XML</a>
11 * @section SEC_INTRO Introduction
12 * Welcome to the short libxspf integration tutorial.
13 * I recommend using this tutorial together with the
14 * code samples in the <c>examples</c> folder; one
15 * of these two sources is likely to answer your questions.
16 * Please drop me a line if you need further assistance.
17 * Good luck with integrating libxspf.
19 * @section SEC_READING Reading a playlist
20 * To read an XSPF playlist you first need a reader instance:
26 * You use the reader like this:
29 * XML_Char const * const baseUri = _PT("http://example.org/");
30 * reader.parseFile(_PT("playlist.xspf"), NULL, baseUri);
33 * That _PT() thing is a macro to support Unicode.
34 * More details on this later.
35 * The second parameter is the callback object that
36 * will receive playlist and track information as
37 * they are made available; it can be <c>NULL</c> if you just
38 * want to verify that the given file is valid XSPF
39 * version 0 or 1 but do not need any more specific
40 * information about it. In general it is a pointer
41 * to an instance of a class derived from XspfReaderCallback.
42 * That means it can override these functions:
45 * void addTrack(XspfTrack * track);
46 * void setProps(XspfProps * props);
48 * void notifyFatalError(..);
49 * bool handleError(..);
50 * bool handleWarning(..);
51 * void notifySuccess();
54 * The first two of these are called by the reader when new
55 * information is made available; setProps() will
56 * be called once per playlist (if the reader has not
57 * stopped before due to an error) but addTrack() can
58 * be called multiple times.
61 * The callback model was copied from Expat,
62 * the underlying XML parser; this model does not
63 * need the whole XML tree in memory at a time,
64 * thus it is more memory-friendly.
66 * These two functions have to do very similar
67 * work: They are passed a data object which they
68 * can analyze (by calling methods starting with "get")
69 * or <i>steal</i> properties from (by calling methods starting with "steal").
72 * Both of these functions have to delete the track/props when
73 * they are done to prevent memory leakage or decide to keep them
74 * for later; the reader will not do this work for you!
75 * Furthermore when deleting the track/props instance all
76 * memory not previously <i>stolen</i> from it will also be deleted
78 * <i>Stealing</i> means transferring the memory's ownership and preventing
79 * it from getting deleted. To avoid memory leakage you should delete
80 * the stolen memory yourself later.
83 * The whole steal-analyze-model was introduced to reduce
84 * the number of unnecessary copy operations and therefore
85 * speed up the parsing process.
87 * When reading is finished the value returned from parseFile() will be
88 * either <c>XSPF_READER_SUCCESS</c> or one of the error codes.
89 * More details about the error occured are passed to the XspfReaderCallback in use.
93 * @section SEC_WRITING Writing a playlist
94 * To write an XSPF playlist file you first need a writer instance:
97 * XML_Char const * const baseUri = _PT("http://example.org/");
98 * XspfWriter * const writer = XspfWriter::makeWriter(formatter, baseUri);
101 * The first parameter is an XML formatter
102 * (an instance of a class derived from XspfXmlFormatter)
103 * which will mainly control the whitespace in the XML output.
104 * libxspf already comes with two built-in formatters:
105 * XspfIndentFormatter which creates well-indented XML output
106 * and XspfSeamlessFormatter which does not create any whitespace at all.
107 * The second parameter is the base URI used to shorten URIs where possible.
109 * If our playlist itself has properties we have to set them
110 * before adding any tracks:
115 * /* Fill in props */
117 * writer->setProps(props);
120 * You could set the playlist's creation time like this:
123 * XspfDateTime dateTime(2006, 8, 28, 11, 30, 11, 1, 0);
124 * props.lendDate(&dateTime);
127 * Similar to the steal/get duality introduced earlier you have to choose
128 * between the "lend" and "give" function family when setting
129 * information. The whole thing again is only about memory ownership:
130 * The lend functions do not transfer ownership, the give functions do.
131 * The give family also offers to copy the memory or to assign
132 * existing memory. To make this more clear calling
135 * props.giveTitle(_PT("Some title"), XspfData::TRANSFER);
138 * would be a bad idea since the memory would not be copied but still
139 * be deleted on destruction. You should use
142 * props.lendTitle(_PT("Some title"));
147 * Back to the writer: When you have created a XspfWriter instance
148 * you can let it write its content to file:
151 * writer.writeFile(_PT("TEST.xspf"));
154 * In this case that would make a valid playlist without any tracks.
155 * So before writing the playlist you should add your tracks:
160 * track.lendCreator(_PT("Breaking Benjamin"));
162 * writer.addTrack(track);
165 * When addTrack() is called the track information is appended to an
166 * internal buffer which is written when writeFile() is called eventually.
167 * You can add the same track several times and you can call writeFile()
168 * several times to write the same playlist to several files;
169 * what you cannot do is add more tracks after writing the playlist
170 * to a file: You will have to call reset() to start over with an empty track list first.
173 * @section SEC_MALICIOUS Handling malicious XML
174 * Let us assume you are using libxspf to power some kind of web service with XSPF
175 * reading cabilities. Let us further assume this service can accept XSPF input
176 * from a user. Due to the nature of XML your service would be vulnerable to
177 * an <i>XML entity explosion attack</i>. What this means is that a rather small
178 * malicious XML file can make the running XML processor a huge amount of memory
179 * or CPU time or both.
181 * A popular example is <i>billion laughs</i>, of which an XSPF version
182 * could look like this:
185 * <?xml version="1.0"?>
186 * <!DOCTYPE billion [
187 * <!ELEMENT billion (#PCDATA)>
188 * <!ENTITY laugh0 "ha">
189 * <!ENTITY laugh1 "&laugh0;&laugh0;">
190 * <!ENTITY laugh2 "&laugh1;&laugh1;">
191 * <!ENTITY laugh3 "&laugh2;&laugh2;">
192 * <!ENTITY laugh4 "&laugh3;&laugh3;">
194 * <!ENTITY laugh30 "&laugh29;&laugh29;">
196 * <playlist version="1" xmlns="http://xspf.org/ns/0/">
197 * <title>&laugh30;</title>
202 * With malicious XML detection enabled, libxspf sets certain limits
203 * to the values of entities. Currently these limits are:
204 * - Length of entity values
205 * - Sum of lookups per entity value
206 * - Lookup depth per entity value
208 * The above <i>billion laughs</i> example has these metrics:
210 * <tr><td class="indexkey">Entity</td><td class="indexkey">Length</td><td class="indexkey">Lookup Sum</td><td class="indexkey">Lookup Depth</td></tr>
211 * <tr><td class="indexvalue">laugh0</td><td class="indexvalue">2</td><td class="indexvalue">0</td><td class="indexvalue">0</td></tr>
212 * <tr><td class="indexvalue">laugh1</td><td class="indexvalue">4</td><td class="indexvalue">2</td><td class="indexvalue">1</td></tr>
213 * <tr><td class="indexvalue">laugh2</td><td class="indexvalue">8</td><td class="indexvalue">6</td><td class="indexvalue">2</td></tr>
214 * <tr><td class="indexvalue">laugh3</td><td class="indexvalue">16</td><td class="indexvalue">14</td><td class="indexvalue">3</td></tr>
215 * <tr><td class="indexvalue">laugh4</td><td class="indexvalue">32</td><td class="indexvalue">30</td><td class="indexvalue">4</td></tr>
216 * <tr><td class="indexvalue">...</td><td class="indexvalue">...</td><td class="indexvalue">...</td><td class="indexvalue">...</td></tr>
217 * <tr><td class="indexvalue">laugh30</td><td class="indexvalue">2,147,483,648</td><td class="indexvalue">2,147,483,646</td><td class="indexvalue">30</td></tr>
218 * <tr><td class="indexvalue">laugh<i>n</i></td><td class="indexvalue">2^(<i>n</i>+1)</td><td class="indexvalue">2^(<i>n</i>+1)-2</td><td class="indexvalue"><i>n</i></td></tr>
221 * Looking at <i>laugh30</i> all three values are much higher than those of most peaceful XML files.
225 * reader.enableMaliciousXmlDetection(true);
228 * you can make a XspfReader instance protocol these metrics in order to detect
229 * malicious XML. Also, all of these metrics can be limited and adjusted individually;
230 * for instance this is how to limit the lookup depth to a maximum of 2:
233 * reader.limitLookupDepthPerEntityValue(true);
234 * reader.setMaxLookupDepthPerEntityValue(2);
238 * Malicious XML detection can only work for you if you are clear
239 * about your software's use cases. Tune the detection parameters to
240 * what you have to allow and reject everything else.