~ubuntu-branches/ubuntu/wily/pianobar/wily-proposed

« back to all changes in this revision

Viewing changes to .pc/protocol30.patch/src/libpiano/piano.c

  • Committer: Bazaar Package Importer
  • Author(s): Luke Faraone
  • Date: 2011-07-09 10:03:03 UTC
  • mfrom: (1.3.4 upstream)
  • Revision ID: james.westby@ubuntu.com-20110709100303-m5heshhcrbrv3w3n
Tags: 2011.07.09-1
* New upstream version. 
  - XMLRPC api version bump (v31) (closes: #633345, LP: #807860)
* Update debian/watch for new location.
* Update symbols file.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
Copyright (c) 2008-2010
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
 
#define _BSD_SOURCE /* required by strdup() */
25
 
 
26
 
#include <stdio.h>
27
 
#include <string.h>
28
 
#include <stdlib.h>
29
 
#include <time.h>
30
 
#include <assert.h>
31
 
#include <stdint.h>
32
 
 
33
 
/* needed for urlencode */
34
 
#include <waitress.h>
35
 
 
36
 
#include "piano_private.h"
37
 
#include "piano.h"
38
 
#include "xml.h"
39
 
#include "crypt.h"
40
 
#include "config.h"
41
 
 
42
 
#define PIANO_PROTOCOL_VERSION "29"
43
 
#define PIANO_RPC_HOST "www.pandora.com"
44
 
#define PIANO_RPC_PORT "80"
45
 
#define PIANO_RPC_PATH "/radio/xmlrpc/v" PIANO_PROTOCOL_VERSION "?"
46
 
#define PIANO_SEND_BUFFER_SIZE 10000
47
 
 
48
 
/*      initialize piano handle
49
 
 *      @param piano handle
50
 
 *      @return nothing
51
 
 */
52
 
void PianoInit (PianoHandle_t *ph) {
53
 
        memset (ph, 0, sizeof (*ph));
54
 
 
55
 
        /* route-id seems to be random. we're using time anyway... */
56
 
        snprintf (ph->routeId, sizeof (ph->routeId), "%07luP",
57
 
                        (unsigned long) time (NULL) % 10000000);
58
 
}
59
 
 
60
 
/*      free complete search result
61
 
 *      @public yes
62
 
 *      @param search result
63
 
 */
64
 
void PianoDestroySearchResult (PianoSearchResult_t *searchResult) {
65
 
        PianoArtist_t *curArtist, *lastArtist;
66
 
        PianoSong_t *curSong, *lastSong;
67
 
 
68
 
        curArtist = searchResult->artists;
69
 
        while (curArtist != NULL) {
70
 
                free (curArtist->name);
71
 
                free (curArtist->musicId);
72
 
                lastArtist = curArtist;
73
 
                curArtist = curArtist->next;
74
 
                free (lastArtist);
75
 
        }
76
 
 
77
 
        curSong = searchResult->songs;
78
 
        while (curSong != NULL) {
79
 
                free (curSong->title);
80
 
                free (curSong->artist);
81
 
                free (curSong->musicId);
82
 
                lastSong = curSong;
83
 
                curSong = curSong->next;
84
 
                free (lastSong);
85
 
        }
86
 
}
87
 
 
88
 
/*      free single station
89
 
 *      @param station
90
 
 */
91
 
void PianoDestroyStation (PianoStation_t *station) {
92
 
        free (station->name);
93
 
        free (station->id);
94
 
        memset (station, 0, sizeof (station));
95
 
}
96
 
 
97
 
/*      free complete station list
98
 
 *      @param piano handle
99
 
 */
100
 
void PianoDestroyStations (PianoStation_t *stations) {
101
 
        PianoStation_t *curStation, *lastStation;
102
 
 
103
 
        curStation = stations;
104
 
        while (curStation != NULL) {
105
 
                lastStation = curStation;
106
 
                curStation = curStation->next;
107
 
                PianoDestroyStation (lastStation);
108
 
                free (lastStation);
109
 
        }
110
 
}
111
 
 
112
 
/* FIXME: copy & waste */
113
 
/*      free _all_ elements of playlist
114
 
 *      @param piano handle
115
 
 *      @return nothing
116
 
 */
117
 
void PianoDestroyPlaylist (PianoSong_t *playlist) {
118
 
        PianoSong_t *curSong, *lastSong;
119
 
 
120
 
        curSong = playlist;
121
 
        while (curSong != NULL) {
122
 
                free (curSong->audioUrl);
123
 
                free (curSong->coverArt);
124
 
                free (curSong->artist);
125
 
                free (curSong->musicId);
126
 
                free (curSong->title);
127
 
                free (curSong->userSeed);
128
 
                free (curSong->stationId);
129
 
                free (curSong->album);
130
 
                free (curSong->artistMusicId);
131
 
                lastSong = curSong;
132
 
                curSong = curSong->next;
133
 
                free (lastSong);
134
 
        }
135
 
}
136
 
 
137
 
/*      destroy genre linked list
138
 
 */
139
 
void PianoDestroyGenres (PianoGenre_t *genres) {
140
 
        PianoGenre_t *curGenre, *lastGenre;
141
 
 
142
 
        curGenre = genres;
143
 
        while (curGenre != NULL) {
144
 
                free (curGenre->name);
145
 
                free (curGenre->musicId);
146
 
                lastGenre = curGenre;
147
 
                curGenre = curGenre->next;
148
 
                free (lastGenre);
149
 
        }
150
 
}
151
 
 
152
 
/*      destroy user information
153
 
 */
154
 
void PianoDestroyUserInfo (PianoUserInfo_t *user) {
155
 
        free (user->webAuthToken);
156
 
        free (user->authToken);
157
 
        free (user->listenerId);
158
 
}
159
 
 
160
 
/*      frees the whole piano handle structure
161
 
 *      @param piano handle
162
 
 *      @return nothing
163
 
 */
164
 
void PianoDestroy (PianoHandle_t *ph) {
165
 
        PianoDestroyUserInfo (&ph->user);
166
 
        PianoDestroyStations (ph->stations);
167
 
        /* destroy genre stations */
168
 
        PianoGenreCategory_t *curGenreCat = ph->genreStations, *lastGenreCat;
169
 
        while (curGenreCat != NULL) {
170
 
                PianoDestroyGenres (curGenreCat->genres);
171
 
                free (curGenreCat->name);
172
 
                lastGenreCat = curGenreCat;
173
 
                curGenreCat = curGenreCat->next;
174
 
                free (lastGenreCat);
175
 
        }
176
 
        memset (ph, 0, sizeof (*ph));
177
 
}
178
 
 
179
 
/*      destroy request, free post data. req->responseData is *not* freed here, as
180
 
 *      it might be allocated by something else than malloc!
181
 
 *      @param piano request
182
 
 */
183
 
void PianoDestroyRequest (PianoRequest_t *req) {
184
 
        free (req->postData);
185
 
        memset (req, 0, sizeof (*req));
186
 
}
187
 
 
188
 
/*      convert audio format id to string that can be used in xml requests
189
 
 *      @param format id
190
 
 *      @return constant string
191
 
 */
192
 
static const char *PianoAudioFormatToString (PianoAudioFormat_t format) {
193
 
        switch (format) {
194
 
                case PIANO_AF_AACPLUS:
195
 
                        return "aacplus";
196
 
                        break;
197
 
 
198
 
                case PIANO_AF_MP3:
199
 
                        return "mp3";
200
 
                        break;
201
 
 
202
 
                case PIANO_AF_MP3_HI:
203
 
                        return "mp3-hifi";
204
 
                        break;
205
 
 
206
 
                default:
207
 
                        return NULL;
208
 
                        break;
209
 
        }
210
 
}
211
 
 
212
 
/*      prepare piano request (initializes request type, urlpath and postData)
213
 
 *      @param piano handle
214
 
 *      @param request structure
215
 
 *      @param request type
216
 
 */
217
 
PianoReturn_t PianoRequest (PianoHandle_t *ph, PianoRequest_t *req,
218
 
                PianoRequestType_t type) {
219
 
        char xmlSendBuf[PIANO_SEND_BUFFER_SIZE];
220
 
        /* corrected timestamp */
221
 
        time_t timestamp = time (NULL) - ph->timeOffset;
222
 
 
223
 
        assert (ph != NULL);
224
 
        assert (req != NULL);
225
 
 
226
 
        req->type = type;
227
 
 
228
 
        switch (req->type) {
229
 
                case PIANO_REQUEST_LOGIN: {
230
 
                        /* authenticate user */
231
 
                        PianoRequestDataLogin_t *logindata = req->data;
232
 
 
233
 
                        assert (logindata != NULL);
234
 
 
235
 
                        switch (logindata->step) {
236
 
                                case 0:
237
 
                                        snprintf (xmlSendBuf, sizeof (xmlSendBuf), 
238
 
                                                        "<?xml version=\"1.0\"?><methodCall>"
239
 
                                                        "<methodName>misc.sync</methodName>"
240
 
                                                        "<params></params></methodCall>");
241
 
                                        snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
242
 
                                                        "rid=%s&method=sync", ph->routeId);
243
 
                                        break;
244
 
 
245
 
                                case 1:
246
 
                                        snprintf (xmlSendBuf, sizeof (xmlSendBuf), 
247
 
                                                        "<?xml version=\"1.0\"?><methodCall>"
248
 
                                                        "<methodName>listener.authenticateListener</methodName>"
249
 
                                                        "<params><param><value><int>%lu</int></value></param>"
250
 
                                                        "<param><value><string>%s</string></value></param>"
251
 
                                                        "<param><value><string>%s</string></value></param>"
252
 
                                                        "</params></methodCall>", (unsigned long) timestamp,
253
 
                                                        logindata->user, logindata->password);
254
 
                                        snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
255
 
                                                        "rid=%s&method=authenticateListener", ph->routeId);
256
 
                                        break;
257
 
                        }
258
 
                        break;
259
 
                }
260
 
 
261
 
                case PIANO_REQUEST_GET_STATIONS:
262
 
                        /* get stations, user must be authenticated */
263
 
                        assert (ph->user.listenerId != NULL);
264
 
 
265
 
                        snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
266
 
                                        "<methodCall><methodName>station.getStations</methodName>"
267
 
                                        "<params><param><value><int>%lu</int></value></param>"
268
 
                                        "<param><value><string>%s</string></value></param>"
269
 
                                        "</params></methodCall>", (unsigned long) timestamp,
270
 
                                        ph->user.authToken);
