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

« back to all changes in this revision

Viewing changes to src/libpiano/piano.c

  • Committer: Bazaar Package Importer
  • Author(s): Luke Faraone
  • Date: 2011-02-08 17:23:25 UTC
  • mfrom: (1.3.3 upstream)
  • Revision ID: james.westby@ubuntu.com-20110208172325-0qf3sxpsu37j5ez9
Tags: 2011.01.24-1
* New upstream version. 
* Switch to DEP5 copyright.
* Augment CFLAGS to use the c99 standard.
* Don't install the now-removed AUTHORS file into docs.
* Drop dep on cmake.

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