コード例 #1
0
ファイル: piano.c プロジェクト: cannabliss/pianobar
/*	get next songs for station (usually four tracks)
 *	@param piano handle
 *	@param station id
 *	@param audio format
 *	@param return value: playlist
 */
PianoReturn_t PianoGetPlaylist (PianoHandle_t *ph, const char *stationId,
		PianoAudioFormat_t format, PianoSong_t **retPlaylist) {
	char xmlSendBuf[PIANO_SEND_BUFFER_SIZE], *retStr;
	PianoReturn_t ret;

	/* FIXME: remove static, "magic" 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>0</string></value></param>"
			"<param><value><string></string></value></param>"
			"<param><value><string></string></value></param>"
			"<param><value><string>%s</string></value></param>"
			"<param><value><string>0</string></value></param>"
			"<param><value><string>0</string></value></param>"
			"</params></methodCall>", time (NULL), ph->user.authToken,
			stationId, PianoAudioFormatToString (format));
	snprintf (ph->waith.path, sizeof (ph->waith.path), 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, stationId,
			PianoAudioFormatToString (format));

	if ((ret = PianoHttpPost (&ph->waith, xmlSendBuf, &retStr)) ==
			PIANO_RET_OK) {
		ret = PianoXmlParsePlaylist (ph, retStr, retPlaylist);
		PianoFree (retStr, 0);
	}

	return ret;
}
コード例 #2
0
ファイル: piano.c プロジェクト: carthique/tipjar
/*	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: {
					char *xmlencodedPassword = NULL;

					/* username == email address does not contain &,<,>," */
					if ((xmlencodedPassword =
							PianoXmlEncodeString (logindata->password)) ==
							NULL) {
						return PIANO_RET_OUT_OF_MEMORY;
					}

					snprintf (xmlSendBuf, sizeof (xmlSendBuf), 
							"<?xml version=\"1.0\"?><methodCall>"
							"<methodName>listener.authenticateListener</methodName>"
							"<params><param><value><int>%lu</int></value></param>"
							/* user */
							"<param><value><string>%s</string></value></param>"
							/* password */
							"<param><value><string>%s</string></value></param>"
							/* vendor */
							"<param><value><string>html5tuner</string></value></param>"
							"<param><value><string/></value></param>"
							"<param><value><string/></value></param>"
							"<param><value><string>HTML5</string></value></param>"
							"<param><value><boolean>1</boolean></value></param>"
							"</params></methodCall>", (unsigned long) timestamp,
							logindata->user, xmlencodedPassword);
					snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
							"rid=%s&method=authenticateListener", ph->routeId);

					free (xmlencodedPassword);
					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->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>"
					/* track token */
					"<param><value><string>%s</string></value></param>"
					/* positive */
					"<param><value><boolean>%i</boolean></value></param>"
					"</params></methodCall>", (unsigned long) timestamp,
					ph->user.authToken, reqData->stationId, reqData->trackToken,
					(reqData->rating == PIANO_RATE_LOVE) ? 1 : 0);
			snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
					"rid=%s&lid=%s&method=addFeedback&arg1=%s&arg2=%s"
					"&arg3=%s",
					ph->routeId, ph->user.listenerId, reqData->stationId,
					reqData->trackToken,
					(reqData->rating == PIANO_RATE_LOVE) ? "true" : "false");
			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>"
					/* auth token */
					"<param><value><string>%s</string></value></param>"
					/* music id */
					"<param><value><string>%s%s</string></value></param>"
					/* empty */
					"<param><value><string></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&arg2=", 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>"
					/* 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->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>"
					/* empty */
					"<param><value><string></string></value></param>"
					/* empty */
					"<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=&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>"
					/* 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);
			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;
		}

		case PIANO_REQUEST_GET_STATION_INFO: {
			/* get station information (seeds and feedback) */
			PianoRequestDataGetStationInfo_t *reqData = req->data;

			assert (reqData != NULL);
			assert (reqData->station != NULL);

			snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
					"<methodCall><methodName>station.getStation</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, reqData->station->id);
			snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
					"rid=%s&lid=%s&method=getStation&arg1=%s",
					ph->routeId, ph->user.listenerId, reqData->station->id);
			break;
		}

		case PIANO_REQUEST_DELETE_FEEDBACK: {
			PianoSong_t *song = req->data;

			assert (song != NULL);

			snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
					"<methodCall><methodName>station.deleteFeedback</methodName>"
					"<params><param><value><int>%lu</int></value></param>"
					/* auth token */
					"<param><value><string>%s</string></value></param>"
					/* feedback id */
					"<param><value><string>%s</string></value></param>"
					"</params></methodCall>", (unsigned long) timestamp,
					ph->user.authToken, song->feedbackId);
			snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
					"rid=%s&lid=%s&method=deleteFeedback&arg1=%s",
					ph->routeId, ph->user.listenerId, song->feedbackId);
			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);

			snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
					"<methodCall><methodName>station.deleteSeed</methodName>"
					"<params><param><value><int>%lu</int></value></param>"
					/* auth token */
					"<param><value><string>%s</string></value></param>"
					/* seed id */
					"<param><value><string>%s</string></value></param>"
					"</params></methodCall>", (unsigned long) timestamp,
					ph->user.authToken, seedId);
			snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
					"rid=%s&lid=%s&method=deleteSeed&arg1=%s",
					ph->routeId, ph->user.listenerId, seedId);
			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;
		}
	}

	if ((req->postData = PianoEncryptString (xmlSendBuf)) == NULL) {
		return PIANO_RET_OUT_OF_MEMORY;
	}

	return PIANO_RET_OK;
}