libxspf  1.2.0
Mainpage
1 /**
2  * @mainpage
3  *
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>
9  *
10  *
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.
18  *
19  * @section SEC_READING Reading a playlist
20  * To read an XSPF playlist you first need a reader instance:
21  *
22  * @code
23  * XspfReaer reader;
24  * @endcode
25  *
26  * You use the reader like this:
27  *
28  * @code
29  * XML_Char const * const baseUri = _PT("http://example.org/");
30  * reader.parseFile(_PT("playlist.xspf"), NULL, baseUri);
31  * @endcode
32  *
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:
43  *
44  * @code
45  * void addTrack(XspfTrack * track);
46  * void setProps(XspfProps * props);
47  *
48  * void notifyFatalError(..);
49  * bool handleError(..);
50  * bool handleWarning(..);
51  * void notifySuccess();
52  * @endcode
53  *
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.
59  *
60  * @remarks
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.
65  *
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").
70  *
71  * @attention
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
77  * by the destructor.
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.
81  *
82  * @remarks
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.
86  *
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.
90  *
91  *
92  *
93  * @section SEC_WRITING Writing a playlist
94  * To write an XSPF playlist file you first need a writer instance:
95  *
96  * @code
97  * XML_Char const * const baseUri = _PT("http://example.org/");
98  * XspfWriter * const writer = XspfWriter::makeWriter(formatter, baseUri);
99  * @endcode
100  *
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.
108  *
109  * If our playlist itself has properties we have to set them
110  * before adding any tracks:
111  *
112  * @code
113  * XspfProps props;
114  * ...
115  * /* Fill in props */
116  * ...
117  * writer->setProps(props);
118  * @endcode
119  *
120  * You could set the playlist's creation time like this:
121  *
122  * @code
123  * XspfDateTime dateTime(2006, 8, 28, 11, 30, 11, 1, 0);
124  * props.lendDate(&dateTime);
125  * @endcode
126  *
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
133  *
134  * @code
135  * props.giveTitle(_PT("Some title"), XspfData::TRANSFER);
136  * @endcode
137  *
138  * would be a bad idea since the memory would not be copied but still
139  * be deleted on destruction. You should use
140  *
141  * @code
142  * props.lendTitle(_PT("Some title"));
143  * @endcode
144  *
145  * in this case.
146  *
147  * Back to the writer: When you have created a XspfWriter instance
148  * you can let it write its content to file:
149  *
150  * @code
151  * writer.writeFile(_PT("TEST.xspf"));
152  * @endcode
153  *
154  * In this case that would make a valid playlist without any tracks.
155  * So before writing the playlist you should add your tracks:
156  *
157  * @code
158  * XspfTrack track;
159  * ...
160  * track.lendCreator(_PT("Breaking Benjamin"));
161  * ...
162  * writer.addTrack(track);
163  * @endcode
164  *
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.
171  *
172  *
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.
180  *
181  * A popular example is <i>billion laughs</i>, of which an XSPF version
182  * could look like this:
183  *
184  * @code
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;">
193  * ...
194  * <!ENTITY laugh30 "&laugh29;&laugh29;">
195  * ]>
196  * <playlist version="1" xmlns="http://xspf.org/ns/0/">
197  * <title>&laugh30;</title>
198  * <trackList />
199  * </playlist>
200  * @endcode
201  *
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
207  *
208  * The above <i>billion laughs</i> example has these metrics:
209  * <table border="0">
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>
219  * </table>
220  *
221  * Looking at <i>laugh30</i> all three values are much higher than those of most peaceful XML files.
222  * Through calling
223  *
224  * @code
225  * reader.enableMaliciousXmlDetection(true);
226  * @endcode
227  *
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:
231  *
232  * @code
233  * reader.limitLookupDepthPerEntityValue(true);
234  * reader.setMaxLookupDepthPerEntityValue(2);
235  * @endcode
236  *
237  * @attention
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.
241  */