~ubuntu-branches/ubuntu/quantal/pianobar/quantal

« back to all changes in this revision

Viewing changes to src/libpiano/xml.c

  • Committer: Package Import Robot
  • Author(s): Luke Faraone
  • Date: 2012-05-06 14:24:34 UTC
  • mfrom: (1.3.9)
  • Revision ID: package-import@ubuntu.com-20120506142434-74kwucnyp97msxdi
Tags: 2012.05.06-1
* New upstream version.
  - JSON api support (closes: #670483, LP: #988395)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
Copyright (c) 2008-2011
3
 
        Lars-Dominik Braun <lars@6xq.net>
4
 
 
5
 
Permission is hereby granted, free of charge, to any person obtaining a copy
6
 
of this software and associated documentation files (the "Software"), to deal
7
 
in the Software without restriction, including without limitation the rights
8
 
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
 
copies of the Software, and to permit persons to whom the Software is
10
 
furnished to do so, subject to the following conditions:
11
 
 
12
 
The above copyright notice and this permission notice shall be included in
13
 
all copies or substantial portions of the Software.
14
 
 
15
 
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
 
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
 
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
 
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
 
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
 
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
 
THE SOFTWARE.
22
 
*/
23
 
 
24
 
#ifndef __FreeBSD__
25
 
#define _BSD_SOURCE /* required by strdup() */
26
 
#define _DARWIN_C_SOURCE /* strdup() on OS X */
27
 
#endif
28
 
 
29
 
#include <stdio.h>
30
 
#include <string.h>
31
 
#include <stdlib.h>
32
 
#include <ezxml.h>
33
 
#include <assert.h>
34
 
 
35
 
#include "xml.h"
36
 
#include "piano.h"
37
 
#include "crypt.h"
38
 
#include "config.h"
39
 
#include "piano_private.h"
40
 
 
41
 
static void PianoXmlStructParser (const ezxml_t,
42
 
                void (*callback) (const char *, const ezxml_t, void *), void *);
43
 
static char *PianoXmlGetNodeText (const ezxml_t);
44
 
 
45
 
/*      parse fault and get fault type
46
 
 *      @param xml <name> content
47
 
 *      @param xml <value> node
48
 
 *      @param return error string
49
 
 *      @return nothing
50
 
 */
51
 
static void PianoXmlIsFaultCb (const char *key, const ezxml_t value,
52
 
                void *data) {
53
 
        PianoReturn_t *ret = data;
54
 
        char *valueStr = PianoXmlGetNodeText (value);
55
 
        char *matchStart, *matchEnd;
56
 
 
57
 
        if (strcmp ("faultString", key) == 0) {
58
 
                *ret = PIANO_RET_ERR;
59
 
                /* find fault identifier in a string like this:
60
 
                 * com.savagebeast.radio.api.protocol.xmlrpc.RadioXmlRpcException:
61
 
                 * 192.168.160.78|1213101717317|AUTH_INVALID_TOKEN|
62
 
                 * Invalid auth token */
63
 
                if ((matchStart = strchr (valueStr, '|')) != NULL) {
64
 
                        if ((matchStart = strchr (matchStart+1, '|')) != NULL) {
65
 
                                if ((matchEnd = strchr (matchStart+1, '|')) != NULL) {
66
 
                                        /* changes text in xml node, but we don't care... */
67
 
                                        *matchEnd = '\0';
68
 
                                        ++matchStart;
69
 
                                        /* translate to our error message system */
70
 
                                        if (strcmp ("AUTH_INVALID_TOKEN", matchStart) == 0) {
71
 
                                                *ret = PIANO_RET_AUTH_TOKEN_INVALID;
72
 
                                        } else if (strcmp ("AUTH_INVALID_USERNAME_PASSWORD",
73
 
                                                        matchStart) == 0) {
74
 
                                                *ret = PIANO_RET_AUTH_USER_PASSWORD_INVALID;
75
 
                                        } else if (strcmp ("LISTENER_NOT_AUTHORIZED",
76
 
                                                        matchStart) == 0) {
77
 
                                                *ret = PIANO_RET_NOT_AUTHORIZED;
78
 
                                        } else if (strcmp ("INCOMPATIBLE_VERSION",
79
 
                                                        matchStart) == 0) {
80
 
                                                *ret = PIANO_RET_PROTOCOL_INCOMPATIBLE;
81
 
                                        } else if (strcmp ("READONLY_MODE", matchStart) == 0) {
82
 
                                                *ret = PIANO_RET_READONLY_MODE;
83
 
                                        } else if (strcmp ("STATION_CODE_INVALID",
84
 
                                                        matchStart) == 0) {
85
 
                                                *ret = PIANO_RET_STATION_CODE_INVALID;
86
 
                                        } else if (strcmp ("STATION_DOES_NOT_EXIST",
87
 
                                                        matchStart) == 0) {
88
 
                                                *ret = PIANO_RET_STATION_NONEXISTENT;
89
 
                                        } else if (strcmp ("OUT_OF_SYNC", matchStart) == 0) {
90
 
                                                *ret = PIANO_RET_OUT_OF_SYNC;
91
 
                                        } else if (strcmp ("PLAYLIST_END", matchStart) == 0) {
92
 
                                                *ret = PIANO_RET_PLAYLIST_END;
93
 
                                        } else if (strcmp ("QUICKMIX_NOT_PLAYABLE", matchStart) == 0) {
94
 
                                                *ret = PIANO_RET_QUICKMIX_NOT_PLAYABLE;
95
 
                                        } else if (strcmp ("REMOVING_TOO_MANY_SEEDS", matchStart) == 0) {
96
 
                                                *ret = PIANO_RET_REMOVING_TOO_MANY_SEEDS;
97
 
                                        } else {
98
 
                                                *ret = PIANO_RET_ERR;
99
 
                                                printf (PACKAGE ": Unknown error %s in %s\n",
100
 
                                                                matchStart, valueStr);
101
 
                                        }
102
 
                                }
103
 
                        }
104
 
                }
105
 
        } else if (strcmp ("faultCode", key) == 0) {
106
 
                /* some errors can only be identified by looking at their id */
107
 
                /* detect pandora's ip restriction */
108
 
                if (strcmp ("12", valueStr) == 0) {
109
 
                        *ret = PIANO_RET_IP_REJECTED;
110
 
                }
111
 
        }
112
 
}
113
 
 
114
 
/*      check whether pandora returned an error or not
115
 
 *      @param document root of xml doc
116
 
 *      @return _RET_OK or fault code (_RET_*)
117
 
 */
118
 
static PianoReturn_t PianoXmlIsFault (ezxml_t xmlDoc) {
119
 
        PianoReturn_t ret;
120
 
 
121
 
        if ((xmlDoc = ezxml_child (xmlDoc, "fault")) != NULL) {
122
 
                xmlDoc = ezxml_get (xmlDoc, "value", 0, "struct", -1);
123
 
                PianoXmlStructParser (xmlDoc, PianoXmlIsFaultCb, &ret);
124
 
                return ret;
125
 
        }
126
 
        return PIANO_RET_OK;
127
 
}
128
 
 
129
 
/*      parses things like this:
130
 
 *      <struct>
131
 
 *              <member>
132
 
 *                      <name />
133
 
 *                      <value />
134
 
 *              </member>
135
 
 *              <!-- ... -->
136
 
 *      </struct>
137
 
 *      @param xml node named "struct" (or containing a similar structure)
138
 
 *      @param who wants to use this data? callback: content of <name> as
139
 
 *                      string, content of <value> as xmlNode (may contain other nodes
140
 
 *                      or text), additional data used by callback(); don't forget
141
 
 *                      to *copy* data taken from <name> or <value> as they will be
142
 
 *                      freed soon
143
 
 *      @param extra data for callback
144
 
 */
145
 
static void PianoXmlStructParser (const ezxml_t structRoot,
146
 
                void (*callback) (const char *, const ezxml_t, void *), void *data) {
147
 
        ezxml_t curNode, keyNode, valueNode;
148
 
        char *key;
149
 
 
150
 
        /* get all <member> nodes */
151
 
    for (curNode = ezxml_child (structRoot, "member"); curNode; curNode = curNode->next) {
152
 
                /* reset variables */
153
 
                key = NULL;
154
 
                valueNode = keyNode = NULL;
155
 
 
156
 
                keyNode = ezxml_child (curNode, "name");
157
 
                if (keyNode != NULL) {
158
 
                        key = ezxml_txt (keyNode);
159
 
                }
160
 
 
161
 
                valueNode = ezxml_child (curNode, "value");
162
 
                /* this will ignore empty <value /> nodes, but well... */
163
 
                if (*key != '\0' && valueNode != NULL) {
164
 
                        (*callback) ((char *) key, valueNode, data);
165
 
                }
166
 
        }
167
 
}
168
 
 
169
 
/*      create xml parser from string
170
 
 *      @param xml document
171
 
 *      @param returns document pointer (needed to free memory later)
172
 
 *      @param returns document root
173
 
 *      @return _OK or error
174
 
 */
175
 
static PianoReturn_t PianoXmlInitDoc (char *xmlStr, ezxml_t *xmlDoc) {
176
 
        PianoReturn_t ret;
177
 
 
178
 
        if ((*xmlDoc = ezxml_parse_str (xmlStr, strlen (xmlStr))) == NULL) {
179
 
                return PIANO_RET_XML_INVALID;
180
 
        }
181
 
 
182
 
        if ((ret = PianoXmlIsFault (*xmlDoc)) != PIANO_RET_OK) {
183
 
                ezxml_free (*xmlDoc);
184
 
                return ret;
185
 
        }
186
 
 
187
 
        return PIANO_RET_OK;
188
 
}
189
 
 
190
 
/*      get text from <value> nodes; some of them have <boolean>, <string>
191
 
 *      or <int> subnodes, just ignore them
192
 
 *      @param xml node <value>
193
 
 */
194
 
static char *PianoXmlGetNodeText (const ezxml_t node) {
195
 
        char *retTxt = NULL;
196
 
 
197
 
        retTxt = ezxml_txt (node);
198
 
        /* no text => empty string */
199
 
        if (*retTxt == '\0') {
200
 
                retTxt = ezxml_txt (node->child);
201
 
        }
202
 
        return retTxt;
203
 
}
204
 
 
205
 
/*      structParser callback; writes userinfo to PianoUserInfo structure
206
 
 *      @param value identifier
207
 
 *      @param value node
208
 
 *      @param pointer to userinfo structure
209
 
 *      @return nothing
210
 
 */
211
 
static void PianoXmlParseUserinfoCb (const char *key, const ezxml_t value,
212
 
                void *data) {
213
 
        PianoUserInfo_t *user = data;
214
 
        char *valueStr = PianoXmlGetNodeText (value);
215
 
 
216
 
        if (strcmp ("webAuthToken", key) == 0) {
217
 
                user->webAuthToken = strdup (valueStr);
218
 
        } else if (strcmp ("authToken", key) == 0) {
219
 
                user->authToken = strdup (valueStr);
220
 
        } else if (strcmp ("listenerId", key) == 0) {
221
 
                user->listenerId = strdup (valueStr);
222
 
        }
223
 
}
224
 
 
225
 
static void PianoXmlParseStationsCb (const char *key, const ezxml_t value,
226
 
                void *data) {
227
 
        PianoStation_t *station = data;
228
 
        char *valueStr = PianoXmlGetNodeText (value);
229
 
 
230
 
        if (strcmp ("stationName", key) == 0) {
231
 
                station->name = strdup (valueStr);
232
 
        } else if (strcmp ("stationId", key) == 0) {
233
 
                station->id = strdup (valueStr);
234
 
        } else if (strcmp ("isQuickMix", key) == 0) {
235
 
                station->isQuickMix = (strcmp (valueStr, "1") == 0);
236
 
        } else if (strcmp ("isCreator", key) == 0) {
237
 
                station->isCreator = (strcmp (valueStr, "1") == 0);
238
 
        }
239
 
}
240
 
 
241
 
static void PianoXmlParsePlaylistCb (const char *key, const ezxml_t value,
242
 
                void *data) {
243
 
        PianoSong_t *song = data;
244
 
        char *valueStr = PianoXmlGetNodeText (value);
245
 
 
246
 
        if (strcmp ("audioURL", key) == 0) {
247
 
                /* last 48 chars of audioUrl are encrypted, but they put the key
248
 
                 * into the door's lock... */
249
 
                const char urlTailN = 48;
250
 
                const size_t valueStrN = strlen (valueStr);
251
 
                char *urlTail = NULL,
252
 
                                *urlTailCrypted = &valueStr[valueStrN - urlTailN];
253
 
 
254
 
                /* don't try to decrypt if string is too short (=> invalid memory
255
 
                 * reads/writes) */
256
 
                if (valueStrN > urlTailN &&
257
 
                                (urlTail = PianoDecryptString (urlTailCrypted)) != NULL) {
258
 
                        if ((song->audioUrl = calloc (valueStrN + 1,
259
 
                                        sizeof (*song->audioUrl))) != NULL) {
260
 
                                memcpy (song->audioUrl, valueStr, valueStrN - urlTailN);
261
 
                                /* FIXME: the key seems to be broken... so ignore 8 x 0x08
262
 
                                 * postfix; urlTailN/2 because the encrypted hex string is now
263
 
                                 * decoded */
264
 
                                memcpy (&song->audioUrl[valueStrN - urlTailN], urlTail,
265
 
                                                urlTailN/2 - 8);
266
 
                        }
267
 
                        free (urlTail);
268
 
                }
269
 
        } else if (strcmp ("artRadio", key) == 0) {
270
 
                song->coverArt = strdup (valueStr);
271
 
        } else if (strcmp ("artistSummary", key) == 0) {
272
 
                song->artist = strdup (valueStr);
273
 
        } else if (strcmp ("musicId", key) == 0) {
274
 
                song->musicId = strdup (valueStr);
275
 
        } else if (strcmp ("userSeed", key) == 0) {
276
 
                song->userSeed = strdup (valueStr);
277
 
        } else if (strcmp ("songTitle", key) == 0) {
278
 
                song->title = strdup (valueStr);
279
 
        } else if (strcmp ("rating", key) == 0) {
280
 
                if (strcmp (valueStr, "1") == 0) {
281
 
                        song->rating = PIANO_RATE_LOVE;
282
 
                } else {
283
 
                        song->rating = PIANO_RATE_NONE;
284
 
                }
285
 
        } else if (strcmp ("isPositive", key) == 0) {
286
 
                if (strcmp (valueStr, "1") == 0) {
287
 
                        song->rating = PIANO_RATE_LOVE;
288
 
                } else {
289
 
                        song->rating = PIANO_RATE_BAN;
290
 
                }
291
 
        } else if (strcmp ("stationId", key) == 0) {
292
 
                song->stationId = strdup (valueStr);
293
 
        } else if (strcmp ("albumTitle", key) == 0) {
294
 
                song->album = strdup (valueStr);
295
 
        } else if (strcmp ("fileGain", key) == 0) {
296
 
                song->fileGain = atof (valueStr);
297
 
        } else if (strcmp ("audioEncoding", key) == 0) {
298
 
                if (strcmp (valueStr, "aacplus") == 0) {
299
 
                        song->audioFormat = PIANO_AF_AACPLUS;
300
 
                } else if (strcmp (valueStr, "mp3") == 0) {
301
 
                        song->audioFormat = PIANO_AF_MP3;
302
 
                } else if (strcmp (valueStr, "mp3-hifi") == 0) {
303
 
                        song->audioFormat = PIANO_AF_MP3_HI;
304
 
                }
305
 
        } else if (strcmp ("artistMusicId", key) == 0) {
306
 
                song->artistMusicId = strdup (valueStr);
307
 
        } else if (strcmp ("feedbackId", key) == 0) {
308
 
                song->feedbackId = strdup (valueStr);
309
 
        } else if (strcmp ("songDetailURL", key) == 0) {
310
 
                song->detailUrl = strdup (valueStr);
311
 
        } else if (strcmp ("trackToken", key) == 0) {
312
 
                song->trackToken = strdup (valueStr);
313
 
        }
314
 
}
315
 
 
316
 
/*      parses userinfos sent by pandora as login response
317
 
 *      @param piano handle
318
 
 *      @param utf-8 string
319
 
 *      @return _RET_OK or error
320
 
 */
321
 
PianoReturn_t PianoXmlParseUserinfo (PianoHandle_t *ph, char *xml) {
322
 
        ezxml_t xmlDoc, structNode;
323
 
        PianoReturn_t ret;
324
 
 
325
 
        if ((ret = PianoXmlInitDoc (xml, &xmlDoc)) != PIANO_RET_OK) {
326
 
                return ret;
327
 
        }
328
 
 
329
 
        /* <methodResponse> <params> <param> <value> <struct> */
330
 
        structNode = ezxml_get (xmlDoc, "params", 0, "param", 0, "value", 0, "struct", -1);
331
 
        PianoXmlStructParser (structNode, PianoXmlParseUserinfoCb, &ph->user);
332
 
 
333
 
        ezxml_free (xmlDoc);
334
 
 
335
 
        return PIANO_RET_OK;
336
 
}
337
 
 
338
 
static void PianoXmlParseQuickMixStationsCb (const char *key, const ezxml_t value,
339
 
                void *data) {
340
 
        char ***retIds = data;
341
 
        char **ids = NULL;
342
 
        size_t idsN = 0;
343
 
        ezxml_t curNode;
344
 
 
345
 
        if (strcmp ("quickMixStationIds", key) == 0) {
346
 
                for (curNode = ezxml_child (ezxml_get (value, "array", 0, "data", -1), "value");
347
 
                                curNode; curNode = curNode->next) {
348
 
                        idsN++;
349
 
                        if (ids == NULL) {
350
 
                                if ((ids = calloc (idsN, sizeof (*ids))) == NULL) {
351
 
                                        *retIds = NULL;
352
 
                                        return;
353
 
                                }
354
 
                        } else {
355
 
                                /* FIXME: memory leak (on failure) */
356
 
                                if ((ids = realloc (ids, idsN * sizeof (*ids))) == NULL) {
357
 
                                        *retIds = NULL;
358
 
                                        return;
359
 
                                }
360
 
                        }
361
 
                        ids[idsN-1] = strdup (PianoXmlGetNodeText (curNode));
362
 
                }
363
 
                /* append NULL: list ends here */
364
 
                idsN++;
365
 
                /* FIXME: copy&waste */
366
 
                if (ids == NULL) {
367
 
                        if ((ids = calloc (idsN, sizeof (*ids))) == NULL) {
368
 
                                *retIds = NULL;
369
 
                                return;
370
 
                        }
371
 
                } else {
372
 
                        if ((ids = realloc (ids, idsN * sizeof (*ids))) == NULL) {
373
 
                                *retIds = NULL;
374
 
                                return;
375
 
                        }
376
 
                }
377
 
                ids[idsN-1] = NULL;
378
 
 
379
 
                *retIds = ids;
380
 
        }
381
 
}
382
 
 
383
 
/*      parse stations returned by pandora
384
 
 *      @param piano handle
385
 
 *      @param xml returned by pandora
386
 
 *      @return _RET_OK or error
387
 
 */
388
 
PianoReturn_t PianoXmlParseStations (PianoHandle_t *ph, char *xml) {
389
 
        ezxml_t xmlDoc, dataNode;
390
 
        PianoReturn_t ret;
391
 
        char **quickMixIds = NULL, **curQuickMixId = NULL;
392
 
 
393
 
        if ((ret = PianoXmlInitDoc (xml, &xmlDoc)) != PIANO_RET_OK) {
394
 
                return ret;
395
 
        }
396
 
 
397
 
        dataNode = ezxml_get (xmlDoc, "params", 0, "param", 0, "value", 0, "array",
398
 
                        0, "data", -1);
399
 
 
400
 
        for (dataNode = ezxml_child (dataNode, "value"); dataNode;
401
 
                        dataNode = dataNode->next) {
402
 
                PianoStation_t *tmpStation;
403
 
 
404
 
                if ((tmpStation = calloc (1, sizeof (*tmpStation))) == NULL) {
405
 
                        ezxml_free (xmlDoc);
406
 
                        return PIANO_RET_OUT_OF_MEMORY;
407
 
                }
408
 
 
409
 
                PianoXmlStructParser (ezxml_child (dataNode, "struct"),
410
 
                                PianoXmlParseStationsCb, tmpStation);
411
 
 
412
 
                /* get stations selected for quickmix */
413
 
                if (tmpStation->isQuickMix) {
414
 
                        PianoXmlStructParser (ezxml_child (dataNode, "struct"),
415
 
                                        PianoXmlParseQuickMixStationsCb, &quickMixIds);
416
 
                }
417
 
                /* start new linked list or append */
418
 
                if (ph->stations == NULL) {
419
 
                        ph->stations = tmpStation;
420
 
                } else {
421
 
                        PianoStation_t *curStation = ph->stations;
422
 
                        while (curStation->next != NULL) {
423
 
                                curStation = curStation->next;
424
 
                        }
425
 
                        curStation->next = tmpStation;
426
 
                }
427
 
        }
428
 
        /* set quickmix flags after all stations are read */
429
 
        if (quickMixIds != NULL) {
430
 
                curQuickMixId = quickMixIds;
431
 
                while (*curQuickMixId != NULL) {
432
 
                        PianoStation_t *curStation = PianoFindStationById (ph->stations,
433
 
                                        *curQuickMixId);
434
 
                        if (curStation != NULL) {
435
 
                                curStation->useQuickMix = 1;
436
 
                        }
437
 
                        free (*curQuickMixId);
438
 
                        curQuickMixId++;
439
 
                }
440
 
                free (quickMixIds);
441
 
        }
442
 
 
443
 
        ezxml_free (xmlDoc);
444
 
 
445
 
        return PIANO_RET_OK;
446
 
}
447
 
 
448
 
/*      parse "create station" answer (it returns a new station structure)
449
 
 *      @param piano handle
450
 
 *      @param xml document
451
 
 *      @return nothing yet
452
 
 */
453
 
PianoReturn_t PianoXmlParseCreateStation (PianoHandle_t *ph, char *xml) {
454
 
        ezxml_t xmlDoc, dataNode;
455
 
        PianoStation_t *tmpStation;
456
 
        PianoReturn_t ret;
457
 
 
458
 
        if ((ret = PianoXmlInitDoc (xml, &xmlDoc)) != PIANO_RET_OK) {
459
 
                return ret;
460
 
        }
461
 
 
462
 
        dataNode = ezxml_get (xmlDoc, "params", 0, "param", 0, "value", 0, "struct", -1);
463
 
 
464
 
        if ((tmpStation = calloc (1, sizeof (*tmpStation))) == NULL) {
465
 
                ezxml_free (xmlDoc);
466
 
                return PIANO_RET_OUT_OF_MEMORY;
467
 
        }
468
 
        PianoXmlStructParser (dataNode, PianoXmlParseStationsCb, tmpStation);
469
 
        /* FIXME: copy & waste */
470
 
        /* start new linked list or append */
471
 
        if (ph->stations == NULL) {
472
 
                ph->stations = tmpStation;
473
 
        } else {
474
 
                PianoStation_t *curStation = ph->stations;
475
 
                while (curStation->next != NULL) {
476
 
                        curStation = curStation->next;
477
 
                }
478
 
                curStation->next = tmpStation;
479
 
        }
480
 
        
481
 
        ezxml_free (xmlDoc);
482
 
 
483
 
        return PIANO_RET_OK;
484
 
}
485
 
 
486
 
/*      parse "add seed" answer, nearly the same as ParseCreateStation
487
 
 *      @param piano handle
488
 
 *      @param xml document
489
 
 *      @param update this station
490
 
 */
491
 
PianoReturn_t PianoXmlParseAddSeed (PianoHandle_t *ph, char *xml,
492
 
                PianoStation_t *station) {
493
 
        ezxml_t xmlDoc, dataNode;
494
 
        PianoReturn_t ret;
495
 
 
496
 
        if ((ret = PianoXmlInitDoc (xml, &xmlDoc)) != PIANO_RET_OK) {
497
 
                return ret;
498
 
        }
499
 
 
500
 
        dataNode = ezxml_get (xmlDoc, "params", 0, "param", 0, "value", 0, "struct", -1);
501
 
        PianoDestroyStation (station);
502
 
        PianoXmlStructParser (dataNode, PianoXmlParseStationsCb, station);
503
 
        
504
 
        ezxml_free (xmlDoc);
505
 
 
506
 
        return PIANO_RET_OK;
507
 
}
508
 
 
509
 
static PianoReturn_t PianoXmlParsePlaylistStruct (ezxml_t xml,
510
 
                PianoSong_t **retSong) {
511
 
        PianoSong_t *playlist = *retSong, *tmpSong;
512
 
        
513
 
        if ((tmpSong = calloc (1, sizeof (*tmpSong))) == NULL) {
514
 
                return PIANO_RET_OUT_OF_MEMORY;
515
 
        }
516
 
 
517
 
        PianoXmlStructParser (ezxml_child (xml, "struct"), PianoXmlParsePlaylistCb,
518
 
                        tmpSong);
519
 
        /* begin linked list or append */
520
 
        if (playlist == NULL) {
521
 
                playlist = tmpSong;
522
 
        } else {
523
 
                PianoSong_t *curSong = playlist;
524
 
                while (curSong->next != NULL) {
525
 
                        curSong = curSong->next;
526
 
                }
527
 
                curSong->next = tmpSong;
528
 
        }
529
 
 
530
 
        *retSong = playlist;
531
 
 
532
 
        return PIANO_RET_OK;
533
 
}
534
 
 
535
 
/*      parses playlist; used when searching too
536
 
 *      @param piano handle
537
 
 *      @param xml document
538
 
 *      @param return: playlist
539
 
 */
540
 
PianoReturn_t PianoXmlParsePlaylist (PianoHandle_t *ph, char *xml,
541
 
                PianoSong_t **retPlaylist) {
542
 
        ezxml_t xmlDoc, dataNode;
543
 
        PianoReturn_t ret = PIANO_RET_OK;
544
 
 
545
 
        if ((ret = PianoXmlInitDoc (xml, &xmlDoc)) != PIANO_RET_OK) {
546
 
                return ret;
547
 
        }
548
 
 
549
 
        dataNode = ezxml_get (xmlDoc, "params", 0, "param", 0, "value", 0, "array",
550
 
                        0, "data", -1);
551
 
 
552
 
        for (dataNode = ezxml_child (dataNode, "value"); dataNode;
553
 
                        dataNode = dataNode->next) {
554
 
                if ((ret = PianoXmlParsePlaylistStruct (dataNode, retPlaylist)) !=
555
 
                                PIANO_RET_OK) {
556
 
                        break;
557
 
                }
558
 
        }
559
 
 
560
 
        ezxml_free (xmlDoc);
561
 
 
562
 
        return ret;
563
 
}
564
 
 
565
 
/*      check for exception only
566
 
 *      @param xml string
567
 
 *      @return _OK or error
568
 
 */
569
 
PianoReturn_t PianoXmlParseSimple (char *xml) {
570
 
        ezxml_t xmlDoc;
571
 
        PianoReturn_t ret;
572
 
 
573
 
        if ((ret = PianoXmlInitDoc (xml, &xmlDoc)) != PIANO_RET_OK) {
574
 
                return ret;
575
 
        }
576
 
 
577
 
        ezxml_free (xmlDoc);
578
 
 
579
 
        return ret;
580
 
}
581
 
 
582
 
/*      xml struct parser callback, used in PianoXmlParseSearchCb
583
 
 */
584
 
static void PianoXmlParseSearchArtistCb (const char *key, const ezxml_t value,
585
 
                void *data) {
586
 
        PianoArtist_t *artist = data;
587
 
        char *valueStr = PianoXmlGetNodeText (value);
588
 
 
589
 
        if (strcmp ("artistName", key) == 0) {
590
 
                artist->name = strdup (valueStr);
591
 
        } else if (strcmp ("musicId", key) == 0) {
592
 
                artist->musicId = strdup (valueStr);
593
 
        }
594
 
}
595
 
 
596
 
/*      callback for xml struct parser used in PianoXmlParseSearch, "switch" for
597
 
 *      PianoXmlParseSearchArtistCb and PianoXmlParsePlaylistCb
598
 
 */
599
 
static void PianoXmlParseSearchCb (const char *key, const ezxml_t value,
600
 
                void *data) {
601
 
        PianoSearchResult_t *searchResult = data;
602
 
        ezxml_t curNode;
603
 
 
604
 
        if (strcmp ("artists", key) == 0) {
605
 
                /* skip <value><array><data> */
606
 
                for (curNode = ezxml_child (ezxml_get (value, "array", 0, "data", -1), "value");
607
 
                                curNode; curNode = curNode->next) {
608
 
                        PianoArtist_t *artist;
609
 
                        
610
 
                        if ((artist = calloc (1, sizeof (*artist))) == NULL) {
611
 
                                /* fail silently */
612
 
                                break;
613
 
                        }
614
 
 
615
 
                        memset (artist, 0, sizeof (*artist));
616
 
 
617
 
                        PianoXmlStructParser (ezxml_child (curNode, "struct"),
618
 
                                        PianoXmlParseSearchArtistCb, artist);
619
 
 
620
 
                        /* add result to linked list */
621
 
                        if (searchResult->artists == NULL) {
622
 
                                searchResult->artists = artist;
623
 
                        } else {
624
 
                                PianoArtist_t *curArtist = searchResult->artists;
625
 
                                while (curArtist->next != NULL) {
626
 
                                        curArtist = curArtist->next;
627
 
                                }
628
 
                                curArtist->next = artist;
629
 
                        }
630
 
                }
631
 
        } else if (strcmp ("songs", key) == 0) {
632
 
                for (curNode = ezxml_child (ezxml_get (value, "array", 0, "data", -1), "value");
633
 
                                curNode; curNode = curNode->next) {
634
 
                        if (PianoXmlParsePlaylistStruct (curNode, &searchResult->songs) !=
635
 
                                        PIANO_RET_OK) {
636
 
                                break;
637
 
                        }
638
 
                }
639
 
        }
640
 
}
641
 
 
642
 
/*      parse search result; searchResult is nulled before use
643
 
 *      @param xml document
644
 
 *      @param returns search result
645
 
 *      @return nothing yet
646
 
 */
647
 
PianoReturn_t PianoXmlParseSearch (char *xml,
648
 
                PianoSearchResult_t *searchResult) {
649
 
        ezxml_t xmlDoc, dataNode;
650
 
        PianoReturn_t ret;
651
 
 
652
 
        if ((ret = PianoXmlInitDoc (xml, &xmlDoc)) != PIANO_RET_OK) {
653
 
                return ret;
654
 
        }
655
 
        
656
 
        dataNode = ezxml_get (xmlDoc, "params", 0, "param", 0, "value", 0, "struct", -1);
657
 
        /* we need a "clean" search result (with null pointers) */
658
 
        memset (searchResult, 0, sizeof (*searchResult));
659
 
        PianoXmlStructParser (dataNode, PianoXmlParseSearchCb, searchResult);
660
 
 
661
 
        ezxml_free (xmlDoc);
662
 
 
663
 
        return PIANO_RET_OK;
664
 
}
665
 
 
666
 
/*      FIXME: copy&waste (PianoXmlParseSearch)
667
 
 */
668
 
PianoReturn_t PianoXmlParseSeedSuggestions (char *xml,
669
 
                PianoSearchResult_t *searchResult) {
670
 
        ezxml_t xmlDoc, dataNode;
671
 
        PianoReturn_t ret;
672
 
 
673
 
        if ((ret = PianoXmlInitDoc (xml, &xmlDoc)) != PIANO_RET_OK) {
674
 
                return ret;
675
 
        }
676
 
        
677
 
        dataNode = ezxml_get (xmlDoc, "params", 0, "param", 0, "value", -1);
678
 
        /* we need a "clean" search result (with null pointers) */
679
 
        memset (searchResult, 0, sizeof (*searchResult));
680
 
        /* reuse seach result parser; structure is nearly the same */
681
 
        PianoXmlParseSearchCb ("artists", dataNode, searchResult);
682
 
 
683
 
        ezxml_free (xmlDoc);
684
 
 
685
 
        return PIANO_RET_OK;
686
 
}
687
 
 
688
 
/*      encode reserved xml chars
689
 
 *      TODO: remove and use ezxml_ampencode
690
 
 *      @param encode this
691
 
 *      @return encoded string or NULL
692
 
 */
693
 
char *PianoXmlEncodeString (const char *s) {
694
 
        static const char *replacements[] = {"&&amp;", "'&apos;", "\"&quot;",
695
 
                        "<&lt;", ">&gt;", NULL};
696
 
        const char **r;
697
 
        char *sOut, *sOutCurr, found;
698
 
 
699
 
        if ((sOut = calloc (strlen (s) * 5 + 1, sizeof (*sOut))) == NULL) {
700
 
                return NULL;
701
 
        }
702
 
 
703
 
        sOutCurr = sOut;
704
 
 
705
 
        while (*s != '\0') {
706
 
                r = replacements;
707
 
                found = 0;
708
 
                while (*r != NULL) {
709
 
                        if (*s == *r[0]) {
710
 
                                found = 1;
711
 
                                strcat (sOutCurr, (*r) + 1);
712
 
                                sOutCurr += strlen ((*r) + 1);
713
 
                                break;
714
 
                        }
715
 
                        r++;
716
 
                }
717
 
                if (!found) {
718
 
                        *sOutCurr = *s;
719
 
                        sOutCurr++;
720
 
                }
721
 
                s++;
722
 
        }
723
 
        return sOut;
724
 
}
725
 
 
726
 
PianoReturn_t PianoXmlParseGenreExplorer (PianoHandle_t *ph, char *xml) {
727
 
        ezxml_t xmlDoc, catNode;
728
 
        PianoReturn_t ret;
729
 
 
730
 
    if ((ret = PianoXmlInitDoc (xml, &xmlDoc)) != PIANO_RET_OK) {
731
 
        return ret;
732
 
    }
733
 
 
734
 
        /* get all <member> nodes */
735
 
    for (catNode = ezxml_child (xmlDoc, "category"); catNode;
736
 
                        catNode = catNode->next) {
737
 
                PianoGenreCategory_t *tmpGenreCategory;
738
 
                ezxml_t genreNode;
739
 
 
740
 
                if ((tmpGenreCategory = calloc (1, sizeof (*tmpGenreCategory))) == NULL) {
741
 
                        ezxml_free (xmlDoc);
742
 
                        return PIANO_RET_OUT_OF_MEMORY;
743
 
                }
744
 
 
745
 
                tmpGenreCategory->name = strdup (ezxml_attr (catNode, "categoryName"));
746
 
 
747
 
                /* get genre subnodes */
748
 
                for (genreNode = ezxml_child (catNode, "genre"); genreNode;
749
 
                                genreNode = genreNode->next) {
750
 
                        PianoGenre_t *tmpGenre;
751
 
 
752
 
                        if ((tmpGenre = calloc (1, sizeof (*tmpGenre))) == NULL) {
753
 
                                ezxml_free (xmlDoc);
754
 
                                return PIANO_RET_OUT_OF_MEMORY;
755
 
                        }
756
 
 
757
 
                        /* get genre attributes */
758
 
                        tmpGenre->name = strdup (ezxml_attr (genreNode, "name"));
759
 
                        tmpGenre->musicId = strdup (ezxml_attr (genreNode, "musicId"));
760
 
 
761
 
                        /* append station */
762
 
                        if (tmpGenreCategory->genres == NULL) {
763
 
                                tmpGenreCategory->genres = tmpGenre;
764
 
                        } else {
765
 
                                PianoGenre_t *curGenre =
766
 
                                                tmpGenreCategory->genres;
767
 
                                while (curGenre->next != NULL) {
768
 
                                        curGenre = curGenre->next;
769
 
                                }
770
 
                                curGenre->next = tmpGenre;
771
 
                        }
772
 
                }
773
 
                /* append category */
774
 
                if (ph->genreStations == NULL) {
775
 
                        ph->genreStations = tmpGenreCategory;
776
 
                } else {
777
 
                        PianoGenreCategory_t *curCat = ph->genreStations;
778
 
                        while (curCat->next != NULL) {
779
 
                                curCat = curCat->next;
780
 
                        }
781
 
                        curCat->next = tmpGenreCategory;
782
 
                }
783
 
        }
784
 
 
785
 
        ezxml_free (xmlDoc);
786
 
 
787
 
        return PIANO_RET_OK;
788
 
}
789
 
 
790
 
/*      dummy function, only checks for errors
791
 
 *      @param xml doc
792
 
 *      @return _OK or error
793
 
 */
794
 
PianoReturn_t PianoXmlParseTranformStation (char *xml) {
795
 
        ezxml_t xmlDoc;
796
 
        PianoReturn_t ret;
797
 
 
798
 
        if ((ret = PianoXmlInitDoc (xml, &xmlDoc)) != PIANO_RET_OK) {
799
 
                return ret;
800
 
        }
801
 
        
802
 
        ezxml_free (xmlDoc);
803
 
 
804
 
        return PIANO_RET_OK;
805
 
}
806
 
 
807
 
/*      parses "why did you play ...?" answer
808
 
 *      @param xml
809
 
 *      @param returns the answer
810
 
 *      @return _OK or error
811
 
 */
812
 
PianoReturn_t PianoXmlParseNarrative (char *xml, char **retNarrative) {
813
 
        ezxml_t xmlDoc, dataNode;
814
 
        PianoReturn_t ret;
815
 
 
816
 
        if ((ret = PianoXmlInitDoc (xml, &xmlDoc)) != PIANO_RET_OK) {
817
 
                return ret;
818
 
        }
819
 
 
820
 
        /* <methodResponse> <params> <param> <value> $textnode */
821
 
        dataNode = ezxml_get (xmlDoc, "params", 0, "param", 0, "value", -1);
822
 
        *retNarrative = strdup (ezxml_txt (dataNode));
823
 
 
824
 
        ezxml_free (xmlDoc);
825
 
 
826
 
        return ret;
827
 
}
828
 
 
829
 
/*      seed bag, required because seedId is not part of artist/song struct in
830
 
 *      pandora's xml response
831
 
 */
832
 
struct PianoXmlParseSeedBag {
833
 
        char *seedId;
834
 
        PianoSong_t *song;
835
 
        PianoArtist_t *artist;
836
 
        PianoStation_t *station;
837
 
};
838
 
 
839
 
/*      parse seed struct
840
 
 */
841
 
static void PianoXmlParseSeedCb (const char *key, const ezxml_t value,
842
 
                void *data) {
843
 
        struct PianoXmlParseSeedBag *bag = data;
844
 
 
845
 
        assert (bag != NULL);
846
 
 
847
 
        if (strcmp ("song", key) == 0) {
848
 
                assert (bag->song == NULL);
849
 
 
850
 
                if ((bag->song = calloc (1, sizeof (*bag->song))) == NULL) {
851
 
                        return;
852
 
                }
853
 
 
854
 
                PianoXmlStructParser (ezxml_child (value, "struct"),
855
 
                                PianoXmlParsePlaylistCb, bag->song);
856
 
        } else if (strcmp ("artist", key) == 0) {
857
 
                assert (bag->artist == NULL);
858
 
 
859
 
                if ((bag->artist = calloc (1, sizeof (*bag->artist))) == NULL) {
860
 
                        return;
861
 
                }
862
 
 
863
 
                PianoXmlStructParser (ezxml_child (value, "struct"),
864
 
                                PianoXmlParseSearchArtistCb, bag->artist);
865
 
        } else if (strcmp ("nonGenomeStation", key) == 0) {
866
 
                /* genre stations are "non genome" station seeds */
867
 
                assert (bag->station == NULL);
868
 
 
869
 
                if ((bag->station = calloc (1, sizeof (*bag->station))) == NULL) {
870
 
                        return;
871
 
                }
872
 
 
873
 
                PianoXmlStructParser (ezxml_child (value, "struct"),
874
 
                                PianoXmlParseStationsCb, bag->station);
875
 
        } else if (strcmp ("seedId", key) == 0) {
876
 
                char *valueStr = PianoXmlGetNodeText (value);
877
 
                bag->seedId = strdup (valueStr);
878
 
        }
879
 
}
880
 
 
881
 
/*      parse getStation xml struct
882
 
 */
883
 
static void PianoXmlParseGetStationInfoCb (const char *key, const ezxml_t value,
884
 
                void *data) {
885
 
        PianoStationInfo_t *info = data;
886
 
 
887
 
        if (strcmp ("seeds", key) == 0) {
888
 
                const ezxml_t dataNode = ezxml_get (value, "array", 0, "data", -1);
889
 
                for (ezxml_t seedNode = ezxml_child (dataNode, "value"); seedNode;
890
 
                                        seedNode = seedNode->next) {
891
 
                        struct PianoXmlParseSeedBag bag;
892
 
                        memset (&bag, 0, sizeof (bag));
893
 
 
894
 
                        PianoXmlStructParser (ezxml_child (seedNode, "struct"),
895
 
                                        PianoXmlParseSeedCb, &bag);
896
 
 
897
 
                        assert (bag.song != NULL || bag.artist != NULL ||
898
 
                                        bag.station != NULL);
899
 
 
900
 
                        if (bag.seedId == NULL) {
901
 
                                /* seeds without id are useless */
902
 
                                continue;
903
 
                        }
904
 
 
905
 
                        /* FIXME: copy&waste */
906
 
                        if (bag.song != NULL) {
907
 
                                bag.song->seedId = bag.seedId;
908
 
 
909
 
                                if (info->songSeeds == NULL) {
910
 
                                        info->songSeeds = bag.song;
911
 
                                } else {
912
 
                                        PianoSong_t *curSong = info->songSeeds;
913
 
                                        while (curSong->next != NULL) {
914
 
                                                curSong = curSong->next;
915
 
                                        }
916
 
                                        curSong->next = bag.song;
917
 
                                }
918
 
                        } else if (bag.artist != NULL) {
919
 
                                bag.artist->seedId = bag.seedId;
920
 
 
921
 
                                if (info->artistSeeds == NULL) {
922
 
                                        info->artistSeeds = bag.artist;
923
 
                                } else {
924
 
                                        PianoArtist_t *curSong = info->artistSeeds;
925
 
                                        while (curSong->next != NULL) {
926
 
                                                curSong = curSong->next;
927
 
                                        }
928
 
                                        curSong->next = bag.artist;
929
 
                                }
930
 
                        } else if (bag.station != NULL) {
931
 
                                bag.station->seedId = bag.seedId;
932
 
 
933
 
                                if (info->stationSeeds == NULL) {
934
 
                                        info->stationSeeds = bag.station;
935
 
                                } else {
936
 
                                        PianoStation_t *curStation = info->stationSeeds;
937
 
                                        while (curStation->next != NULL) {
938
 
                                                curStation = curStation->next;
939
 
                                        }
940
 
                                        curStation->next = bag.station;
941
 
                                }
942
 
                        } else {
943
 
                                free (bag.seedId);
944
 
                        }
945
 
                }
946
 
        } else if (strcmp ("feedback", key) == 0) {
947
 
                const ezxml_t dataNode = ezxml_get (value, "array", 0, "data", -1);
948
 
                for (ezxml_t feedbackNode = ezxml_child (dataNode, "value"); feedbackNode;
949
 
                                        feedbackNode = feedbackNode->next) {
950
 
                        if (PianoXmlParsePlaylistStruct (feedbackNode, &info->feedback) !=
951
 
                                        PIANO_RET_OK) {
952
 
                                break;
953
 
                        }
954
 
                }
955
 
        }
956
 
}
957
 
 
958
 
/*      parse getStation response
959
 
 */
960
 
PianoReturn_t PianoXmlParseGetStationInfo (char *xml,
961
 
                PianoStationInfo_t *stationInfo) {
962
 
        ezxml_t xmlDoc, dataNode;
963
 
        PianoReturn_t ret;
964
 
 
965
 
        if ((ret = PianoXmlInitDoc (xml, &xmlDoc)) != PIANO_RET_OK) {
966
 
                return ret;
967
 
        }
968
 
        
969
 
        dataNode = ezxml_get (xmlDoc, "params", 0, "param", 0, "value", 0, "struct", -1);
970
 
        PianoXmlStructParser (dataNode, PianoXmlParseGetStationInfoCb, stationInfo);
971
 
 
972
 
        ezxml_free (xmlDoc);
973
 
 
974
 
        return PIANO_RET_OK;
975
 
}
976