271
 
                        snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
272
 
                                        "rid=%s&lid=%s&method=getStations", ph->routeId,
273
 
                                        ph->user.listenerId);
274
 
                        break;
275
 
 
276
 
                case PIANO_REQUEST_GET_PLAYLIST: {
277
 
                        /* get playlist for specified station */
278
 
                        PianoRequestDataGetPlaylist_t *reqData = req->data;
279
 
 
280
 
                        assert (reqData != NULL);
281
 
                        assert (reqData->station != NULL);
282
 
                        assert (reqData->station->id != NULL);
283
 
                        assert (reqData->format != PIANO_AF_UNKNOWN);
284
 
 
285
 
                        snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
286
 
                                        "<methodCall><methodName>playlist.getFragment</methodName>"
287
 
                                        "<params><param><value><int>%lu</int></value></param>"
288
 
                                        /* auth token */
289
 
                                        "<param><value><string>%s</string></value></param>"
290
 
                                        /* station id */
291
 
                                        "<param><value><string>%s</string></value></param>"
292
 
                                        /* total listening time */
293
 
                                        "<param><value><string>0</string></value></param>"
294
 
                                        /* time since last session */
295
 
                                        "<param><value><string></string></value></param>"
296
 
                                        /* tracking code */
297
 
                                        "<param><value><string></string></value></param>"
298
 
                                        /* audio format */
299
 
                                        "<param><value><string>%s</string></value></param>"
300
 
                                        /* delta listening time */
301
 
                                        "<param><value><string>0</string></value></param>"
302
 
                                        /* listening timestamp */
303
 
                                        "<param><value><string>0</string></value></param>"
304
 
                                        "</params></methodCall>", (unsigned long) timestamp,
305
 
                                        ph->user.authToken, reqData->station->id,
306
 
                                        PianoAudioFormatToString (reqData->format));
307
 
                        snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
308
 
                                        "rid=%s&lid=%s&method=getFragment&arg1=%s&arg2=0"
309
 
                                        "&arg3=&arg4=&arg5=%s&arg6=0&arg7=0", ph->routeId,
