diff options
| author | Lars-Dominik Braun <lars@6xq.net> | 2012-05-01 11:26:30 +0200 | 
|---|---|---|
| committer | Lars-Dominik Braun <lars@6xq.net> | 2012-05-01 11:26:30 +0200 | 
| commit | 2996dda98357643334a334a6fcac05cd0c9065ea (patch) | |
| tree | f0c62aab3ad88d7ce33cd8567ec4279d5b76eac0 /src/libpiano/request.c | |
| parent | b47ecbe9d30952861dabd1591a01bd5964049fcd (diff) | |
| download | pianobar-2996dda98357643334a334a6fcac05cd0c9065ea.tar.gz pianobar-2996dda98357643334a334a6fcac05cd0c9065ea.tar.bz2 pianobar-2996dda98357643334a334a6fcac05cd0c9065ea.zip  | |
piano: Split piano.c into request.c and response.c
Diffstat (limited to 'src/libpiano/request.c')
| -rw-r--r-- | src/libpiano/request.c | 509 | 
1 files changed, 509 insertions, 0 deletions
diff --git a/src/libpiano/request.c b/src/libpiano/request.c new file mode 100644 index 0000000..a5a33e9 --- /dev/null +++ b/src/libpiano/request.c @@ -0,0 +1,509 @@ +/* +Copyright (c) 2008-2012 +	Lars-Dominik Braun <lars@6xq.net> + +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. +*/ + +#ifndef __FreeBSD__ +#define _BSD_SOURCE /* required by strdup() */ +#define _DARWIN_C_SOURCE /* strdup() on OS X */ +#endif + +#include <json.h> +#include <assert.h> +#include <stdio.h> +#include <string.h> +/* needed for urlencode */ +#include <waitress.h> + +#include "piano.h" +#include "crypt.h" + +/*	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) { +	const char *jsonSendBuf; +	const char *method = NULL; +	json_object *j = json_object_new_object (); +	/* corrected timestamp */ +	time_t timestamp = time (NULL) - ph->timeOffset; +	bool encrypted = true; + +	assert (ph != NULL); +	assert (req != NULL); + +	req->type = type; +	/* no tls by default */ +	req->secure = false; + +	switch (req->type) { +		case PIANO_REQUEST_LOGIN: { +			/* authenticate user */ +			PianoRequestDataLogin_t *logindata = req->data; + +			assert (logindata != NULL); + +			switch (logindata->step) { +				case 0: +					encrypted = false; +					req->secure = true; + +					json_object_object_add (j, "username", +							json_object_new_string ("android")); +					json_object_object_add (j, "password", +							json_object_new_string ("AC7IBG09A3DTSYM4R41UJWL07VLN8JI7")); +					json_object_object_add (j, "deviceModel", +							json_object_new_string ("android-generic")); +					json_object_object_add (j, "version", +							json_object_new_string ("5")); +					json_object_object_add (j, "includeUrls", +							json_object_new_boolean (true)); +					snprintf (req->urlPath, sizeof (req->urlPath), +							PIANO_RPC_PATH "method=auth.partnerLogin"); +					break; + +				case 1: { +					char *urlencAuthToken; + +					req->secure = true; + +					json_object_object_add (j, "loginType", +							json_object_new_string ("user")); +					json_object_object_add (j, "username", +							json_object_new_string (logindata->user)); +					json_object_object_add (j, "password", +							json_object_new_string (logindata->password)); +					json_object_object_add (j, "partnerAuthToken", +							json_object_new_string (ph->partnerAuthToken)); +					json_object_object_add (j, "syncTime", +							json_object_new_int (timestamp)); + +					urlencAuthToken = WaitressUrlEncode (ph->partnerAuthToken); +					assert (urlencAuthToken != NULL); +					snprintf (req->urlPath, sizeof (req->urlPath), +							PIANO_RPC_PATH "method=auth.userLogin&" +							"auth_token=%s&partner_id=%i", urlencAuthToken, +							ph->partnerId); +					free (urlencAuthToken); + +					break; +				} +			} +			break; +		} + +		case PIANO_REQUEST_GET_STATIONS: { +			/* get stations, user must be authenticated */ +			assert (ph->user.listenerId != NULL); +			method = "user.getStationList"; +			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); + +			req->secure = true; + +			json_object_object_add (j, "stationToken", +					json_object_new_string (reqData->station->id)); + +			method = "station.getPlaylist"; +			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->trackToken != NULL); +			assert (reqData->rating != PIANO_RATE_NONE); + +			json_object_object_add (j, "trackToken", +					json_object_new_string (reqData->trackToken)); +			json_object_object_add (j, "isPositive", +					json_object_new_boolean (reqData->rating == PIANO_RATE_LOVE)); + +			method = "station.addFeedback"; +			break; +		} + +		case PIANO_REQUEST_RENAME_STATION: { +			PianoRequestDataRenameStation_t *reqData = req->data; + +			assert (reqData != NULL); +			assert (reqData->station != NULL); +			assert (reqData->newName != NULL); + +			json_object_object_add (j, "stationToken", +					json_object_new_string (reqData->station->id)); +			json_object_object_add (j, "stationName", +					json_object_new_string (reqData->newName)); + +			method = "station.renameStation"; +			break; +		} + +		case PIANO_REQUEST_DELETE_STATION: { +			/* delete station */ +			PianoStation_t *station = req->data; + +			assert (station != NULL); +			assert (station->id != NULL); + +			json_object_object_add (j, "stationToken", +					json_object_new_string (station->id)); + +			method = "station.deleteStation"; +			break; +		} + +		case PIANO_REQUEST_SEARCH: { +			/* search for artist/song title */ +			PianoRequestDataSearch_t *reqData = req->data; + +			assert (reqData != NULL); +			assert (reqData->searchStr != NULL); + +			json_object_object_add (j, "searchText", +					json_object_new_string (reqData->searchStr)); + +			method = "music.search"; +			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); + +			json_object_object_add (j, "musicToken", +					json_object_new_string (reqData->id)); + +			method = "station.createStation"; +			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); + +			json_object_object_add (j, "musicToken", +					json_object_new_string (reqData->musicId)); +			json_object_object_add (j, "stationToken", +					json_object_new_string (reqData->station->id)); + +			method = "station.addMusic"; +			break; +		} + +		case PIANO_REQUEST_ADD_TIRED_SONG: { +			/* ban song for a month from all stations */ +			PianoSong_t *song = req->data; + +			assert (song != NULL); + +			json_object_object_add (j, "trackToken", +					json_object_new_string (song->trackToken)); + +			method = "user.sleepSong"; +			break; +		} + +		case PIANO_REQUEST_SET_QUICKMIX: { +			/* select stations included in quickmix (see useQuickMix flag of +			 * PianoStation_t) */ +			PianoStation_t *curStation = ph->stations; +			json_object *a = json_object_new_array (); + +			while (curStation != NULL) { +				/* quick mix can't contain itself */ +				if (curStation->useQuickMix && !curStation->isQuickMix) { +					json_object_array_add (a, +							json_object_new_string (curStation->id)); +				} + +				curStation = curStation->next; +			} + +			json_object_object_add (j, "quickMixStationIds", a); + +			method = "user.setQuickMix"; +			break; +		} + +		case PIANO_REQUEST_GET_GENRE_STATIONS: { +			/* receive list of pandora's genre stations */ +			method = "station.getGenreStations"; +			break; +		} + +		case PIANO_REQUEST_TRANSFORM_STATION: { +			/* transform shared station into private */ +			PianoStation_t *station = req->data; + +			assert (station != NULL); + +			json_object_object_add (j, "stationToken", +					json_object_new_string (station->id)); + +			method = "station.transformSharedStation"; +			break; +		} + +		case PIANO_REQUEST_EXPLAIN: { +			/* explain why particular song was played */ +			PianoRequestDataExplain_t *reqData = req->data; + +			assert (reqData != NULL); +			assert (reqData->song != NULL); + +			json_object_object_add (j, "trackToken", +					json_object_new_string (reqData->song->trackToken)); + +			method = "track.explainTrack"; +			break; +		} + +		case PIANO_REQUEST_GET_SEED_SUGGESTIONS: { +#if 0 +			/* 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>" +					/* station id */ +					"<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->station->id, 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); +#endif +			break; +		} + +		case PIANO_REQUEST_BOOKMARK_SONG: { +			/* bookmark song */ +			PianoSong_t *song = req->data; + +			assert (song != NULL); + +			json_object_object_add (j, "trackToken", +					json_object_new_string (song->trackToken)); + +			method = "bookmark.addSongBookmark"; +			break; +		} + +		case PIANO_REQUEST_BOOKMARK_ARTIST: { +			/* bookmark artist */ +			PianoSong_t *song = req->data; + +			assert (song != NULL); + +			json_object_object_add (j, "trackToken", +					json_object_new_string (song->trackToken)); + +			method = "bookmark.addArtistBookmark"; +			break; +		} + +		case PIANO_REQUEST_GET_STATION_INFO: { +			/* get station information (seeds and feedback) */ +			PianoRequestDataGetStationInfo_t *reqData = req->data; + +			assert (reqData != NULL); +			assert (reqData->station != NULL); + +			json_object_object_add (j, "stationToken", +					json_object_new_string (reqData->station->id)); +			json_object_object_add (j, "includeExtendedAttributes", +					json_object_new_boolean (true)); + +			method = "station.getStation"; +			break; +		} + +		case PIANO_REQUEST_DELETE_FEEDBACK: { +			PianoSong_t *song = req->data; + +			assert (song != NULL); + +			json_object_object_add (j, "feedbackId", +					json_object_new_string (song->feedbackId)); + +			method = "station.deleteFeedback"; +			break; +		} + +		case PIANO_REQUEST_DELETE_SEED: { +			PianoRequestDataDeleteSeed_t *reqData = req->data; +			char *seedId = NULL; + +			assert (reqData != NULL); +			assert (reqData->song != NULL || reqData->artist != NULL || +					reqData->station != NULL); + +			if (reqData->song != NULL) { +				seedId = reqData->song->seedId; +			} else if (reqData->artist != NULL) { +				seedId = reqData->artist->seedId; +			} else if (reqData->station != NULL) { +				seedId = reqData->station->seedId; +			} + +			assert (seedId != NULL); + +			json_object_object_add (j, "seedId", +					json_object_new_string (seedId)); + +			method = "station.deleteMusic"; +			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.trackToken = reqData->song->trackToken; +			transformedReqData.rating = reqData->rating; +			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.trackToken = reqData->song->trackToken; +			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; +		} +	} + +	/* standard parameter */ +	if (method != NULL) { +		char *urlencAuthToken; + +		assert (ph->user.authToken != NULL); + +		urlencAuthToken = WaitressUrlEncode (ph->user.authToken); +		assert (urlencAuthToken != NULL); + +		snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH +				"method=%s&auth_token=%s&partner_id=%i&user_id=%s", method, +				urlencAuthToken, ph->partnerId, ph->user.listenerId); + +		free (urlencAuthToken); + +		json_object_object_add (j, "userAuthToken", +				json_object_new_string (ph->user.authToken)); +		json_object_object_add (j, "syncTime", +				json_object_new_int (timestamp)); +	} + +	/* json to string */ +	jsonSendBuf = json_object_to_json_string (j); +	if (encrypted) { +		if ((req->postData = PianoEncryptString (jsonSendBuf)) == NULL) { +			return PIANO_RET_OUT_OF_MEMORY; +		} +	} else { +		req->postData = strdup (jsonSendBuf); +	} +	json_object_put (j); + +	return PIANO_RET_OK; +} +  | 
