diff options
Diffstat (limited to 'libpiano/src/main.c')
-rw-r--r-- | libpiano/src/main.c | 465 |
1 files changed, 465 insertions, 0 deletions
diff --git a/libpiano/src/main.c b/libpiano/src/main.c new file mode 100644 index 0000000..95adc48 --- /dev/null +++ b/libpiano/src/main.c @@ -0,0 +1,465 @@ +/* +Copyright (c) 2008 Lars-Dominik Braun + +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. +*/ + +#include <curl/curl.h> +#include <string.h> +#include <stdlib.h> +#include <time.h> + +#include "const.h" +#include "main.h" +#include "piano.h" +#include "http.h" +#include "xml.h" +#include "crypt.h" + +/* initialize piano handle, set up curl handle and settings; note: you + * _must_ init curl and libxml2 using curl_global_init (CURL_GLOBAL_SSL) + * and xmlInitParser (), you also _must_ cleanup their garbage on your own! + * @param piano handle + * @return nothing + */ +void PianoInit (PianoHandle_t *ph) { + memset (ph, 0, sizeof (*ph)); + ph->curlHandle = curl_easy_init (); + /* FIXME: 64-bit may make this hack useless */ + snprintf (ph->routeId, sizeof (ph->routeId), "%07liP", time (NULL)>>8); + /* at the moment we don't need publicity */ + curl_easy_setopt (ph->curlHandle, CURLOPT_USERAGENT, PIANO_USERAGENT); +} + +/* 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; + memset (lastArtist, 0, sizeof (*lastArtist)); + free (lastArtist); + } + + curSong = searchResult->songs; + while (curSong != NULL) { + free (curSong->title); + free (curSong->artist); + free (curSong->musicId); + lastSong = curSong; + curSong = curSong->next; + memset (lastSong, 0, sizeof (*lastSong)); + free (lastSong); + } +} + +/* free single station + * @public yes + * @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 (PianoHandle_t *ph) { + PianoStation_t *curStation, *lastStation; + + curStation = ph->stations; + while (curStation != NULL) { + lastStation = curStation; + curStation = curStation->next; + PianoDestroyStation (lastStation); + free (lastStation); + } + ph->stations = NULL; +} + +/* FIXME: copy & waste */ +/* free _all_ elements of playlist + * @param piano handle + * @return nothing + */ +void PianoDestroyPlaylist (PianoHandle_t *ph) { + PianoSong_t *curSong, *lastSong; + + curSong = ph->playlist; + while (curSong != NULL) { + free (curSong->audioUrl); + free (curSong->artist); + free (curSong->focusTraitId); + free (curSong->matchingSeed); + free (curSong->musicId); + free (curSong->title); + free (curSong->userSeed); + lastSong = curSong; + curSong = curSong->next; + memset (lastSong, 0, sizeof (*lastSong)); + free (lastSong); + } + ph->playlist = NULL; +} + +/* frees the whole piano handle structure; this will _not_ cleanup curl's + * internal garbage, you have to call curl_global_cleanup () and + * xmlCleanupParser () for libxml2 + * @param piano handle + * @return nothing + */ +void PianoDestroy (PianoHandle_t *ph) { + curl_easy_cleanup (ph->curlHandle); + /* FIXME: only free if pointer != NULL */ + free (ph->user.webAuthToken); + free (ph->user.authToken); + free (ph->user.listenerId); + + PianoDestroyStations (ph); + PianoDestroyPlaylist (ph); + memset (ph, 0, sizeof (*ph)); +} + +/* authenticates user + * @param piano handle + * @param username (utf-8 encoded) + * @param password (plaintext, utf-8 encoded) + * @return nothing + */ +PianoReturn_t PianoConnect (PianoHandle_t *ph, char *user, char *password) { + char url[PIANO_URL_BUFFER_SIZE]; + char *requestStr = PianoEncryptString ("<?xml version=\"1.0\"?>" + "<methodCall><methodName>misc.sync</methodName>" + "<params></params></methodCall>"); + char *retStr, requestStrPlain[10000]; + PianoReturn_t ret; + + /* sync (is the return value used by pandora? for now: ignore result) */ + snprintf (url, sizeof (url), PIANO_RPC_URL "rid=%s&method=sync", + ph->routeId); + PianoHttpPost (ph->curlHandle, url, requestStr, &retStr); + free (requestStr); + free (retStr); + + /* authenticate */ + snprintf (requestStrPlain, sizeof (requestStrPlain), + "<?xml version=\"1.0\"?><methodCall>" + "<methodName>listener.authenticateListener</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), user, password); + requestStr = PianoEncryptString (requestStrPlain); + snprintf (url, sizeof (url), PIANO_SECURE_RPC_URL "rid=%s" + "&method=authenticateListener", ph->routeId); + PianoHttpPost (ph->curlHandle, url, requestStr, &retStr); + ret = PianoXmlParseUserinfo (ph, retStr); + + free (requestStr); + free (retStr); + + return ret; +} + +/* get all stations for authenticated user (so: PianoConnect needs to + * be run before) + * @param piano handle filled with some authentication data by PianoConnect + * @return nothing + */ +PianoReturn_t PianoGetStations (PianoHandle_t *ph) { + char xmlSendBuf[10000], url[PIANO_URL_BUFFER_SIZE]; + char *requestStr, *retStr; + PianoReturn_t ret; + + snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>" + "<methodCall><methodName>station.getStations</methodName>" + "<params><param><value><int>%li</int></value></param>" + "<param><value><string>%s</string></value></param>" + "</params></methodCall>", time (NULL), ph->user.authToken); + requestStr = PianoEncryptString (xmlSendBuf); + snprintf (url, sizeof (url), PIANO_RPC_URL + "rid=%s&lid=%s&method=getStations", ph->routeId, + ph->user.listenerId); + PianoHttpPost (ph->curlHandle, url, requestStr, &retStr); + ret = PianoXmlParseStations (ph, retStr); + free (retStr); + free (requestStr); + + return ret; +} + +/* get next songs for station (usually four tracks) + * @param piano handle + * @param station id + * @return nothing yet + */ +void PianoGetPlaylist (PianoHandle_t *ph, char *stationId) { + char xmlSendBuf[10000], url[PIANO_URL_BUFFER_SIZE]; + char *requestStr, *retStr; + + /* FIXME: remove static numbers */ + snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>" + "<methodCall><methodName>playlist.getFragment</methodName>" + "<params><param><value><int>%li</int></value></param>" + "<param><value><string>%s</string></value></param>" + "<param><value><string>%s</string></value></param>" + "<param><value><string>15941546</string></value></param>" + "<param><value><string>181840822</string></value></param>" + "<param><value><string></string></value></param>" + "<param><value><string></string></value></param>" + "<param><value><string>aacplus</string></value></param>" + "</params></methodCall>", time (NULL), ph->user.authToken, + stationId); + requestStr = PianoEncryptString (xmlSendBuf); + snprintf (url, sizeof (url), PIANO_RPC_URL + "rid=%s&lid=%s&method=getFragment&arg1=%s&arg2=15941546" + "&arg3=181840822&arg4=&arg5=&arg6=aacplus", ph->routeId, + ph->user.listenerId, stationId); + PianoHttpPost (ph->curlHandle, url, requestStr, &retStr); + PianoXmlParsePlaylist (ph, retStr); + free (retStr); + free (requestStr); +} + +/* love or ban track (you cannot remove your rating, so PIANO_RATE_NONE is + * not allowed) + * @public yes + * @param piano handle + * @param track will be added to this stations loved tracks list + * @param rate this track + * @param your rating + * @return value from return enum + */ +PianoReturn_t PianoRateTrack (PianoHandle_t *ph, PianoStation_t *station, + PianoSong_t *song, PianoSongRating_t rating) { + char xmlSendBuf[10000], url[PIANO_URL_BUFFER_SIZE]; + char *requestStr, *retStr; + PianoReturn_t ret = PIANO_RET_ERR; + + snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>" + "<methodCall><methodName>station.addFeedback</methodName>" + "<params><param><value><int>%li</int></value></param>" + "<param><value><string>%s</string></value></param>" + "<param><value><string>%s</string></value></param>" + "<param><value><string>%s</string></value></param>" + "<param><value><string>%s</string></value></param>" + "<param><value><string>%s</string></value></param>" + "<param><value><string>%s</string></value></param>" + "<param><value><boolean>%i</boolean></value></param>" + "<param><value><boolean>0</boolean></value></param>" + "</params></methodCall>", time (NULL), ph->user.authToken, + station->id, song->musicId, song->matchingSeed, song->userSeed, + /* sometimes focusTraitId is not set, dunno why yet */ + (song->focusTraitId == NULL) ? "" : song->focusTraitId, + (rating == PIANO_RATE_LOVE) ? 1 : 0); + requestStr = PianoEncryptString (xmlSendBuf); + snprintf (url, sizeof (url), PIANO_RPC_URL + "rid=%s&lid=%s&method=addFeedback&arg1=%s&arg2=%s" + "&arg3=%s&arg4=%s&arg5=%s&arg6=%s&arg7=false", ph->routeId, + ph->user.listenerId, station->id, song->musicId, + song->matchingSeed, song->userSeed, + (song->focusTraitId == NULL) ? "" : song->focusTraitId, + (rating == PIANO_RATE_LOVE) ? "true" : "false"); + PianoHttpPost (ph->curlHandle, url, requestStr, &retStr); + ret = PianoXmlParseSimple (retStr); + + if (ret == PIANO_RET_OK) { + song->rating = rating; + } + + free (requestStr); + free (retStr); + + return ret; +} + +/* rename station (on the server and local) + * @public yes + * @param piano handle + * @param change this stations name + * @param new name + * @return + */ +PianoReturn_t PianoRenameStation (PianoHandle_t *ph, PianoStation_t *station, + char *newName) { + char xmlSendBuf[10000], url[PIANO_URL_BUFFER_SIZE]; + char *requestStr, *retStr, *urlencodedNewName, *xmlencodedNewName; + PianoReturn_t ret = PIANO_RET_ERR; + + xmlencodedNewName = PianoXmlEncodeString (newName); + snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>" + "<methodCall><methodName>station.setStationName</methodName>" + "<params><param><value><int>%li</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>", time (NULL), ph->user.authToken, + station->id, xmlencodedNewName); + requestStr = PianoEncryptString (xmlSendBuf); + + urlencodedNewName = curl_easy_escape (ph->curlHandle, newName, 0); + snprintf (url, sizeof (url), PIANO_RPC_URL "rid=%s&lid=%s" + "&method=setStationName&arg1=%s&arg2=%s", ph->routeId, + ph->user.listenerId, station->id, urlencodedNewName); + PianoHttpPost (ph->curlHandle, url, requestStr, &retStr); + ret = PianoXmlParseSimple (retStr); + + if (ret == PIANO_RET_OK) { + free (station->name); + station->name = strdup (newName); + } + + curl_free (urlencodedNewName); + free (xmlencodedNewName); + free (requestStr); + free (retStr); + + return ret; +} + +/* delete station + * @public yes + * @param piano handle + * @param station you want to delete + * @return + */ +PianoReturn_t PianoDeleteStation (PianoHandle_t *ph, PianoStation_t *station) { + char xmlSendBuf[10000], url[PIANO_URL_BUFFER_SIZE]; + char *requestStr, *retStr; + PianoReturn_t ret = PIANO_RET_ERR; + + snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>" + "<methodCall><methodName>station.removeStation</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, + station->id); + requestStr = PianoEncryptString (xmlSendBuf); + + snprintf (url, sizeof (url), PIANO_RPC_URL "rid=%s&lid=%s" + "&method=removeStation&arg1=%s", ph->routeId, ph->user.listenerId, + station->id); + PianoHttpPost (ph->curlHandle, url, requestStr, &retStr); + ret = PianoXmlParseSimple (retStr); + + if (ret == PIANO_RET_OK) { + /* delete station from local station list */ + PianoStation_t *curStation = ph->stations, *lastStation = NULL; + while (curStation != NULL) { + if (curStation == station) { + printf ("deleting station\n"); + if (lastStation != NULL) { + lastStation->next = curStation->next; + } else { + /* first station in list */ + ph->stations = curStation->next; + } + PianoDestroyStation (curStation); + free (curStation); + } + lastStation = curStation; + curStation = curStation->next; + } + } + + free (requestStr); + free (retStr); + + return ret; +} + +/* 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 + * @return nothing yet + */ +void PianoSearchMusic (PianoHandle_t *ph, char *searchStr, + PianoSearchResult_t *searchResult) { + char xmlSendBuf[10000], url[PIANO_URL_BUFFER_SIZE]; + char *requestStr, *retStr, *xmlencodedSearchStr, *urlencodedSearchStr; + + xmlencodedSearchStr = PianoXmlEncodeString (searchStr); + 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); + requestStr = PianoEncryptString (xmlSendBuf); + + urlencodedSearchStr = curl_easy_escape (ph->curlHandle, searchStr, 0); + snprintf (url, sizeof (url), PIANO_RPC_URL "rid=%s&lid=%s&" + "method=search&arg1=%s", ph->routeId, ph->user.listenerId, + urlencodedSearchStr); + + PianoHttpPost (ph->curlHandle, url, requestStr, &retStr); + PianoXmlParseSearch (retStr, searchResult); + + curl_free (urlencodedSearchStr); + free (xmlencodedSearchStr); + free (retStr); + free (requestStr); +} + +/* create new station on server + * @public yes + * @param piano handle + * @param music id from artist or track, you may obtain one by calling + * PianoSearchMusic + * @return nothing, yet + */ +void PianoCreateStation (PianoHandle_t *ph, char *musicId) { + char xmlSendBuf[10000], url[PIANO_URL_BUFFER_SIZE]; + char *requestStr, *retStr; + + snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>" + "<methodCall><methodName>station.createStation</methodName>" + "<params><param><value><int>%li</int></value></param>" + "<param><value><string>%s</string></value></param>" + "<param><value><string>mi%s</string></value></param>" + "</params></methodCall>", time (NULL), ph->user.authToken, + musicId); + requestStr = PianoEncryptString (xmlSendBuf); + + snprintf (url, sizeof (url), PIANO_RPC_URL "rid=%s&lid=%s" + "&method=createStation&arg1=mi%s", ph->routeId, + ph->user.listenerId, musicId); + + PianoHttpPost (ph->curlHandle, url, requestStr, &retStr); + PianoXmlParseCreateStation (ph, retStr); + + free (requestStr); + free (retStr); +} |