310
 
                                        ph->user.listenerId, reqData->station->id,
311
 
                                        PianoAudioFormatToString (reqData->format));
312
 
                        break;
313
 
                }
314
 
 
315
 
                case PIANO_REQUEST_ADD_FEEDBACK: {
316
 
                        /* low-level, don't use directly (see _RATE_SONG and _MOVE_SONG) */
317
 
                        PianoRequestDataAddFeedback_t *reqData = req->data;
318
 
                        
319
 
                        assert (reqData != NULL);
320
 
                        assert (reqData->stationId != NULL);
321
 
                        assert (reqData->musicId != NULL);
322
 
                        assert (reqData->rating != PIANO_RATE_NONE);
323
 
 
324
 
                        snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
325
 
                                        "<methodCall><methodName>station.addFeedback</methodName>"
326
 
                                        "<params><param><value><int>%lu</int></value></param>"
327
 
                                        /* auth token */
328
 
                                        "<param><value><string>%s</string></value></param>"
329
 
                                        /* station id */
330
 
                                        "<param><value><string>%s</string></value></param>"
331
 
                                        /* music id */
332
 
                                        "<param><value><string>%s</string></value></param>"
333
 
                                        /* user seed */
334
 
                                        "<param><value><string>%s</string></value></param>"
335
 
                                        /* test strategy */
336
 
                                        "<param><value>%u</value></param>"
337
 
                                        /* positive */
338
 
                                        "<param><value><boolean>%i</boolean></value></param>"
339
 
                                        /* "is-creator-quickmix" */
340
 
                                        "<param><value><boolean>0</boolean></value></param>"
341
 
                                        /* song type */
342
 
                                        "<param><value><int>%u</int></value></param>"
343
 
                                        "</params></methodCall>", (unsigned long) timestamp,
344
 
                                        ph->user.authToken, reqData->stationId, reqData->musicId,
345
 
                                        (reqData->userSeed == NULL) ? "" : reqData->userSeed,
346
 
                                        reqData->testStrategy,
347
 
                                        (reqData->rating == PIANO_RATE_LOVE) ? 1 : 0,
348
 
                                        reqData->songType);
349
 
                        snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
350
 
                                        "rid=%s&lid=%s&method=addFeedback&arg1=%s&arg2=%s"
351
 
                                        "&arg3=%s&arg4=%u&arg5=%s&arg6=false&arg7=%u",
352
 
                                        ph->routeId, ph->user.listenerId, reqData->stationId,
353
 
                                        reqData->musicId,
354
 
                                        (reqData->userSeed == NULL) ? "" : reqData->userSeed,
355
 
                                        reqData->testStrategy,
356
 
                                        (reqData->rating == PIANO_RATE_LOVE) ? "true" : "false",
357
 
                                        reqData->songType);
358
 
                        break;
359
 
                }
360
 
 
361
 
                case PIANO_REQUEST_RENAME_STATION: {
362
 
                        /* rename stations */
363
 
                        PianoRequestDataRenameStation_t *reqData = req->data;
364
 
                        char *urlencodedNewName, *xmlencodedNewName;
365
 
 
366
 
                        assert (reqData != NULL);
367
 
                        assert (reqData->station != NULL);
368
 
                        assert (reqData->newName != NULL);
369
 
 
370
 
                        if ((xmlencodedNewName = PianoXmlEncodeString (reqData->newName)) == NULL) {
371
 
                                return PIANO_RET_OUT_OF_MEMORY;
372
 
                        }
373
 
                        urlencodedNewName = WaitressUrlEncode (reqData->newName);
374
 
 
375
 
                        snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
376
 
                                        "<methodCall><methodName>station.setStationName</methodName>"
377
 
                                        "<params><param><value><int>%lu</int></value></param>"
378
 
                                        /* auth token */
379
 
                                        "<param><value><string>%s</string></value></param>"
380
 
                                        /* station id */
381
 
                                        "<param><value><string>%s</string></value></param>"
382
 
                                        /* new name */
383
 
                                        "<param><value><string>%s</string></value></param>"
384
 
                                        "</params></methodCall>", (unsigned long) timestamp,
385
 
                                        ph->user.authToken, reqData->station->id,
386
 
                                        xmlencodedNewName);
387
 
                        snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
388
 
                                        "rid=%s&lid=%s&method=setStationName&arg1=%s&arg2=%s",
389
 
                                        ph->routeId, ph->user.listenerId, reqData->station->id,
390
 
                                        urlencodedNewName);
391
 
 
392
 
                        free (urlencodedNewName);
393
 
                        free (xmlencodedNewName);
394
 
                        break;
395
 
                }
396
 
 
397
 
                case PIANO_REQUEST_DELETE_STATION: {
398
 
                        /* delete station */
399
 
                        PianoStation_t *station = req->data;
400
 
 
401
 
                        assert (station != NULL);
402
 
 
403
 
                        snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
404
 
                                        "<methodCall><methodName>station.removeStation</methodName>"
405
 
                                        "<params><param><value><int>%lu</int></value></param>"
406
 
                                        /* auth token */
407
 
                                        "<param><value><string>%s</string></value></param>"
408
 
                                        /* station id */
409
 
                                        "<param><value><string>%s</string></value></param>"
410
 
                                        "</params></methodCall>", (unsigned long) timestamp,
411
 
                                        ph->user.authToken, station->id);
412
 
                        snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
413
 
                                        "rid=%s&lid=%s&method=removeStation&arg1=%s", ph->routeId,
414
 
                                        ph->user.listenerId, station->id);
415
 
                        break;
416
 
                }
417
 
 
418
 
                case PIANO_REQUEST_SEARCH: {
419
 
                        /* search for artist/song title */
420
 
                        PianoRequestDataSearch_t *reqData = req->data;
421
 
                        char *xmlencodedSearchStr, *urlencodedSearchStr;
422
 
 
423
 
                        assert (reqData != NULL);
424
 
                        assert (reqData->searchStr != NULL);
425
 
 
426
 
                        if ((xmlencodedSearchStr = PianoXmlEncodeString (reqData->searchStr)) == NULL) {
427
 
                                return PIANO_RET_OUT_OF_MEMORY;
428
 
                        }
429
 
                        urlencodedSearchStr = WaitressUrlEncode (reqData->searchStr);
430
 
 
431
 
                        snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
432
 
                                        "<methodCall><methodName>music.search</methodName>"
433
 
                                        "<params><param><value><int>%lu</int></value></param>"
434
 
                                        /* auth token */
435
 
                                        "<param><value><string>%s</string></value></param>"
436
 
                                        /* search string */
437
 
                                        "<param><value><string>%s</string></value></param>"
438
 
                                        "</params></methodCall>", (unsigned long) timestamp,
439
 
                                        ph->user.authToken, xmlencodedSearchStr);
