/* search for music (artist or track), needed to create new station; don't * forget to free the search result; beware! searchResult will be nulled * by PianoXmlParseSearch * @public yes * @param piano handle * @param utf-8 search string * @param return search result */ PianoReturn_t PianoSearchMusic (PianoHandle_t *ph, const char *searchStr, PianoSearchResult_t *searchResult) { char xmlSendBuf[PIANO_SEND_BUFFER_SIZE], *retStr; char *xmlencodedSearchStr, *urlencodedSearchStr; PianoReturn_t ret; if ((xmlencodedSearchStr = PianoXmlEncodeString (searchStr)) == NULL) { return PIANO_RET_OUT_OF_MEMORY; } snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>" "<methodCall><methodName>music.search</methodName>" "<params><param><value><int>%li</int></value></param>" "<param><value><string>%s</string></value></param>" "<param><value><string>%s</string></value></param>" "</params></methodCall>", time (NULL), ph->user.authToken, xmlencodedSearchStr); urlencodedSearchStr = WaitressUrlEncode (searchStr); snprintf (ph->waith.path, sizeof (ph->waith.path), PIANO_RPC_PATH "rid=%s&lid=%s&method=search&arg1=%s", ph->routeId, ph->user.listenerId, urlencodedSearchStr); if ((ret = PianoHttpPost (&ph->waith, xmlSendBuf, &retStr)) == PIANO_RET_OK) { ret = PianoXmlParseSearch (retStr, searchResult); PianoFree (retStr, 0); } PianoFree (urlencodedSearchStr, 0); PianoFree (xmlencodedSearchStr, 0); return ret; }
/* parse xml response and update data structures/return new data structure * @param piano handle * @param initialized request (expects responseData to be a NUL-terminated * string) */ PianoReturn_t PianoResponse (PianoHandle_t *ph, PianoRequest_t *req) { PianoReturn_t ret = PIANO_RET_ERR; assert (ph != NULL); assert (req != NULL); switch (req->type) { case PIANO_REQUEST_LOGIN: { /* authenticate user */ PianoRequestDataLogin_t *reqData = req->data; assert (req->responseData != NULL); assert (reqData != NULL); switch (reqData->step) { case 0: { char *cryptedTimestamp = NULL; assert (req->responseData != NULL); /* abusing parseNarrative; has same xml structure */ ret = PianoXmlParseNarrative (req->responseData, &cryptedTimestamp); if (cryptedTimestamp != NULL) { unsigned long timestamp = 0; time_t realTimestamp = time (NULL); char *decryptedTimestamp = NULL, *decryptedPos = NULL; unsigned char i = 4; if ((decryptedTimestamp = PianoDecryptString (cryptedTimestamp)) != NULL) { decryptedPos = decryptedTimestamp; /* skip four bytes garbage? at beginning */ while (i-- > 0 && *decryptedPos++ != '\0'); timestamp = strtoul (decryptedPos, NULL, 0); ph->timeOffset = (int)realTimestamp - (int)timestamp; free (decryptedTimestamp); } free (cryptedTimestamp); } ret = PIANO_RET_CONTINUE_REQUEST; ++reqData->step; break; } case 1: /* information exists when reauthenticating, destroy to * avoid memleak */ if (ph->user.listenerId != NULL) { PianoDestroyUserInfo (&ph->user); } ret = PianoXmlParseUserinfo (ph, req->responseData); break; } break; } case PIANO_REQUEST_GET_STATIONS: /* get stations */ assert (req->responseData != NULL); ret = PianoXmlParseStations (ph, req->responseData); break; case PIANO_REQUEST_GET_PLAYLIST: { /* get playlist, usually four songs */ PianoRequestDataGetPlaylist_t *reqData = req->data; assert (req->responseData != NULL); assert (reqData != NULL); reqData->retPlaylist = NULL; ret = PianoXmlParsePlaylist (ph, req->responseData, &reqData->retPlaylist); break; } case PIANO_REQUEST_RATE_SONG: /* love/ban song */ assert (req->responseData != NULL); ret = PianoXmlParseSimple (req->responseData); if (ret == PIANO_RET_OK) { PianoRequestDataRateSong_t *reqData = req->data; reqData->song->rating = reqData->rating; } break; case PIANO_REQUEST_ADD_FEEDBACK: /* never ever use this directly, low-level call */ assert (0); break; case PIANO_REQUEST_MOVE_SONG: { /* move song to different station */ PianoRequestDataMoveSong_t *reqData = req->data; assert (req->responseData != NULL); assert (reqData != NULL); assert (reqData->step < 2); ret = PianoXmlParseSimple (req->responseData); if (ret == PIANO_RET_OK && reqData->step == 0) { ret = PIANO_RET_CONTINUE_REQUEST; ++reqData->step; } break; } case PIANO_REQUEST_RENAME_STATION: /* rename station and update PianoStation_t structure */ assert (req->responseData != NULL); if ((ret = PianoXmlParseSimple (req->responseData)) == PIANO_RET_OK) { PianoRequestDataRenameStation_t *reqData = req->data; assert (reqData != NULL); assert (reqData->station != NULL); assert (reqData->newName != NULL); free (reqData->station->name); reqData->station->name = strdup (reqData->newName); } break; case PIANO_REQUEST_DELETE_STATION: /* delete station from server and station list */ assert (req->responseData != NULL); if ((ret = PianoXmlParseSimple (req->responseData)) == PIANO_RET_OK) { PianoStation_t *station = req->data; assert (station != NULL); /* delete station from local station list */ PianoStation_t *curStation = ph->stations, *lastStation = NULL; while (curStation != NULL) { if (curStation == station) { if (lastStation != NULL) { lastStation->next = curStation->next; } else { /* first station in list */ ph->stations = curStation->next; } PianoDestroyStation (curStation); free (curStation); break; } lastStation = curStation; curStation = curStation->next; } } break; case PIANO_REQUEST_SEARCH: { /* search artist/song */ PianoRequestDataSearch_t *reqData = req->data; assert (req->responseData != NULL); assert (reqData != NULL); ret = PianoXmlParseSearch (req->responseData, &reqData->searchResult); break; } case PIANO_REQUEST_CREATE_STATION: { /* create station, insert new station into station list on success */ assert (req->responseData != NULL); ret = PianoXmlParseCreateStation (ph, req->responseData); break; } case PIANO_REQUEST_ADD_SEED: { /* add seed to station, updates station structure */ PianoRequestDataAddSeed_t *reqData = req->data; assert (req->responseData != NULL); assert (reqData != NULL); assert (reqData->station != NULL); /* FIXME: update station data instead of replacing them */ ret = PianoXmlParseAddSeed (ph, req->responseData, reqData->station); break; } case PIANO_REQUEST_ADD_TIRED_SONG: case PIANO_REQUEST_SET_QUICKMIX: case PIANO_REQUEST_BOOKMARK_SONG: case PIANO_REQUEST_BOOKMARK_ARTIST: case PIANO_REQUEST_DELETE_FEEDBACK: assert (req->responseData != NULL); ret = PianoXmlParseSimple (req->responseData); break; case PIANO_REQUEST_GET_GENRE_STATIONS: /* get genre stations */ assert (req->responseData != NULL); ret = PianoXmlParseGenreExplorer (ph, req->responseData); break; case PIANO_REQUEST_TRANSFORM_STATION: { /* transform shared station into private and update isCreator flag */ PianoStation_t *station = req->data; assert (req->responseData != NULL); assert (station != NULL); /* though this call returns a bunch of "new" data only this one is * changed and important (at the moment) */ if ((ret = PianoXmlParseTranformStation (req->responseData)) == PIANO_RET_OK) { station->isCreator = 1; } break; } case PIANO_REQUEST_EXPLAIN: { /* explain why song was selected */ PianoRequestDataExplain_t *reqData = req->data; assert (req->responseData != NULL); assert (reqData != NULL); ret = PianoXmlParseNarrative (req->responseData, &reqData->retExplain); break; } case PIANO_REQUEST_GET_SEED_SUGGESTIONS: { /* find similar artists */ PianoRequestDataGetSeedSuggestions_t *reqData = req->data; assert (req->responseData != NULL); assert (reqData != NULL); ret = PianoXmlParseSeedSuggestions (req->responseData, &reqData->searchResult); break; } case PIANO_REQUEST_GET_STATION_INFO: { /* get station information (seeds and feedback) */ PianoRequestDataGetStationInfo_t *reqData = req->data; assert (req->responseData != NULL); assert (reqData != NULL); ret = PianoXmlParseGetStationInfo (req->responseData, &reqData->info); break; } case PIANO_REQUEST_DELETE_SEED: { assert (req->responseData != NULL); /* dummy function, checks for errors only */ ret = PianoXmlParseTranformStation (req->responseData); } } return ret; }