summaryrefslogtreecommitdiff
path: root/libpiano/src/piano.c
diff options
context:
space:
mode:
authorLars-Dominik Braun <PromyLOPh@lavabit.com>2010-11-23 21:59:31 +0100
committerLars-Dominik Braun <lars@6xq.net>2010-12-26 15:47:42 +0100
commit13106ac8bb95e325cf522817f91ad1f3b0fcecb0 (patch)
tree5488d884b5fac8bf5f1946cae92e536fa2d0d345 /libpiano/src/piano.c
parentd074480b9159a53fc4e6bdb40463289c69c2f6a7 (diff)
downloadpianobar-windows-13106ac8bb95e325cf522817f91ad1f3b0fcecb0.tar.gz
pianobar-windows-13106ac8bb95e325cf522817f91ad1f3b0fcecb0.tar.bz2
pianobar-windows-13106ac8bb95e325cf522817f91ad1f3b0fcecb0.zip
Better directory layout
Removed useless AUTHORS, COPYING and README files. Move manpage to contrib (it's not exactly source code).
Diffstat (limited to 'libpiano/src/piano.c')
-rw-r--r--libpiano/src/piano.c1114
1 files changed, 0 insertions, 1114 deletions
diff --git a/libpiano/src/piano.c b/libpiano/src/piano.c
deleted file mode 100644
index f6343b4..0000000
--- a/libpiano/src/piano.c
+++ /dev/null
@@ -1,1114 +0,0 @@
-/*
-Copyright (c) 2008-2010
- Lars-Dominik Braun <PromyLOPh@lavabit.com>
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
-*/
-
-#define _BSD_SOURCE /* required by strdup() */
-
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-#include <time.h>
-#include <assert.h>
-#include <stdint.h>
-
-/* needed for urlencode */
-#include <waitress.h>
-
-#include "piano_private.h"
-#include "piano.h"
-#include "xml.h"
-#include "crypt.h"
-#include "config.h"
-
-#define PIANO_PROTOCOL_VERSION "29"
-#define PIANO_RPC_HOST "www.pandora.com"
-#define PIANO_RPC_PORT "80"
-#define PIANO_RPC_PATH "/radio/xmlrpc/v" PIANO_PROTOCOL_VERSION "?"
-#define PIANO_SEND_BUFFER_SIZE 10000
-
-/* initialize piano handle
- * @param piano handle
- * @return nothing
- */
-void PianoInit (PianoHandle_t *ph) {
- memset (ph, 0, sizeof (*ph));
-
- /* route-id seems to be random. we're using time anyway... */
- snprintf (ph->routeId, sizeof (ph->routeId), "%07luP",
- (unsigned long) time (NULL) % 10000000);
-}
-
-/* free complete search result
- * @public yes
- * @param search result
- */
-void PianoDestroySearchResult (PianoSearchResult_t *searchResult) {
- PianoArtist_t *curArtist, *lastArtist;
- PianoSong_t *curSong, *lastSong;
-
- curArtist = searchResult->artists;
- while (curArtist != NULL) {
- free (curArtist->name);
- free (curArtist->musicId);
- lastArtist = curArtist;
- curArtist = curArtist->next;
- free (lastArtist);
- }
-
- curSong = searchResult->songs;
- while (curSong != NULL) {
- free (curSong->title);
- free (curSong->artist);
- free (curSong->musicId);
- lastSong = curSong;
- curSong = curSong->next;
- free (lastSong);
- }
-}
-
-/* free single station
- * @param station
- */
-void PianoDestroyStation (PianoStation_t *station) {
- free (station->name);
- free (station->id);
- memset (station, 0, sizeof (station));
-}
-
-/* free complete station list
- * @param piano handle
- */
-void PianoDestroyStations (PianoStation_t *stations) {
- PianoStation_t *curStation, *lastStation;
-
- curStation = stations;
- while (curStation != NULL) {
- lastStation = curStation;
- curStation = curStation->next;
- PianoDestroyStation (lastStation);
- free (lastStation);
- }
-}
-
-/* FIXME: copy & waste */
-/* free _all_ elements of playlist
- * @param piano handle
- * @return nothing
- */
-void PianoDestroyPlaylist (PianoSong_t *playlist) {
- PianoSong_t *curSong, *lastSong;
-
- curSong = playlist;
- while (curSong != NULL) {
- free (curSong->audioUrl);
- free (curSong->coverArt);
- free (curSong->artist);
- free (curSong->musicId);
- free (curSong->title);
- free (curSong->userSeed);
- free (curSong->stationId);
- free (curSong->album);
- free (curSong->artistMusicId);
- lastSong = curSong;
- curSong = curSong->next;
- free (lastSong);
- }
-}
-
-/* destroy genre linked list
- */
-void PianoDestroyGenres (PianoGenre_t *genres) {
- PianoGenre_t *curGenre, *lastGenre;
-
- curGenre = genres;
- while (curGenre != NULL) {
- free (curGenre->name);
- free (curGenre->musicId);
- lastGenre = curGenre;
- curGenre = curGenre->next;
- free (lastGenre);
- }
-}
-
-/* destroy user information
- */
-void PianoDestroyUserInfo (PianoUserInfo_t *user) {
- free (user->webAuthToken);
- free (user->authToken);
- free (user->listenerId);
-}
-
-/* frees the whole piano handle structure
- * @param piano handle
- * @return nothing
- */
-void PianoDestroy (PianoHandle_t *ph) {
- PianoDestroyUserInfo (&ph->user);
- PianoDestroyStations (ph->stations);
- /* destroy genre stations */
- PianoGenreCategory_t *curGenreCat = ph->genreStations, *lastGenreCat;
- while (curGenreCat != NULL) {
- PianoDestroyGenres (curGenreCat->genres);
- free (curGenreCat->name);
- lastGenreCat = curGenreCat;
- curGenreCat = curGenreCat->next;
- free (lastGenreCat);
- }
- memset (ph, 0, sizeof (*ph));
-}
-
-/* destroy request, free post data. req->responseData is *not* freed here, as
- * it might be allocated by something else than malloc!
- * @param piano request
- */
-void PianoDestroyRequest (PianoRequest_t *req) {
- free (req->postData);
- memset (req, 0, sizeof (*req));
-}
-
-/* convert audio format id to string that can be used in xml requests
- * @param format id
- * @return constant string
- */
-static const char *PianoAudioFormatToString (PianoAudioFormat_t format) {
- switch (format) {
- case PIANO_AF_AACPLUS:
- return "aacplus";
- break;
-
- case PIANO_AF_MP3:
- return "mp3";
- break;
-
- case PIANO_AF_MP3_HI:
- return "mp3-hifi";
- break;
-
- default:
- return NULL;
- break;
- }
-}
-
-/* prepare piano request (initializes request type, urlpath and postData)
- * @param piano handle
- * @param request structure
- * @param request type
- */
-PianoReturn_t PianoRequest (PianoHandle_t *ph, PianoRequest_t *req,
- PianoRequestType_t type) {
- char xmlSendBuf[PIANO_SEND_BUFFER_SIZE];
- /* corrected timestamp */
- time_t timestamp = time (NULL) - ph->timeOffset;
-
- assert (ph != NULL);
- assert (req != NULL);
-
- req->type = type;
-
- switch (req->type) {
- case PIANO_REQUEST_LOGIN: {
- /* authenticate user */
- PianoRequestDataLogin_t *logindata = req->data;
-
- assert (logindata != NULL);
-
- switch (logindata->step) {
- case 0:
- snprintf (xmlSendBuf, sizeof (xmlSendBuf),
- "<?xml version=\"1.0\"?><methodCall>"
- "<methodName>misc.sync</methodName>"
- "<params></params></methodCall>");
- snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
- "rid=%s&method=sync", ph->routeId);
- break;
-
- case 1:
- snprintf (xmlSendBuf, sizeof (xmlSendBuf),
- "<?xml version=\"1.0\"?><methodCall>"
- "<methodName>listener.authenticateListener</methodName>"
- "<params><param><value><int>%lu</int></value></param>"
- "<param><value><string>%s</string></value></param>"
- "<param><value><string>%s</string></value></param>"
- "</params></methodCall>", (unsigned long) timestamp,
- logindata->user, logindata->password);
- snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
- "rid=%s&method=authenticateListener", ph->routeId);
- break;
- }
- break;
- }
-
- case PIANO_REQUEST_GET_STATIONS:
- /* get stations, user must be authenticated */
- assert (ph->user.listenerId != NULL);
-
- snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
- "<methodCall><methodName>station.getStations</methodName>"
- "<params><param><value><int>%lu</int></value></param>"
- "<param><value><string>%s</string></value></param>"
- "</params></methodCall>", (unsigned long) timestamp,
- ph->user.authToken);
- snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
- "rid=%s&lid=%s&method=getStations", ph->routeId,
- ph->user.listenerId);
- break;
-
- case PIANO_REQUEST_GET_PLAYLIST: {
- /* get playlist for specified station */
- PianoRequestDataGetPlaylist_t *reqData = req->data;
-
- assert (reqData != NULL);
- assert (reqData->station != NULL);
- assert (reqData->station->id != NULL);
- assert (reqData->format != PIANO_AF_UNKNOWN);
-
- snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
- "<methodCall><methodName>playlist.getFragment</methodName>"
- "<params><param><value><int>%lu</int></value></param>"
- /* auth token */
- "<param><value><string>%s</string></value></param>"
- /* station id */
- "<param><value><string>%s</string></value></param>"
- /* total listening time */
- "<param><value><string>0</string></value></param>"
- /* time since last session */
- "<param><value><string></string></value></param>"
- /* tracking code */
- "<param><value><string></string></value></param>"
- /* audio format */
- "<param><value><string>%s</string></value></param>"
- /* delta listening time */
- "<param><value><string>0</string></value></param>"
- /* listening timestamp */
- "<param><value><string>0</string></value></param>"
- "</params></methodCall>", (unsigned long) timestamp,
- ph->user.authToken, reqData->station->id,
- PianoAudioFormatToString (reqData->format));
- snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
- "rid=%s&lid=%s&method=getFragment&arg1=%s&arg2=0"
- "&arg3=&arg4=&arg5=%s&arg6=0&arg7=0", ph->routeId,
- ph->user.listenerId, reqData->station->id,
- PianoAudioFormatToString (reqData->format));
- break;
- }
-
- case PIANO_REQUEST_ADD_FEEDBACK: {
- /* low-level, don't use directly (see _RATE_SONG and _MOVE_SONG) */
- PianoRequestDataAddFeedback_t *reqData = req->data;
-
- assert (reqData != NULL);
- assert (reqData->stationId != NULL);
- assert (reqData->musicId != NULL);
- assert (reqData->rating != PIANO_RATE_NONE);
-
- snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
- "<methodCall><methodName>station.addFeedback</methodName>"
- "<params><param><value><int>%lu</int></value></param>"
- /* auth token */
- "<param><value><string>%s</string></value></param>"
- /* station id */
- "<param><value><string>%s</string></value></param>"
- /* music id */
- "<param><value><string>%s</string></value></param>"
- /* user seed */
- "<param><value><string>%s</string></value></param>"
- /* test strategy */
- "<param><value>%u</value></param>"
- /* positive */
- "<param><value><boolean>%i</boolean></value></param>"
- /* "is-creator-quickmix" */
- "<param><value><boolean>0</boolean></value></param>"
- /* song type */
- "<param><value><int>%u</int></value></param>"
- "</params></methodCall>", (unsigned long) timestamp,
- ph->user.authToken, reqData->stationId, reqData->musicId,
- (reqData->userSeed == NULL) ? "" : reqData->userSeed,
- reqData->testStrategy,
- (reqData->rating == PIANO_RATE_LOVE) ? 1 : 0,
- reqData->songType);
- snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
- "rid=%s&lid=%s&method=addFeedback&arg1=%s&arg2=%s"
- "&arg3=%s&arg4=%u&arg5=%s&arg6=false&arg7=%u",
- ph->routeId, ph->user.listenerId, reqData->stationId,
- reqData->musicId,
- (reqData->userSeed == NULL) ? "" : reqData->userSeed,
- reqData->testStrategy,
- (reqData->rating == PIANO_RATE_LOVE) ? "true" : "false",
- reqData->songType);
- break;
- }
-
- case PIANO_REQUEST_RENAME_STATION: {
- /* rename stations */
- PianoRequestDataRenameStation_t *reqData = req->data;
- char *urlencodedNewName, *xmlencodedNewName;
-
- assert (reqData != NULL);
- assert (reqData->station != NULL);
- assert (reqData->newName != NULL);
-
- if ((xmlencodedNewName = PianoXmlEncodeString (reqData->newName)) == NULL) {
- return PIANO_RET_OUT_OF_MEMORY;
- }
- urlencodedNewName = WaitressUrlEncode (reqData->newName);
-
- snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
- "<methodCall><methodName>station.setStationName</methodName>"
- "<params><param><value><int>%lu</int></value></param>"
- /* auth token */
- "<param><value><string>%s</string></value></param>"
- /* station id */
- "<param><value><string>%s</string></value></param>"
- /* new name */
- "<param><value><string>%s</string></value></param>"
- "</params></methodCall>", (unsigned long) timestamp,
- ph->user.authToken, reqData->station->id,
- xmlencodedNewName);
- snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
- "rid=%s&lid=%s&method=setStationName&arg1=%s&arg2=%s",
- ph->routeId, ph->user.listenerId, reqData->station->id,
- urlencodedNewName);
-
- free (urlencodedNewName);
- free (xmlencodedNewName);
- break;
- }
-
- case PIANO_REQUEST_DELETE_STATION: {
- /* delete station */
- PianoStation_t *station = req->data;
-
- assert (station != NULL);
-
- snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
- "<methodCall><methodName>station.removeStation</methodName>"
- "<params><param><value><int>%lu</int></value></param>"
- /* auth token */
- "<param><value><string>%s</string></value></param>"
- /* station id */
- "<param><value><string>%s</string></value></param>"
- "</params></methodCall>", (unsigned long) timestamp,
- ph->user.authToken, station->id);
- snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
- "rid=%s&lid=%s&method=removeStation&arg1=%s", ph->routeId,
- ph->user.listenerId, station->id);
- break;
- }
-
- case PIANO_REQUEST_SEARCH: {
- /* search for artist/song title */
- PianoRequestDataSearch_t *reqData = req->data;
- char *xmlencodedSearchStr, *urlencodedSearchStr;
-
- assert (reqData != NULL);
- assert (reqData->searchStr != NULL);
-
- if ((xmlencodedSearchStr = PianoXmlEncodeString (reqData->searchStr)) == NULL) {
- return PIANO_RET_OUT_OF_MEMORY;
- }
- urlencodedSearchStr = WaitressUrlEncode (reqData->searchStr);
-
- snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
- "<methodCall><methodName>music.search</methodName>"
- "<params><param><value><int>%lu</int></value></param>"
- /* auth token */
- "<param><value><string>%s</string></value></param>"
- /* search string */
- "<param><value><string>%s</string></value></param>"
- "</params></methodCall>", (unsigned long) timestamp,
- ph->user.authToken, xmlencodedSearchStr);
- snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
- "rid=%s&lid=%s&method=search&arg1=%s", ph->routeId,
- ph->user.listenerId, urlencodedSearchStr);
-
- free (urlencodedSearchStr);
- free (xmlencodedSearchStr);
- break;
- }
-
- case PIANO_REQUEST_CREATE_STATION: {
- /* create new station from specified musicid (type=mi, get one by
- * performing a search) or shared station id (type=sh) */
- PianoRequestDataCreateStation_t *reqData = req->data;
-
- assert (reqData != NULL);
- assert (reqData->id != NULL);
- assert (reqData->type != NULL);
-
- snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
- "<methodCall><methodName>station.createStation</methodName>"
- "<params><param><value><int>%lu</int></value></param>"
- "<param><value><string>%s</string></value></param>"
- "<param><value><string>%s%s</string></value></param>"
- "</params></methodCall>", (unsigned long) timestamp,
- ph->user.authToken, reqData->type, reqData->id);
-
- snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
- "rid=%s&lid=%s&method=createStation&arg1=%s%s", ph->routeId,
- ph->user.listenerId, reqData->type, reqData->id);
- break;
- }
-
- case PIANO_REQUEST_ADD_SEED: {
- /* add another seed to specified station */
- PianoRequestDataAddSeed_t *reqData = req->data;
-
- assert (reqData != NULL);
- assert (reqData->station != NULL);
- assert (reqData->musicId != NULL);
-
- snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
- "<methodCall><methodName>station.addSeed</methodName><params>"
- "<param><value><int>%lu</int></value></param>"
- "<param><value><string>%s</string></value></param>"
- "<param><value><string>%s</string></value></param>"
- "<param><value><string>%s</string></value></param>"
- "</params></methodCall>", (unsigned long) timestamp,
- ph->user.authToken, reqData->station->id, reqData->musicId);
- snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
- "rid=%s&lid=%s&method=addSeed&arg1=%s&arg2=%s", ph->routeId,
- ph->user.listenerId, reqData->station->id, reqData->musicId);
- break;
- }
-
- case PIANO_REQUEST_ADD_TIRED_SONG: {
- /* ban song for a month from all stations */
- PianoSong_t *song = req->data;
-
- assert (song != NULL);
-
- snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
- "<methodCall><methodName>listener.addTiredSong</methodName><params>"
- "<param><value><int>%lu</int></value></param>"
- "<param><value><string>%s</string></value></param>"
- /* key */
- "<param><value><string>%s</string></value></param>"
- /* user seed */
- "<param><value><string>%s</string></value></param>"
- /* station id */
- "<param><value><string>%s</string></value></param>"
- "</params></methodCall>", (unsigned long) timestamp,
- ph->user.authToken,
- (song->musicId == NULL) ? "" : song->musicId,
- (song->userSeed == NULL) ? "" : song->userSeed,
- song->stationId);
- snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
- "rid=%s&lid=%s&method=addTiredSong&arg1=%s&arg2=%s&arg3=%s",
- ph->routeId, ph->user.listenerId,
- (song->musicId == NULL) ? "" : song->musicId,
- (song->userSeed == NULL) ? "" : song->userSeed,
- song->stationId);
- break;
- }
-
- case PIANO_REQUEST_SET_QUICKMIX: {
- /* select stations included in quickmix (see useQuickMix flag of
- * PianoStation_t) */
- char valueBuf[1000], urlArgBuf[1000];
- PianoStation_t *curStation = ph->stations;
-
- memset (urlArgBuf, 0, sizeof (urlArgBuf));
- snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
- "<methodCall><methodName>station.setQuickMix</methodName><params>"
- "<param><value><int>%lu</int></value></param>"
- "<param><value><string>%s</string></value></param>"
- /* quick mix type */
- "<param><value><string>RANDOM</string></value></param>"
- "<param><value><array><data>", (unsigned long) timestamp,
- ph->user.authToken);
- while (curStation != NULL) {
- /* quick mix can't contain itself */
- if (!curStation->useQuickMix || curStation->isQuickMix) {
- curStation = curStation->next;
- continue;
- }
- /* append to xml doc */
- snprintf (valueBuf, sizeof (valueBuf),
- "<value><string>%s</string></value>", curStation->id);
- strncat (xmlSendBuf, valueBuf, sizeof (xmlSendBuf) -
- strlen (xmlSendBuf) - 1);
- /* append to url arg */
- strncat (urlArgBuf, curStation->id, sizeof (urlArgBuf) -
- strlen (urlArgBuf) - 1);
- curStation = curStation->next;
- /* if not last item: append "," */
- if (curStation != NULL) {
- strncat (urlArgBuf, "%2C", sizeof (urlArgBuf) -
- strlen (urlArgBuf) - 1);
- }
- }
- strncat (xmlSendBuf,
- "</data></array></value></param>"
- /* content type */
- "<param><value><string>CUSTOM</string></value></param>"
- /* genre */
- "<param><value><string></string></value></param>"
- "</params></methodCall>",
- sizeof (xmlSendBuf) - strlen (xmlSendBuf) - 1);
-
- snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
- "rid=%s&lid=%s&method=setQuickMix&arg1=RANDOM&arg2=%s&arg3=CUSTOM&arg4=",
- ph->routeId, ph->user.listenerId, urlArgBuf);
- break;
- }
-
- case PIANO_REQUEST_GET_GENRE_STATIONS:
- /* receive list of pandora's genre stations */
- xmlSendBuf[0] = '\0';
- snprintf (req->urlPath, sizeof (req->urlPath), "/xml/genre?r=%lu",
- (unsigned long) timestamp);
- break;
-
- case PIANO_REQUEST_TRANSFORM_STATION: {
- /* transform shared station into private */
- PianoStation_t *station = req->data;
-
- assert (station != NULL);
-
- snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
- "<methodCall><methodName>station.transformShared</methodName>"
- "<params><param><value><int>%lu</int></value></param>"
- /* auth token */
- "<param><value><string>%s</string></value></param>"
- /* station id */
- "<param><value><string>%s</string></value></param>"
- "</params></methodCall>", (unsigned long) timestamp,
- ph->user.authToken, station->id);
- snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
- "rid=%s&lid=%s&method=transformShared&arg1=%s", ph->routeId,
- ph->user.listenerId, station->id);
- break;
- }
-
- case PIANO_REQUEST_EXPLAIN: {
- /* explain why particular song was played */
- PianoRequestDataExplain_t *reqData = req->data;
-
- assert (reqData != NULL);
- assert (reqData->song != NULL);
-
- snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
- "<methodCall><methodName>playlist.narrative</methodName>"
- "<params><param><value><int>%lu</int></value></param>"
- /* auth token */
- "<param><value><string>%s</string></value></param>"
- /* station id */
- "<param><value><string>%s</string></value></param>"
- /* music id */
- "<param><value><string>%s</string></value></param>"
- "</params></methodCall>", (unsigned long) timestamp,
- ph->user.authToken, reqData->song->stationId,
- reqData->song->musicId);
- snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
- "rid=%s&lid=%s&method=narrative&arg1=%s&arg2=%s",
- ph->routeId, ph->user.listenerId, reqData->song->stationId,
- reqData->song->musicId);
- break;
- }
-
- case PIANO_REQUEST_GET_SEED_SUGGESTIONS: {
- /* find similar artists */
- PianoRequestDataGetSeedSuggestions_t *reqData = req->data;
-
- assert (reqData != NULL);
- assert (reqData->musicId != NULL);
- assert (reqData->max != 0);
-
- snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
- "<methodCall><methodName>music.getSeedSuggestions</methodName>"
- "<params><param><value><int>%lu</int></value></param>"
- /* auth token */
- "<param><value><string>%s</string></value></param>"
- /* seed music id */
- "<param><value><string>%s</string></value></param>"
- /* max */
- "<param><value><int>%u</int></value></param>"
- "</params></methodCall>", (unsigned long) timestamp,
- ph->user.authToken, reqData->musicId, reqData->max);
- snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
- "rid=%s&lid=%s&method=getSeedSuggestions&arg1=%s&arg2=%u",
- ph->routeId, ph->user.listenerId, reqData->musicId, reqData->max);
- break;
- }
-
- case PIANO_REQUEST_BOOKMARK_SONG: {
- /* bookmark song */
- PianoSong_t *song = req->data;
-
- assert (song != NULL);
-
- snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
- "<methodCall><methodName>station.createBookmark</methodName>"
- "<params><param><value><int>%lu</int></value></param>"
- /* auth token */
- "<param><value><string>%s</string></value></param>"
- /* station id */
- "<param><value><string>%s</string></value></param>"
- /* music id */
- "<param><value><string>%s</string></value></param>"
- "</params></methodCall>", (unsigned long) timestamp,
- ph->user.authToken, song->stationId, song->musicId);
- snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
- "rid=%s&lid=%s&method=createBookmark&arg1=%s&arg2=%s",
- ph->routeId, ph->user.listenerId, song->stationId,
- song->musicId);
- break;
- }
-
- case PIANO_REQUEST_BOOKMARK_ARTIST: {
- /* bookmark artist */
- PianoSong_t *song = req->data;
-
- assert (song != NULL);
-
- snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
- "<methodCall><methodName>station.createArtistBookmark</methodName>"
- "<params><param><value><int>%lu</int></value></param>"
- /* auth token */
- "<param><value><string>%s</string></value></param>"
- /* music id */
- "<param><value><string>%s</string></value></param>"
- "</params></methodCall>", (unsigned long) timestamp,
- ph->user.authToken, song->artistMusicId);
- snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
- "rid=%s&lid=%s&method=createArtistBookmark&arg1=%s",
- ph->routeId, ph->user.listenerId, song->artistMusicId);
- break;
- }
-
- /* "high-level" wrapper */
- case PIANO_REQUEST_RATE_SONG: {
- /* love/ban song */
- PianoRequestDataRateSong_t *reqData = req->data;
- PianoReturn_t pRet;
-
- assert (reqData != NULL);
- assert (reqData->song != NULL);
- assert (reqData->rating != PIANO_RATE_NONE);
-
- PianoRequestDataAddFeedback_t transformedReqData;
- transformedReqData.stationId = reqData->song->stationId;
- transformedReqData.musicId = reqData->song->musicId;
- transformedReqData.userSeed = reqData->song->userSeed;
- transformedReqData.rating = reqData->rating;
- transformedReqData.testStrategy = reqData->song->testStrategy;
- transformedReqData.songType = reqData->song->songType;
- req->data = &transformedReqData;
-
- /* create request data (url, post data) */
- pRet = PianoRequest (ph, req, PIANO_REQUEST_ADD_FEEDBACK);
- /* and reset request type/data */
- req->type = PIANO_REQUEST_RATE_SONG;
- req->data = reqData;
-
- return pRet;
- break;
- }
-
- case PIANO_REQUEST_MOVE_SONG: {
- /* move song to a different station, needs two requests */
- PianoRequestDataMoveSong_t *reqData = req->data;
- PianoRequestDataAddFeedback_t transformedReqData;
- PianoReturn_t pRet;
-
- assert (reqData != NULL);
- assert (reqData->song != NULL);
- assert (reqData->from != NULL);
- assert (reqData->to != NULL);
- assert (reqData->step < 2);
-
- transformedReqData.musicId = reqData->song->musicId;
- transformedReqData.userSeed = "";
- transformedReqData.songType = reqData->song->songType;
- transformedReqData.testStrategy = reqData->song->testStrategy;
- req->data = &transformedReqData;
-
- switch (reqData->step) {
- case 0:
- transformedReqData.stationId = reqData->from->id;
- transformedReqData.rating = PIANO_RATE_BAN;
- break;
-
- case 1:
- transformedReqData.stationId = reqData->to->id;
- transformedReqData.rating = PIANO_RATE_LOVE;
- break;
- }
-
- /* create request data (url, post data) */
- pRet = PianoRequest (ph, req, PIANO_REQUEST_ADD_FEEDBACK);
- /* and reset request type/data */
- req->type = PIANO_REQUEST_MOVE_SONG;
- req->data = reqData;
-
- return pRet;
- break;
- }
- }
-
- if ((req->postData = PianoEncryptString (xmlSendBuf)) == NULL) {
- return PIANO_RET_OUT_OF_MEMORY;
- }
-
- return PIANO_RET_OK;
-}
-
-/* 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 = realTimestamp - 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:
- 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;
- }
- }
-
- return ret;
-}
-
-/* get station from list by id
- * @param search here
- * @param search for this
- * @return the first station structure matching the given id
- */
-PianoStation_t *PianoFindStationById (PianoStation_t *stations,
- const char *searchStation) {
- while (stations != NULL) {
- if (strcmp (stations->id, searchStation) == 0) {
- return stations;
- }
- stations = stations->next;
- }
- return NULL;
-}
-
-/* convert return value to human-readable string
- * @param enum
- * @return error string
- */
-const char *PianoErrorToStr (PianoReturn_t ret) {
- switch (ret) {
- case PIANO_RET_OK:
- return "Everything is fine :)";
- break;
-
- case PIANO_RET_ERR:
- return "Unknown.";
- break;
-
- case PIANO_RET_XML_INVALID:
- return "Invalid XML.";
- break;
-
- case PIANO_RET_AUTH_TOKEN_INVALID:
- return "Invalid auth token.";
- break;
-
- case PIANO_RET_AUTH_USER_PASSWORD_INVALID:
- return "Username and/or password not correct.";
- break;
-
- case PIANO_RET_NOT_AUTHORIZED:
- return "Not authorized.";
- break;
-
- case PIANO_RET_PROTOCOL_INCOMPATIBLE:
- return "Protocol incompatible. Please upgrade " PACKAGE ".";
- break;
-
- case PIANO_RET_READONLY_MODE:
- return "Request cannot be completed at this time, please try "
- "again later.";
- break;
-
- case PIANO_RET_STATION_CODE_INVALID:
- return "Station id is invalid.";
- break;
-
- case PIANO_RET_IP_REJECTED:
- return "Your ip address was rejected. Please setup a control "
- "proxy (see manpage).";
- break;
-
- case PIANO_RET_STATION_NONEXISTENT:
- return "Station does not exist.";
- break;
-
- case PIANO_RET_OUT_OF_MEMORY:
- return "Out of memory.";
- break;
-
- case PIANO_RET_OUT_OF_SYNC:
- return "Out of sync. Please correct your system's time.";
- break;
-
- case PIANO_RET_PLAYLIST_END:
- return "Playlist end.";
- break;
-
- case PIANO_RET_QUICKMIX_NOT_PLAYABLE:
- return "Quickmix not playable.";
- break;
-
- default:
- return "No error message available.";
- break;
- }
-}
-