440
 
                        snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
441
 
                                        "rid=%s&lid=%s&method=search&arg1=%s", ph->routeId,
442
 
                                        ph->user.listenerId, urlencodedSearchStr);
443
 
 
444
 
                        free (urlencodedSearchStr);
445
 
                        free (xmlencodedSearchStr);
446
 
                        break;
447
 
                }
448
 
 
449
 
                case PIANO_REQUEST_CREATE_STATION: {
450
 
                        /* create new station from specified musicid (type=mi, get one by
451
 
                         * performing a search) or shared station id (type=sh) */
452
 
                        PianoRequestDataCreateStation_t *reqData = req->data;
453
 
 
454
 
                        assert (reqData != NULL);
455
 
                        assert (reqData->id != NULL);
456
 
                        assert (reqData->type != NULL);
457
 
 
458
 
                        snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
459
 
                                        "<methodCall><methodName>station.createStation</methodName>"
460
 
                                        "<params><param><value><int>%lu</int></value></param>"
461
 
                                        "<param><value><string>%s</string></value></param>"
462
 
                                        "<param><value><string>%s%s</string></value></param>"
463
 
                                        "</params></methodCall>", (unsigned long) timestamp,
464
 
                                        ph->user.authToken, reqData->type, reqData->id);
465
 
 
466
 
                        snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
467
 
                                        "rid=%s&lid=%s&method=createStation&arg1=%s%s", ph->routeId,
468
 
                                        ph->user.listenerId, reqData->type, reqData->id);
469
 
                        break;
470
 
                }
471
 
 
472
 
                case PIANO_REQUEST_ADD_SEED: {
473
 
                        /* add another seed to specified station */
474
 
                        PianoRequestDataAddSeed_t *reqData = req->data;
475
 
 
476
 
                        assert (reqData != NULL);
477
 
                        assert (reqData->station != NULL);
478
 
                        assert (reqData->musicId != NULL);
479
 
 
480
 
                        snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
481
 
                                        "<methodCall><methodName>station.addSeed</methodName><params>"
482
 
                                        "<param><value><int>%lu</int></value></param>"
483
 
                                        "<param><value><string>%s</string></value></param>"
484
 
                                        "<param><value><string>%s</string></value></param>"
485
 
                                        "<param><value><string>%s</string></value></param>"
486
 
                                        "</params></methodCall>", (unsigned long) timestamp,
487
 
                                        ph->user.authToken, reqData->station->id, reqData->musicId);
488
 
                        snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
489
 
                                        "rid=%s&lid=%s&method=addSeed&arg1=%s&arg2=%s", ph->routeId,
490
 
                                        ph->user.listenerId, reqData->station->id, reqData->musicId);
491
 
                        break;
492
 
                }
493
 
 
494
 
                case PIANO_REQUEST_ADD_TIRED_SONG: {
495
 
                        /* ban song for a month from all stations */
496
 
                        PianoSong_t *song = req->data;
497
 
 
498
 
                        assert (song != NULL);
499
 
 
500
 
                        snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
501
 
                                        "<methodCall><methodName>listener.addTiredSong</methodName><params>"
502
 
                                        "<param><value><int>%lu</int></value></param>"
503
 
                                        "<param><value><string>%s</string></value></param>"
504
 
                                        /* key */
505
 
                                        "<param><value><string>%s</string></value></param>"
506
 
                                        /* user seed */
507
 
                                        "<param><value><string>%s</string></value></param>"
508
 
                                        /* station id */
509
 
                                        "<param><value><string>%s</string></value></param>"
510
 
                                        "</params></methodCall>", (unsigned long) timestamp,
511
 
                                        ph->user.authToken,
512
 
                                        (song->musicId == NULL) ? "" : song->musicId,
513
 
                                        (song->userSeed == NULL) ? "" : song->userSeed,
514
 
                                        song->stationId);
515
 
                        snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
516
 
                                        "rid=%s&lid=%s&method=addTiredSong&arg1=%s&arg2=%s&arg3=%s",
517
 
                                        ph->routeId, ph->user.listenerId,
518
 
                                        (song->musicId == NULL) ? "" : song->musicId,
519
 
                                        (song->userSeed == NULL) ? "" : song->userSeed,
520
 
                                        song->stationId);
521
 
                        break;
522
 
                }
523
 
 
524
 
                case PIANO_REQUEST_SET_QUICKMIX: {
525
 
                        /* select stations included in quickmix (see useQuickMix flag of
526
 
                         * PianoStation_t) */
527
 
                        char valueBuf[1000], urlArgBuf[1000];
528
 
                        PianoStation_t *curStation = ph->stations;
529
 
 
530
 
                        memset (urlArgBuf, 0, sizeof (urlArgBuf));
531
 
                        snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
532
 
                                        "<methodCall><methodName>station.setQuickMix</methodName><params>"
533
 
                                        "<param><value><int>%lu</int></value></param>"
534
 
                                        "<param><value><string>%s</string></value></param>"
535
 
                                        /* quick mix type */
536
 
                                        "<param><value><string>RANDOM</string></value></param>"
537
 
                                        "<param><value><array><data>", (unsigned long) timestamp,
538
 
                                        ph->user.authToken);
539
 
                        while (curStation != NULL) {
540
 
                                /* quick mix can't contain itself */
541
 
                                if (!curStation->useQuickMix || curStation->isQuickMix) {
542
 
                                        curStation = curStation->next;
543
 
                                        continue;
544
 
                                }
545
 
                                /* append to xml doc */
546
 
                                snprintf (valueBuf, sizeof (valueBuf),
547
 
                                                "<value><string>%s</string></value>", curStation->id);
548
 
                                strncat (xmlSendBuf, valueBuf, sizeof (xmlSendBuf) -
549
 
                                                strlen (xmlSendBuf) - 1);
550
 
                                /* append to url arg */
551
 
                                strncat (urlArgBuf, curStation->id, sizeof (urlArgBuf) -
552
 
                                                strlen (urlArgBuf) - 1);
553
 
                                curStation = curStation->next;
554
 
                                /* if not last item: append "," */
555
 
                                if (curStation != NULL) {
556
 
                                        strncat (urlArgBuf, "%2C", sizeof (urlArgBuf) -
557
 
                                                        strlen (urlArgBuf) - 1);
558
 
                                }
559
 
                        }
