197
206
memset (req, 0, sizeof (*req));
200
/* convert audio format id to string that can be used in xml requests
202
* @return constant string
204
static const char *PianoAudioFormatToString (PianoAudioFormat_t format) {
206
case PIANO_AF_AACPLUS:
214
case PIANO_AF_MP3_HI:
224
/* prepare piano request (initializes request type, urlpath and postData)
225
* @param piano handle
226
* @param request structure
227
* @param request type
229
PianoReturn_t PianoRequest (PianoHandle_t *ph, PianoRequest_t *req,
230
PianoRequestType_t type) {
231
char xmlSendBuf[PIANO_SEND_BUFFER_SIZE];
232
/* corrected timestamp */
233
time_t timestamp = time (NULL) - ph->timeOffset;
236
assert (req != NULL);
239
/* no tls by default */
243
case PIANO_REQUEST_LOGIN: {
244
/* authenticate user */
245
PianoRequestDataLogin_t *logindata = req->data;
247
assert (logindata != NULL);
249
switch (logindata->step) {
251
snprintf (xmlSendBuf, sizeof (xmlSendBuf),
252
"<?xml version=\"1.0\"?><methodCall>"
253
"<methodName>misc.sync</methodName>"
254
"<params></params></methodCall>");
255
snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
256
"rid=%s&method=sync", ph->routeId);
260
char *xmlencodedPassword = NULL;
264
/* username == email address does not contain &,<,>," */
265
if ((xmlencodedPassword =
266
PianoXmlEncodeString (logindata->password)) ==
268
return PIANO_RET_OUT_OF_MEMORY;
271
snprintf (xmlSendBuf, sizeof (xmlSendBuf),
272
"<?xml version=\"1.0\"?><methodCall>"
273
"<methodName>listener.authenticateListener</methodName>"
274
"<params><param><value><int>%lu</int></value></param>"
276
"<param><value><string>%s</string></value></param>"
278
"<param><value><string>%s</string></value></param>"
280
"<param><value><string>html5tuner</string></value></param>"
281
"<param><value><string/></value></param>"
282
"<param><value><string/></value></param>"
283
"<param><value><string>HTML5</string></value></param>"
284
"<param><value><boolean>1</boolean></value></param>"
285
"</params></methodCall>", (unsigned long) timestamp,
286
logindata->user, xmlencodedPassword);
287
snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
288
"rid=%s&method=authenticateListener", ph->routeId);
290
free (xmlencodedPassword);
297
case PIANO_REQUEST_GET_STATIONS:
298
/* get stations, user must be authenticated */
299
assert (ph->user.listenerId != NULL);
301
snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
302
"<methodCall><methodName>station.getStations</methodName>"
303
"<params><param><value><int>%lu</int></value></param>"
304
"<param><value><string>%s</string></value></param>"
305
"</params></methodCall>", (unsigned long) timestamp,
307
snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
308
"rid=%s&lid=%s&method=getStations", ph->routeId,
309
ph->user.listenerId);
312
case PIANO_REQUEST_GET_PLAYLIST: {
313
/* get playlist for specified station */
314
PianoRequestDataGetPlaylist_t *reqData = req->data;
316
assert (reqData != NULL);
317
assert (reqData->station != NULL);
318
assert (reqData->station->id != NULL);
319
assert (reqData->format != PIANO_AF_UNKNOWN);
321
snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
322
"<methodCall><methodName>playlist.getFragment</methodName>"
323
"<params><param><value><int>%lu</int></value></param>"
325
"<param><value><string>%s</string></value></param>"
327
"<param><value><string>%s</string></value></param>"
328
/* total listening time */
329
"<param><value><string>0</string></value></param>"
330
/* time since last session */
331
"<param><value><string></string></value></param>"
333
"<param><value><string></string></value></param>"
335
"<param><value><string>%s</string></value></param>"
336
/* delta listening time */
337
"<param><value><string>0</string></value></param>"
338
/* listening timestamp */
339
"<param><value><string>0</string></value></param>"
340
"</params></methodCall>", (unsigned long) timestamp,
341
ph->user.authToken, reqData->station->id,
342
PianoAudioFormatToString (reqData->format));
343
snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
344
"rid=%s&lid=%s&method=getFragment&arg1=%s&arg2=0"
345
"&arg3=&arg4=&arg5=%s&arg6=0&arg7=0", ph->routeId,
346
ph->user.listenerId, reqData->station->id,
347
PianoAudioFormatToString (reqData->format));
351
case PIANO_REQUEST_ADD_FEEDBACK: {
352
/* low-level, don't use directly (see _RATE_SONG and _MOVE_SONG) */
353
PianoRequestDataAddFeedback_t *reqData = req->data;
355
assert (reqData != NULL);
356
assert (reqData->stationId != NULL);
357
assert (reqData->rating != PIANO_RATE_NONE);
359
snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
360
"<methodCall><methodName>station.addFeedback</methodName>"
361
"<params><param><value><int>%lu</int></value></param>"
363
"<param><value><string>%s</string></value></param>"
365
"<param><value><string>%s</string></value></param>"
367
"<param><value><string>%s</string></value></param>"
369
"<param><value><boolean>%i</boolean></value></param>"
370
"</params></methodCall>", (unsigned long) timestamp,
371
ph->user.authToken, reqData->stationId, reqData->trackToken,
372
(reqData->rating == PIANO_RATE_LOVE) ? 1 : 0);
373
snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
374
"rid=%s&lid=%s&method=addFeedback&arg1=%s&arg2=%s"
376
ph->routeId, ph->user.listenerId, reqData->stationId,
378
(reqData->rating == PIANO_RATE_LOVE) ? "true" : "false");
382
case PIANO_REQUEST_RENAME_STATION: {
383
/* rename stations */
384
PianoRequestDataRenameStation_t *reqData = req->data;
385
char *urlencodedNewName, *xmlencodedNewName;
387
assert (reqData != NULL);
388
assert (reqData->station != NULL);
389
assert (reqData->newName != NULL);
391
if ((xmlencodedNewName = PianoXmlEncodeString (reqData->newName)) == NULL) {
392
return PIANO_RET_OUT_OF_MEMORY;
394
urlencodedNewName = WaitressUrlEncode (reqData->newName);
396
snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
397
"<methodCall><methodName>station.setStationName</methodName>"
398
"<params><param><value><int>%lu</int></value></param>"
400
"<param><value><string>%s</string></value></param>"
402
"<param><value><string>%s</string></value></param>"
404
"<param><value><string>%s</string></value></param>"
405
"</params></methodCall>", (unsigned long) timestamp,
406
ph->user.authToken, reqData->station->id,
408
snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
409
"rid=%s&lid=%s&method=setStationName&arg1=%s&arg2=%s",
410
ph->routeId, ph->user.listenerId, reqData->station->id,
413
free (urlencodedNewName);
414
free (xmlencodedNewName);
418
case PIANO_REQUEST_DELETE_STATION: {
420
PianoStation_t *station = req->data;
422
assert (station != NULL);
424
snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
425
"<methodCall><methodName>station.removeStation</methodName>"
426
"<params><param><value><int>%lu</int></value></param>"
428
"<param><value><string>%s</string></value></param>"
430
"<param><value><string>%s</string></value></param>"
431
"</params></methodCall>", (unsigned long) timestamp,
432
ph->user.authToken, station->id);
433
snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
434
"rid=%s&lid=%s&method=removeStation&arg1=%s", ph->routeId,
435
ph->user.listenerId, station->id);
439
case PIANO_REQUEST_SEARCH: {
440
/* search for artist/song title */
441
PianoRequestDataSearch_t *reqData = req->data;
442
char *xmlencodedSearchStr, *urlencodedSearchStr;
444
assert (reqData != NULL);
445
assert (reqData->searchStr != NULL);
447
if ((xmlencodedSearchStr = PianoXmlEncodeString (reqData->searchStr)) == NULL) {
448
return PIANO_RET_OUT_OF_MEMORY;
450
urlencodedSearchStr = WaitressUrlEncode (reqData->searchStr);
452
snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
453
"<methodCall><methodName>music.search</methodName>"
454
"<params><param><value><int>%lu</int></value></param>"
456
"<param><value><string>%s</string></value></param>"
458
"<param><value><string>%s</string></value></param>"
459
"</params></methodCall>", (unsigned long) timestamp,
460
ph->user.authToken, xmlencodedSearchStr);
461
snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
462
"rid=%s&lid=%s&method=search&arg1=%s", ph->routeId,
463
ph->user.listenerId, urlencodedSearchStr);
465
free (urlencodedSearchStr);
466
free (xmlencodedSearchStr);
470
case PIANO_REQUEST_CREATE_STATION: {
471
/* create new station from specified musicid (type=mi, get one by
472
* performing a search) or shared station id (type=sh) */
473
PianoRequestDataCreateStation_t *reqData = req->data;
475
assert (reqData != NULL);
476
assert (reqData->id != NULL);
477
assert (reqData->type != NULL);
479
snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
480
"<methodCall><methodName>station.createStation</methodName>"
481
"<params><param><value><int>%lu</int></value></param>"
483
"<param><value><string>%s</string></value></param>"
485
"<param><value><string>%s%s</string></value></param>"
487
"<param><value><string></string></value></param>"
488
"</params></methodCall>", (unsigned long) timestamp,
489
ph->user.authToken, reqData->type, reqData->id);
491
snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
492
"rid=%s&lid=%s&method=createStation&arg1=%s%s&arg2=", ph->routeId,
493
ph->user.listenerId, reqData->type, reqData->id);
497
case PIANO_REQUEST_ADD_SEED: {
498
/* add another seed to specified station */
499
PianoRequestDataAddSeed_t *reqData = req->data;
501
assert (reqData != NULL);
502
assert (reqData->station != NULL);
503
assert (reqData->musicId != NULL);
505
snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
506
"<methodCall><methodName>station.addSeed</methodName><params>"
507
"<param><value><int>%lu</int></value></param>"
509
"<param><value><string>%s</string></value></param>"
511
"<param><value><string>%s</string></value></param>"
513
"<param><value><string>%s</string></value></param>"
514
"</params></methodCall>", (unsigned long) timestamp,
515
ph->user.authToken, reqData->station->id, reqData->musicId);
516
snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
517
"rid=%s&lid=%s&method=addSeed&arg1=%s&arg2=%s", ph->routeId,
518
ph->user.listenerId, reqData->station->id, reqData->musicId);
522
case PIANO_REQUEST_ADD_TIRED_SONG: {
523
/* ban song for a month from all stations */
524
PianoSong_t *song = req->data;
526
assert (song != NULL);
528
snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
529
"<methodCall><methodName>listener.addTiredSong</methodName><params>"
530
"<param><value><int>%lu</int></value></param>"
531
"<param><value><string>%s</string></value></param>"
533
"<param><value><string>%s</string></value></param>"
535
"<param><value><string>%s</string></value></param>"
537
"<param><value><string>%s</string></value></param>"
538
"</params></methodCall>", (unsigned long) timestamp,
540
(song->musicId == NULL) ? "" : song->musicId,
541
(song->userSeed == NULL) ? "" : song->userSeed,
543
snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
544
"rid=%s&lid=%s&method=addTiredSong&arg1=%s&arg2=%s&arg3=%s",
545
ph->routeId, ph->user.listenerId,
546
(song->musicId == NULL) ? "" : song->musicId,
547
(song->userSeed == NULL) ? "" : song->userSeed,
552
case PIANO_REQUEST_SET_QUICKMIX: {
553
/* select stations included in quickmix (see useQuickMix flag of
555
char valueBuf[1000], urlArgBuf[1000];
556
PianoStation_t *curStation = ph->stations;
558
memset (urlArgBuf, 0, sizeof (urlArgBuf));
559
snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
560
"<methodCall><methodName>station.setQuickMix</methodName><params>"
561
"<param><value><int>%lu</int></value></param>"
562
"<param><value><string>%s</string></value></param>"
564
"<param><value><string>RANDOM</string></value></param>"
565
"<param><value><array><data>", (unsigned long) timestamp,
567
while (curStation != NULL) {
568
/* quick mix can't contain itself */
569
if (!curStation->useQuickMix || curStation->isQuickMix) {
570
curStation = curStation->next;
573
/* append to xml doc */
574
snprintf (valueBuf, sizeof (valueBuf),
575
"<value><string>%s</string></value>", curStation->id);
576
strncat (xmlSendBuf, valueBuf, sizeof (xmlSendBuf) -
577
strlen (xmlSendBuf) - 1);
578
/* append to url arg */
579
strncat (urlArgBuf, curStation->id, sizeof (urlArgBuf) -
580
strlen (urlArgBuf) - 1);
581
curStation = curStation->next;
582
/* if not last item: append "," */
583
if (curStation != NULL) {
584
strncat (urlArgBuf, "%2C", sizeof (urlArgBuf) -
585
strlen (urlArgBuf) - 1);
589
"</data></array></value></param>"
591
"<param><value><string></string></value></param>"
593
"<param><value><string></string></value></param>"
594
"</params></methodCall>",
595
sizeof (xmlSendBuf) - strlen (xmlSendBuf) - 1);
597
snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
598
"rid=%s&lid=%s&method=setQuickMix&arg1=RANDOM&arg2=%s&arg3=&arg4=",
599
ph->routeId, ph->user.listenerId, urlArgBuf);
603
case PIANO_REQUEST_GET_GENRE_STATIONS:
604
/* receive list of pandora's genre stations */
605
xmlSendBuf[0] = '\0';
606
snprintf (req->urlPath, sizeof (req->urlPath), "/xml/genre?r=%lu",
607
(unsigned long) timestamp);
610
case PIANO_REQUEST_TRANSFORM_STATION: {
611
/* transform shared station into private */
612
PianoStation_t *station = req->data;
614
assert (station != NULL);
616
snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
617
"<methodCall><methodName>station.transformShared</methodName>"
618
"<params><param><value><int>%lu</int></value></param>"
620
"<param><value><string>%s</string></value></param>"
622
"<param><value><string>%s</string></value></param>"
623
"</params></methodCall>", (unsigned long) timestamp,
624
ph->user.authToken, station->id);
625
snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
626
"rid=%s&lid=%s&method=transformShared&arg1=%s", ph->routeId,
627
ph->user.listenerId, station->id);
631
case PIANO_REQUEST_EXPLAIN: {
632
/* explain why particular song was played */
633
PianoRequestDataExplain_t *reqData = req->data;
635
assert (reqData != NULL);
636
assert (reqData->song != NULL);
638
snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
639
"<methodCall><methodName>playlist.narrative</methodName>"
640
"<params><param><value><int>%lu</int></value></param>"
642
"<param><value><string>%s</string></value></param>"
644
"<param><value><string>%s</string></value></param>"
646
"<param><value><string>%s</string></value></param>"
647
"</params></methodCall>", (unsigned long) timestamp,
648
ph->user.authToken, reqData->song->stationId,
649
reqData->song->musicId);
650
snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
651
"rid=%s&lid=%s&method=narrative&arg1=%s&arg2=%s",
652
ph->routeId, ph->user.listenerId, reqData->song->stationId,
653
reqData->song->musicId);
657
case PIANO_REQUEST_GET_SEED_SUGGESTIONS: {
658
/* find similar artists */
659
PianoRequestDataGetSeedSuggestions_t *reqData = req->data;
661
assert (reqData != NULL);
662
assert (reqData->musicId != NULL);
663
assert (reqData->max != 0);
665
snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
666
"<methodCall><methodName>music.getSeedSuggestions</methodName>"
667
"<params><param><value><int>%lu</int></value></param>"
669
"<param><value><string>%s</string></value></param>"
671
"<param><value><string>%s</string></value></param>"
673
"<param><value><string>%s</string></value></param>"
675
"<param><value><int>%u</int></value></param>"
676
"</params></methodCall>", (unsigned long) timestamp,
677
ph->user.authToken, reqData->station->id, reqData->musicId,
679
snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
680
"rid=%s&lid=%s&method=getSeedSuggestions&arg1=%s&arg2=%u",
681
ph->routeId, ph->user.listenerId, reqData->musicId, reqData->max);
685
case PIANO_REQUEST_BOOKMARK_SONG: {
687
PianoSong_t *song = req->data;
689
assert (song != NULL);
691
snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
692
"<methodCall><methodName>station.createBookmark</methodName>"
693
"<params><param><value><int>%lu</int></value></param>"
695
"<param><value><string>%s</string></value></param>"
697
"<param><value><string>%s</string></value></param>"
699
"<param><value><string>%s</string></value></param>"
700
"</params></methodCall>", (unsigned long) timestamp,
701
ph->user.authToken, song->stationId, song->musicId);
702
snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
703
"rid=%s&lid=%s&method=createBookmark&arg1=%s&arg2=%s",
704
ph->routeId, ph->user.listenerId, song->stationId,
709
case PIANO_REQUEST_BOOKMARK_ARTIST: {
710
/* bookmark artist */
711
PianoSong_t *song = req->data;
713
assert (song != NULL);
715
snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
716
"<methodCall><methodName>station.createArtistBookmark</methodName>"
717
"<params><param><value><int>%lu</int></value></param>"
719
"<param><value><string>%s</string></value></param>"
721
"<param><value><string>%s</string></value></param>"
722
"</params></methodCall>", (unsigned long) timestamp,
723
ph->user.authToken, song->artistMusicId);
724
snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
725
"rid=%s&lid=%s&method=createArtistBookmark&arg1=%s",
726
ph->routeId, ph->user.listenerId, song->artistMusicId);
730
case PIANO_REQUEST_GET_STATION_INFO: {
731
/* get station information (seeds and feedback) */
732
PianoRequestDataGetStationInfo_t *reqData = req->data;
734
assert (reqData != NULL);
735
assert (reqData->station != NULL);
737
snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
738
"<methodCall><methodName>station.getStation</methodName>"
739
"<params><param><value><int>%lu</int></value></param>"
741
"<param><value><string>%s</string></value></param>"
743
"<param><value><string>%s</string></value></param>"
744
"</params></methodCall>", (unsigned long) timestamp,
745
ph->user.authToken, reqData->station->id);
746
snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
747
"rid=%s&lid=%s&method=getStation&arg1=%s",
748
ph->routeId, ph->user.listenerId, reqData->station->id);
752
case PIANO_REQUEST_DELETE_FEEDBACK: {
753
PianoSong_t *song = req->data;
755
assert (song != NULL);
757
snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
758
"<methodCall><methodName>station.deleteFeedback</methodName>"
759
"<params><param><value><int>%lu</int></value></param>"
761
"<param><value><string>%s</string></value></param>"
763
"<param><value><string>%s</string></value></param>"
764
"</params></methodCall>", (unsigned long) timestamp,
765
ph->user.authToken, song->feedbackId);
766
snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
767
"rid=%s&lid=%s&method=deleteFeedback&arg1=%s",
768
ph->routeId, ph->user.listenerId, song->feedbackId);
772
case PIANO_REQUEST_DELETE_SEED: {
773
PianoRequestDataDeleteSeed_t *reqData = req->data;
776
assert (reqData != NULL);
777
assert (reqData->song != NULL || reqData->artist != NULL ||
778
reqData->station != NULL);
780
if (reqData->song != NULL) {
781
seedId = reqData->song->seedId;
782
} else if (reqData->artist != NULL) {
783
seedId = reqData->artist->seedId;
784
} else if (reqData->station != NULL) {
785
seedId = reqData->station->seedId;
788
assert (seedId != NULL);
790
snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
791
"<methodCall><methodName>station.deleteSeed</methodName>"
792
"<params><param><value><int>%lu</int></value></param>"
794
"<param><value><string>%s</string></value></param>"
796
"<param><value><string>%s</string></value></param>"
797
"</params></methodCall>", (unsigned long) timestamp,
798
ph->user.authToken, seedId);
799
snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
800
"rid=%s&lid=%s&method=deleteSeed&arg1=%s",
801
ph->routeId, ph->user.listenerId, seedId);
805
/* "high-level" wrapper */
806
case PIANO_REQUEST_RATE_SONG: {
808
PianoRequestDataRateSong_t *reqData = req->data;
811
assert (reqData != NULL);
812
assert (reqData->song != NULL);
813
assert (reqData->rating != PIANO_RATE_NONE);
815
PianoRequestDataAddFeedback_t transformedReqData;
816
transformedReqData.stationId = reqData->song->stationId;
817
transformedReqData.trackToken = reqData->song->trackToken;
818
transformedReqData.rating = reqData->rating;
819
req->data = &transformedReqData;
821
/* create request data (url, post data) */
822
pRet = PianoRequest (ph, req, PIANO_REQUEST_ADD_FEEDBACK);
823
/* and reset request type/data */
824
req->type = PIANO_REQUEST_RATE_SONG;
831
case PIANO_REQUEST_MOVE_SONG: {
832
/* move song to a different station, needs two requests */
833
PianoRequestDataMoveSong_t *reqData = req->data;
834
PianoRequestDataAddFeedback_t transformedReqData;
837
assert (reqData != NULL);
838
assert (reqData->song != NULL);
839
assert (reqData->from != NULL);
840
assert (reqData->to != NULL);
841
assert (reqData->step < 2);
843
transformedReqData.trackToken = reqData->song->trackToken;
844
req->data = &transformedReqData;
846
switch (reqData->step) {
848
transformedReqData.stationId = reqData->from->id;
849
transformedReqData.rating = PIANO_RATE_BAN;
853
transformedReqData.stationId = reqData->to->id;
854
transformedReqData.rating = PIANO_RATE_LOVE;
858
/* create request data (url, post data) */
859
pRet = PianoRequest (ph, req, PIANO_REQUEST_ADD_FEEDBACK);
860
/* and reset request type/data */
861
req->type = PIANO_REQUEST_MOVE_SONG;
869
if ((req->postData = PianoEncryptString (xmlSendBuf)) == NULL) {
870
return PIANO_RET_OUT_OF_MEMORY;
876
/* parse xml response and update data structures/return new data structure
877
* @param piano handle
878
* @param initialized request (expects responseData to be a NUL-terminated
881
PianoReturn_t PianoResponse (PianoHandle_t *ph, PianoRequest_t *req) {
882
PianoReturn_t ret = PIANO_RET_ERR;
885
assert (req != NULL);
888
case PIANO_REQUEST_LOGIN: {
889
/* authenticate user */
890
PianoRequestDataLogin_t *reqData = req->data;
892
assert (req->responseData != NULL);
893
assert (reqData != NULL);
895
switch (reqData->step) {
897
char *cryptedTimestamp = NULL;
899
assert (req->responseData != NULL);
901
/* abusing parseNarrative; has same xml structure */
902
ret = PianoXmlParseNarrative (req->responseData, &cryptedTimestamp);
903
if (cryptedTimestamp != NULL) {
904
unsigned long timestamp = 0;
905
time_t realTimestamp = time (NULL);
906
char *decryptedTimestamp = NULL, *decryptedPos = NULL;
909
if ((decryptedTimestamp = PianoDecryptString (cryptedTimestamp)) != NULL) {
910
decryptedPos = decryptedTimestamp;
911
/* skip four bytes garbage? at beginning */
912
while (i-- > 0 && *decryptedPos++ != '\0');
913
timestamp = strtoul (decryptedPos, NULL, 0);
914
ph->timeOffset = realTimestamp - timestamp;
916
free (decryptedTimestamp);
918
free (cryptedTimestamp);
920
ret = PIANO_RET_CONTINUE_REQUEST;
926
/* information exists when reauthenticating, destroy to
928
if (ph->user.listenerId != NULL) {
929
PianoDestroyUserInfo (&ph->user);
931
ret = PianoXmlParseUserinfo (ph, req->responseData);
937
case PIANO_REQUEST_GET_STATIONS:
939
assert (req->responseData != NULL);
941
ret = PianoXmlParseStations (ph, req->responseData);
944
case PIANO_REQUEST_GET_PLAYLIST: {
945
/* get playlist, usually four songs */
946
PianoRequestDataGetPlaylist_t *reqData = req->data;
948
assert (req->responseData != NULL);
949
assert (reqData != NULL);
951
reqData->retPlaylist = NULL;
952
ret = PianoXmlParsePlaylist (ph, req->responseData,
953
&reqData->retPlaylist);
957
case PIANO_REQUEST_RATE_SONG:
959
assert (req->responseData != NULL);
961
ret = PianoXmlParseSimple (req->responseData);
962
if (ret == PIANO_RET_OK) {
963
PianoRequestDataRateSong_t *reqData = req->data;
964
reqData->song->rating = reqData->rating;
968
case PIANO_REQUEST_ADD_FEEDBACK:
969
/* never ever use this directly, low-level call */
973
case PIANO_REQUEST_MOVE_SONG: {
974
/* move song to different station */
975
PianoRequestDataMoveSong_t *reqData = req->data;
977
assert (req->responseData != NULL);
978
assert (reqData != NULL);
979
assert (reqData->step < 2);
981
ret = PianoXmlParseSimple (req->responseData);
982
if (ret == PIANO_RET_OK && reqData->step == 0) {
983
ret = PIANO_RET_CONTINUE_REQUEST;
989
case PIANO_REQUEST_RENAME_STATION:
990
/* rename station and update PianoStation_t structure */
991
assert (req->responseData != NULL);
993
if ((ret = PianoXmlParseSimple (req->responseData)) == PIANO_RET_OK) {
994
PianoRequestDataRenameStation_t *reqData = req->data;
996
assert (reqData != NULL);
997
assert (reqData->station != NULL);
998
assert (reqData->newName != NULL);
1000
free (reqData->station->name);
1001
reqData->station->name = strdup (reqData->newName);
1005
case PIANO_REQUEST_DELETE_STATION:
1006
/* delete station from server and station list */
1007
assert (req->responseData != NULL);
1009
if ((ret = PianoXmlParseSimple (req->responseData)) == PIANO_RET_OK) {
1010
PianoStation_t *station = req->data;
1012
assert (station != NULL);
1014
/* delete station from local station list */
1015
PianoStation_t *curStation = ph->stations, *lastStation = NULL;
1016
while (curStation != NULL) {
1017
if (curStation == station) {
1018
if (lastStation != NULL) {
1019
lastStation->next = curStation->next;
1021
/* first station in list */
1022
ph->stations = curStation->next;
1024
PianoDestroyStation (curStation);
1028
lastStation = curStation;
1029
curStation = curStation->next;
1034
case PIANO_REQUEST_SEARCH: {
1035
/* search artist/song */
1036
PianoRequestDataSearch_t *reqData = req->data;
1038
assert (req->responseData != NULL);
1039
assert (reqData != NULL);
1041
ret = PianoXmlParseSearch (req->responseData, &reqData->searchResult);
1045
case PIANO_REQUEST_CREATE_STATION: {
1046
/* create station, insert new station into station list on success */
1047
assert (req->responseData != NULL);
1049
ret = PianoXmlParseCreateStation (ph, req->responseData);
1053
case PIANO_REQUEST_ADD_SEED: {
1054
/* add seed to station, updates station structure */
1055
PianoRequestDataAddSeed_t *reqData = req->data;
1057
assert (req->responseData != NULL);
1058
assert (reqData != NULL);
1059
assert (reqData->station != NULL);
1061
/* FIXME: update station data instead of replacing them */
1062
ret = PianoXmlParseAddSeed (ph, req->responseData, reqData->station);
1066
case PIANO_REQUEST_ADD_TIRED_SONG:
1067
case PIANO_REQUEST_SET_QUICKMIX:
1068
case PIANO_REQUEST_BOOKMARK_SONG:
1069
case PIANO_REQUEST_BOOKMARK_ARTIST:
1070
case PIANO_REQUEST_DELETE_FEEDBACK:
1071
assert (req->responseData != NULL);
1073
ret = PianoXmlParseSimple (req->responseData);
1076
case PIANO_REQUEST_GET_GENRE_STATIONS:
1077
/* get genre stations */
1078
assert (req->responseData != NULL);
1080
ret = PianoXmlParseGenreExplorer (ph, req->responseData);
1083
case PIANO_REQUEST_TRANSFORM_STATION: {
1084
/* transform shared station into private and update isCreator flag */
1085
PianoStation_t *station = req->data;
1087
assert (req->responseData != NULL);
1088
assert (station != NULL);
1090
/* though this call returns a bunch of "new" data only this one is
1091
* changed and important (at the moment) */
1092
if ((ret = PianoXmlParseTranformStation (req->responseData)) ==
1094
station->isCreator = 1;
1099
case PIANO_REQUEST_EXPLAIN: {
1100
/* explain why song was selected */
1101
PianoRequestDataExplain_t *reqData = req->data;
1103
assert (req->responseData != NULL);
1104
assert (reqData != NULL);
1106
ret = PianoXmlParseNarrative (req->responseData, &reqData->retExplain);
1110
case PIANO_REQUEST_GET_SEED_SUGGESTIONS: {
1111
/* find similar artists */
1112
PianoRequestDataGetSeedSuggestions_t *reqData = req->data;
1114
assert (req->responseData != NULL);
1115
assert (reqData != NULL);
1117
ret = PianoXmlParseSeedSuggestions (req->responseData,
1118
&reqData->searchResult);
1122
case PIANO_REQUEST_GET_STATION_INFO: {
1123
/* get station information (seeds and feedback) */
1124
PianoRequestDataGetStationInfo_t *reqData = req->data;
1126
assert (req->responseData != NULL);
1127
assert (reqData != NULL);
1129
ret = PianoXmlParseGetStationInfo (req->responseData,
1134
case PIANO_REQUEST_DELETE_SEED: {
1135
assert (req->responseData != NULL);
1137
/* dummy function, checks for errors only */
1138
ret = PianoXmlParseTranformStation (req->responseData);
1145
209
/* get station from list by id
1146
210
* @param search here
1147
211
* @param search for this