560
 
                        strncat (xmlSendBuf,
561
 
                                        "</data></array></value></param>"
562
 
                                        /* content type */
563
 
                                        "<param><value><string>CUSTOM</string></value></param>"
564
 
                                        /* genre */
565
 
                                        "<param><value><string></string></value></param>"
566
 
                                        "</params></methodCall>",
567
 
                                        sizeof (xmlSendBuf) - strlen (xmlSendBuf) - 1);
568
 
 
569
 
                        snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
570
 
                                        "rid=%s&lid=%s&method=setQuickMix&arg1=RANDOM&arg2=%s&arg3=CUSTOM&arg4=",
571
 
                                        ph->routeId, ph->user.listenerId, urlArgBuf);
572
 
                        break;
573
 
                }
574
 
 
575
 
                case PIANO_REQUEST_GET_GENRE_STATIONS:
576
 
                        /* receive list of pandora's genre stations */
577
 
                        xmlSendBuf[0] = '\0';
578
 
                        snprintf (req->urlPath, sizeof (req->urlPath), "/xml/genre?r=%lu",
579
 
                                        (unsigned long) timestamp);
580
 
                        break;
581
 
 
582
 
                case PIANO_REQUEST_TRANSFORM_STATION: {
583
 
                        /* transform shared station into private */
584
 
                        PianoStation_t *station = req->data;
585
 
 
586
 
                        assert (station != NULL);
587
 
 
588
 
                        snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
589
 
                                        "<methodCall><methodName>station.transformShared</methodName>"
590
 
                                        "<params><param><value><int>%lu</int></value></param>"
591
 
                                        /* auth token */
592
 
                                        "<param><value><string>%s</string></value></param>"
593
 
                                        /* station id */
594
 
                                        "<param><value><string>%s</string></value></param>"
595
 
                                        "</params></methodCall>", (unsigned long) timestamp,
596
 
                                        ph->user.authToken, station->id);
597
 
                        snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
598
 
                                        "rid=%s&lid=%s&method=transformShared&arg1=%s", ph->routeId,
599
 
                                        ph->user.listenerId, station->id);
600
 
                        break;
601
 
                }
602
 
 
603
 
                case PIANO_REQUEST_EXPLAIN: {
604
 
                        /* explain why particular song was played */
605
 
                        PianoRequestDataExplain_t *reqData = req->data;
606
 
 
607
 
                        assert (reqData != NULL);
608
 
                        assert (reqData->song != NULL);
609
 
 
610
 
                        snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
611
 
                                        "<methodCall><methodName>playlist.narrative</methodName>"
612
 
                                        "<params><param><value><int>%lu</int></value></param>"
613
 
                                        /* auth token */
614
 
                                        "<param><value><string>%s</string></value></param>"
615
 
                                        /* station id */
616
 
                                        "<param><value><string>%s</string></value></param>"
617
 
                                        /* music id */
618
 
                                        "<param><value><string>%s</string></value></param>"
619
 
                                        "</params></methodCall>", (unsigned long) timestamp,
620
 
                                        ph->user.authToken, reqData->song->stationId,
621
 
                                        reqData->song->musicId);
622
 
                        snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
623
 
                                        "rid=%s&lid=%s&method=narrative&arg1=%s&arg2=%s",
624
 
                                        ph->routeId, ph->user.listenerId, reqData->song->stationId,
625
 
                                        reqData->song->musicId);
626
 
                        break;
627
 
                }
628
 
 
629
 
                case PIANO_REQUEST_GET_SEED_SUGGESTIONS: {
630
 
                        /* find similar artists */
631
 
                        PianoRequestDataGetSeedSuggestions_t *reqData = req->data;
632
 
 
633
 
                        assert (reqData != NULL);
634
 
                        assert (reqData->musicId != NULL);
635
 
                        assert (reqData->max != 0);
636
 
 
637
 
                        snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
638
 
                                        "<methodCall><methodName>music.getSeedSuggestions</methodName>"
639
 
                                        "<params><param><value><int>%lu</int></value></param>"
640
 
                                        /* auth token */
641
 
                                        "<param><value><string>%s</string></value></param>"
642
 
                                        /* seed music id */
643
 
                                        "<param><value><string>%s</string></value></param>"
644
 
                                        /* max */
645
 
                                        "<param><value><int>%u</int></value></param>"
646
 
                                        "</params></methodCall>", (unsigned long) timestamp,
647
 
                                        ph->user.authToken, reqData->musicId, reqData->max);
648
 
                        snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
649
 
                                        "rid=%s&lid=%s&method=getSeedSuggestions&arg1=%s&arg2=%u",
650
 
                                        ph->routeId, ph->user.listenerId, reqData->musicId, reqData->max);
651
 
                        break;
652
 
                }
653
 
 
654
 
                case PIANO_REQUEST_BOOKMARK_SONG: {
655
 
                        /* bookmark song */
656
 
                        PianoSong_t *song = req->data;
657
 
 
658
 
                        assert (song != NULL);
659
 
 
660
 
                        snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
661
 
                                        "<methodCall><methodName>station.createBookmark</methodName>"
662
 
                                        "<params><param><value><int>%lu</int></value></param>"
663
 
                                        /* auth token */
664
 
                                        "<param><value><string>%s</string></value></param>"
665
 
                                        /* station id */
666
 
                                        "<param><value><string>%s</string></value></param>"
667
 
                                        /* music id */
668
 
                                        "<param><value><string>%s</string></value></param>"
669
 
                                        "</params></methodCall>", (unsigned long) timestamp,
670
 
                                        ph->user.authToken, song->stationId, song->musicId);
671
 
                        snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
672
 
                                        "rid=%s&lid=%s&method=createBookmark&arg1=%s&arg2=%s",
673
 
                                        ph->routeId, ph->user.listenerId, song->stationId,
674
 
                                        song->musicId);
675
 
                        break;
676
 
                }
677
 
 
678
 
                case PIANO_REQUEST_BOOKMARK_ARTIST: {
679
 
                        /* bookmark artist */
680
 
                        PianoSong_t *song = req->data;
681
 
 
682
 
                        assert (song != NULL);
683
 
 
684
 
                        snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
685
 
                                        "<methodCall><methodName>station.createArtistBookmark</methodName>"
686
 
                                        "<params><param><value><int>%lu</int></value></param>"
687
 
                                        /* auth token */
688
 
                                        "<param><value><string>%s</string></value></param>"
689
 
                                        /* music id */
690
 
                                        "<param><value><string>%s</string></value></param>"
691
 
                                        "</params></methodCall>", (unsigned long) timestamp,
692
 
                                        ph->user.authToken, song->artistMusicId);
693
 
                        snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
694
 
                                        "rid=%s&lid=%s&method=createArtistBookmark&arg1=%s",
695
 
                                        ph->routeId, ph->user.listenerId, song->artistMusicId);
696
 
                        break;
697
 
                }
698
 
 
699
 
                /* "high-level" wrapper */
700
 
                case PIANO_REQUEST_RATE_SONG: {
701
 
                        /* love/ban song */
702
 
                        PianoRequestDataRateSong_t *reqData = req->data;
703
 
                        PianoReturn_t pRet;
704
 
 
705
 
                        assert (reqData != NULL);
706
 
                        assert (reqData->song != NULL);
707
 
                        assert (reqData->rating != PIANO_RATE_NONE);
708
 
 
709
 
                        PianoRequestDataAddFeedback_t transformedReqData;
710
 
                        transformedReqData.stationId = reqData->song->stationId;
711
 
                        transformedReqData.musicId = reqData->song->musicId;
712
 
                        transformedReqData.userSeed = reqData->song->userSeed;
713
 
                        transformedReqData.rating = reqData->rating;
714
 
                        transformedReqData.testStrategy = reqData->song->testStrategy;
715
 
                        transformedReqData.songType = reqData->song->songType;
716
 
                        req->data = &transformedReqData;
717
 
 
718
 
                        /* create request data (url, post data) */
719
 
                        pRet = PianoRequest (ph, req, PIANO_REQUEST_ADD_FEEDBACK);
720
 
                        /* and reset request type/data */
721
 
                        req->type = PIANO_REQUEST_RATE_SONG;
722
 
                        req->data = reqData;
723
 
 
724
 
                        return pRet;
725
 
                        break;
726
 
                }
727
 
 
728
 
                case PIANO_REQUEST_MOVE_SONG: {
729
 
                        /* move song to a different station, needs two requests */
730
 
                        PianoRequestDataMoveSong_t *reqData = req->data;
731
 
                        PianoRequestDataAddFeedback_t transformedReqData;
732
 
                        PianoReturn_t pRet;
733
 
 
734
 
                        assert (reqData != NULL);
735
 
                        assert (reqData->song != NULL);
736
 
                        assert (reqData->from != NULL);
737
 
                        assert (reqData->to != NULL);
738
 
                        assert (reqData->step < 2);
739
 
 
740
 
                        transformedReqData.musicId = reqData->song->musicId;
741
 
                        transformedReqData.userSeed = "";
742
 
                        transformedReqData.songType = reqData->song->songType;
743
 
                        transformedReqData.testStrategy = reqData->song->testStrategy;
744
 
                        req->data = &transformedReqData;
745
 
 
746
 
                        switch (reqData->step) {
747
 
                                case 0:
748
 
                                        transformedReqData.stationId = reqData->from->id;
749
 
                                        transformedReqData.rating = PIANO_RATE_BAN;
750
 
                                        break;
751
 
 
752
 
                                case 1:
753
 
                                        transformedReqData.stationId = reqData->to->id;
754
 
                                        transformedReqData.rating = PIANO_RATE_LOVE;
755
 
                                        break;
756
 
                        }
757
 
 
758
 
                        /* create request data (url, post data) */
759
 
                        pRet = PianoRequest (ph, req, PIANO_REQUEST_ADD_FEEDBACK);
760
 
                        /* and reset request type/data */
761
 
                        req->type = PIANO_REQUEST_MOVE_SONG;
762
 
                        req->data = reqData;
763
 
 
764
 
                        return pRet;
765
 
                        break;
766
 
                }
767
 
        }
768
 
 
769
 
        if ((req->postData = PianoEncryptString (xmlSendBuf)) == NULL) {
770
 
                return PIANO_RET_OUT_OF_MEMORY;
771
 
        }
772
 
 
773
 
        return PIANO_RET_OK;
774
 
}
775
 
 
776
 
/*      parse xml response and update data structures/return new data structure
777
 
 *      @param piano handle
778
 
 *      @param initialized request (expects responseData to be a NUL-terminated
779
 
 *                      string)
780
 
 */
781
 
PianoReturn_t PianoResponse (PianoHandle_t *ph, PianoRequest_t *req) {
782
 
        PianoReturn_t ret = PIANO_RET_ERR;
783
 
 
784
 
        assert (ph != NULL);
785
 
        assert (req != NULL);
786
 
 
787
 
        switch (req->type) {
788
 
                case PIANO_REQUEST_LOGIN: {
789
 
                        /* authenticate user */
790
 
                        PianoRequestDataLogin_t *reqData = req->data;
791
 
 
792
 
                        assert (req->responseData != NULL);
793
 
                        assert (reqData != NULL);
794
 
 
795
 
                        switch (reqData->step) {
796
 
                                case 0: {
797
 
                                        char *cryptedTimestamp = NULL;
798
 
 
799
 
                                        assert (req->responseData != NULL);
800
 
 
801
 
                                        /* abusing parseNarrative; has same xml structure */
802
 
                                        ret = PianoXmlParseNarrative (req->responseData, &cryptedTimestamp);
803
 
                                        if (cryptedTimestamp != NULL) {
804
 
                                                unsigned long timestamp = 0;
805
 
                                                time_t realTimestamp = time (NULL);
806
 
                                                char *decryptedTimestamp = NULL, *decryptedPos = NULL;
807
 
                                                unsigned char i = 4;
808
 
 
809
 
                                                if ((decryptedTimestamp = PianoDecryptString (cryptedTimestamp)) != NULL) {
810
 
                                                        decryptedPos = decryptedTimestamp;
811
 
                                                        /* skip four bytes garbage? at beginning */
812
 
                                                        while (i-- > 0 && *decryptedPos++ != '\0');
813
 
                                                        timestamp = strtoul (decryptedPos, NULL, 0);
814
 
                                                        ph->timeOffset = realTimestamp - timestamp;
815
 
 
816
 
                                                        free (decryptedTimestamp);
817
 
                                                }
818
 
                                                free (cryptedTimestamp);
819
 
                                        }
820
 
                                        ret = PIANO_RET_CONTINUE_REQUEST;
821
 
                                        ++reqData->step;
822
 
                                        break;
823
 
                                }
824
 
 
825
 
                                case 1:
826
 
                                        /* information exists when reauthenticating, destroy to
827
 
                                         * avoid memleak */
828
 
                                        if (ph->user.listenerId != NULL) {
829
 
                                                PianoDestroyUserInfo (&ph->user);
830
 
                                        }
831
 
                                        ret = PianoXmlParseUserinfo (ph, req->responseData);
832
 
                                        break;
833
 
                        }
834
 
                        break;
835
 
                }
836
 
 
837
 
                case PIANO_REQUEST_GET_STATIONS:
838
 
                        /* get stations */
839
 
                        assert (req->responseData != NULL);
840
 
                        
841
 
                        ret = PianoXmlParseStations (ph, req->responseData);
842
 
                        break;
843
 
 
844
 
                case PIANO_REQUEST_GET_PLAYLIST: {
845
 
                        /* get playlist, usually four songs */
846
 
                        PianoRequestDataGetPlaylist_t *reqData = req->data;
847
 
 
848
 
                        assert (req->responseData != NULL);
849
 
                        assert (reqData != NULL);
850
 
 
851
 
                        reqData->retPlaylist = NULL;
852
 
                        ret = PianoXmlParsePlaylist (ph, req->responseData,
853
 
                                        &reqData->retPlaylist);
854
 
                        break;
855
 
                }
856
 
 
857
 
                case PIANO_REQUEST_RATE_SONG:
858
 
                        /* love/ban song */
859
 
                        assert (req->responseData != NULL);
860
 
 
861
 
                        ret = PianoXmlParseSimple (req->responseData);
862
 
                        if (ret == PIANO_RET_OK) {
863
 
                                PianoRequestDataRateSong_t *reqData = req->data;
864
 
                                reqData->song->rating = reqData->rating;
865
 
                        }
866
 
                        break;
867
 
 
868
 
                case PIANO_REQUEST_ADD_FEEDBACK:
869
 
                        /* never ever use this directly, low-level call */
870
 
                        assert (0);
871
 
                        break;
872
 
 
873
 
                case PIANO_REQUEST_MOVE_SONG: {
874
 
                        /* move song to different station */
875
 
                        PianoRequestDataMoveSong_t *reqData = req->data;
876
 
 
877
 
                        assert (req->responseData != NULL);
878
 
                        assert (reqData != NULL);
879
 
                        assert (reqData->step < 2);
880
 
 
881
 
                        ret = PianoXmlParseSimple (req->responseData);
882
 
                        if (ret == PIANO_RET_OK && reqData->step == 0) {
883
 
                                ret = PIANO_RET_CONTINUE_REQUEST;
884
 
                                ++reqData->step;
885
 
                        }
886
 
                        break;
887
 
                }
888
 
 
889
 
                case PIANO_REQUEST_RENAME_STATION:
890
 
                        /* rename station and update PianoStation_t structure */
891
 
                        assert (req->responseData != NULL);
892
 
 
893
 
                        if ((ret = PianoXmlParseSimple (req->responseData)) == PIANO_RET_OK) {
894
 
                                PianoRequestDataRenameStation_t *reqData = req->data;
895
 
 
896
 
                                assert (reqData != NULL);
897
 
                                assert (reqData->station != NULL);
898
 
                                assert (reqData->newName != NULL);
899
 
 
900
 
                                free (reqData->station->name);
901
 
                                reqData->station->name = strdup (reqData->newName);
902
 
                        }
903
 
                        break;
904
 
 
905
 
                case PIANO_REQUEST_DELETE_STATION:
906
 
                        /* delete station from server and station list */
907
 
                        assert (req->responseData != NULL);
908
 
 
909
 
                        if ((ret = PianoXmlParseSimple (req->responseData)) == PIANO_RET_OK) {
910
 
                                PianoStation_t *station = req->data;
911
 
 
912
 
                                assert (station != NULL);
913
 
 
914
 
                                /* delete station from local station list */
915
 
                                PianoStation_t *curStation = ph->stations, *lastStation = NULL;
916
 
                                while (curStation != NULL) {
917
 
                                        if (curStation == station) {
918
 
                                                if (lastStation != NULL) {
919
 
                                                        lastStation->next = curStation->next;
920
 
                                                } else {
921
 
                                                        /* first station in list */
922
 
                                                        ph->stations = curStation->next;
923
 
                                                }
924
 
                                                PianoDestroyStation (curStation);
925
 
                                                free (curStation);
926
 
                                                break;
927
 
                                        }
928
 
                                        lastStation = curStation;
929
 
                                        curStation = curStation->next;
930
 
                                }
931
 
                        }
932
 
                        break;
933
 
 
934
 
                case PIANO_REQUEST_SEARCH: {
935
 
                        /* search artist/song */
936
 
                        PianoRequestDataSearch_t *reqData = req->data;
937
 
 
938
 
                        assert (req->responseData != NULL);
939
 
                        assert (reqData != NULL);
940
 
 
941
 
                        ret = PianoXmlParseSearch (req->responseData, &reqData->searchResult);
942
 
                        break;
943
 
                }
944
 
 
945
 
                case PIANO_REQUEST_CREATE_STATION: {
946
 
                        /* create station, insert new station into station list on success */
947
 
                        assert (req->responseData != NULL);
948
 
 
949
 
                        ret = PianoXmlParseCreateStation (ph, req->responseData);
950
 
                        break;
951
 
                }
952
 
 
953
 
                case PIANO_REQUEST_ADD_SEED: {
954
 
                        /* add seed to station, updates station structure */
955
 
                        PianoRequestDataAddSeed_t *reqData = req->data;
956
 
 
957
 
                        assert (req->responseData != NULL);
958
 
                        assert (reqData != NULL);
959
 
                        assert (reqData->station != NULL);
960
 
 
961
 
                        /* FIXME: update station data instead of replacing them */
962
 
                        ret = PianoXmlParseAddSeed (ph, req->responseData, reqData->station);
963
 
                        break;
964
 
                }
965
 
 
966
 
                case PIANO_REQUEST_ADD_TIRED_SONG:
967
 
                case PIANO_REQUEST_SET_QUICKMIX:
968
 
                case PIANO_REQUEST_BOOKMARK_SONG:
969
 
                case PIANO_REQUEST_BOOKMARK_ARTIST:
970
 
                        assert (req->responseData != NULL);
971
 
 
972
 
                        ret = PianoXmlParseSimple (req->responseData);
973
 
                        break;
974
 
 
975
 
                case PIANO_REQUEST_GET_GENRE_STATIONS:
976
 
                        /* get genre stations */
977
 
                        assert (req->responseData != NULL);
978
 
 
979
 
                        ret = PianoXmlParseGenreExplorer (ph, req->responseData);
980
 
                        break;
981
 
 
982
 
                case PIANO_REQUEST_TRANSFORM_STATION: {
983
 
                        /* transform shared station into private and update isCreator flag */
984
 
                        PianoStation_t *station = req->data;
985
 
 
986
 
                        assert (req->responseData != NULL);
987
 
                        assert (station != NULL);
988
 
 
989
 
                        /* though this call returns a bunch of "new" data only this one is
990
 
                         * changed and important (at the moment) */
991
 
                        if ((ret = PianoXmlParseTranformStation (req->responseData)) ==
992
 
                                        PIANO_RET_OK) {
993
 
                                station->isCreator = 1;
994
 
                        }
995
 
                        break;
996
 
                }
997
 
 
998
 
                case PIANO_REQUEST_EXPLAIN: {
999
 
                        /* explain why song was selected */
1000
 
                        PianoRequestDataExplain_t *reqData = req->data;
1001
 
 
1002
 
                        assert (req->responseData != NULL);
1003
 
                        assert (reqData != NULL);
1004
 
 
1005
 
                        ret = PianoXmlParseNarrative (req->responseData, &reqData->retExplain);
1006
 
                        break;
1007
 
                }
1008
 
 
1009
 
                case PIANO_REQUEST_GET_SEED_SUGGESTIONS: {
1010
 
                        /* find similar artists */
1011
 
                        PianoRequestDataGetSeedSuggestions_t *reqData = req->data;
1012
 
 
1013
 
                        assert (req->responseData != NULL);
1014
 
                        assert (reqData != NULL);
1015
 
 
1016
 
                        ret = PianoXmlParseSeedSuggestions (req->responseData,
1017
 
                                        &reqData->searchResult);
1018
 
                        break;
1019
 
                }
1020
 
        }
1021
 
 
1022
 
        return ret;
1023
 
}
1024
 
 
1025
 
/*      get station from list by id
1026
 
 *      @param search here
1027
 
 *      @param search for this
1028
 
 *      @return the first station structure matching the given id
1029
 
 */
1030
 
PianoStation_t *PianoFindStationById (PianoStation_t *stations,
1031
 
                const char *searchStation) {
1032
 
        while (stations != NULL) {
1033
 
                if (strcmp (stations->id, searchStation) == 0) {
1034
 
                        return stations;
1035
 
                }
1036
 
                stations = stations->next;
1037
 
        }
1038
 
        return NULL;
1039
 
}
1040
 
 
1041
 
/*      convert return value to human-readable string
1042
 
 *      @param enum
1043
 
 *      @return error string
1044
 
 */
1045
 
const char *PianoErrorToStr (PianoReturn_t ret) {
1046
 
        switch (ret) {
1047
 
                case PIANO_RET_OK:
1048
 
                        return "Everything is fine :)";
1049
 
                        break;
1050
 
 
1051
 
                case PIANO_RET_ERR:
1052
 
                        return "Unknown.";
1053
 
                        break;
1054
 
 
1055
 
                case PIANO_RET_XML_INVALID:
1056
 
                        return "Invalid XML.";
1057
 
                        break;
1058
 
 
1059
 
                case PIANO_RET_AUTH_TOKEN_INVALID:
1060
 
                        return "Invalid auth token.";
1061
 
                        break;
1062
 
                
1063
 
                case PIANO_RET_AUTH_USER_PASSWORD_INVALID:
1064
 
                        return "Username and/or password not correct.";
1065
 
                        break;
1066
 
 
1067
 
                case PIANO_RET_NOT_AUTHORIZED:
1068
 
                        return "Not authorized.";
1069
 
                        break;
1070
 
 
1071
 
                case PIANO_RET_PROTOCOL_INCOMPATIBLE:
1072
 
                        return "Protocol incompatible. Please upgrade " PACKAGE ".";
1073
 
                        break;
1074
 
 
1075
 
                case PIANO_RET_READONLY_MODE:
1076
 
                        return "Request cannot be completed at this time, please try "
1077
 
                                        "again later.";
1078
 
                        break;
1079
 
 
1080
 
                case PIANO_RET_STATION_CODE_INVALID:
1081
 
                        return "Station id is invalid.";
1082
 
                        break;
1083
 
 
1084
 
                case PIANO_RET_IP_REJECTED:
1085
 
                        return "Your ip address was rejected. Please setup a control "
1086
 
                                        "proxy (see manpage).";
1087
 
                        break;
1088
 
 
1089
 
                case PIANO_RET_STATION_NONEXISTENT:
1090
 
                        return "Station does not exist.";
1091
 
                        break;
1092
 
 
1093
 
                case PIANO_RET_OUT_OF_MEMORY:
1094
 
                        return "Out of memory.";
1095
 
                        break;
1096
 
 
1097
 
                case PIANO_RET_OUT_OF_SYNC:
1098
 
                        return "Out of sync. Please correct your system's time.";
1099
 
                        break;
1100
 
 
1101
 
                case PIANO_RET_PLAYLIST_END:
1102
 
                        return "Playlist end.";
1103
 
                        break;
1104
 
 
1105
 
                case PIANO_RET_QUICKMIX_NOT_PLAYABLE:
1106
 
                        return "Quickmix not playable.";
1107
 
                        break;
1108
 
 
1109
 
                default:
1110
 
                        return "No error message available.";
1111
 
                        break;
1112
 
        }
1113
 
}
1114