From d57bf656fd35eff82034dea5072014bffdc92f82 Mon Sep 17 00:00:00 2001
From: Lars-Dominik Braun <PromyLOPh@lavabit.com>
Date: Tue, 15 Dec 2009 20:44:40 +0100
Subject: Rename libraries' main.c files

---
 libpiano/src/CMakeLists.txt  |   2 +-
 libpiano/src/crypt.c         |   2 +-
 libpiano/src/http.c          |   2 +-
 libpiano/src/main.c          | 908 -------------------------------------------
 libpiano/src/main.h          |  32 --
 libpiano/src/piano.c         | 908 +++++++++++++++++++++++++++++++++++++++++++
 libpiano/src/piano_private.h |  32 ++
 libpiano/src/xml.c           |   2 +-
 8 files changed, 944 insertions(+), 944 deletions(-)
 delete mode 100644 libpiano/src/main.c
 delete mode 100644 libpiano/src/main.h
 create mode 100644 libpiano/src/piano.c
 create mode 100644 libpiano/src/piano_private.h

(limited to 'libpiano')

diff --git a/libpiano/src/CMakeLists.txt b/libpiano/src/CMakeLists.txt
index 1344be2..8d1b77e 100644
--- a/libpiano/src/CMakeLists.txt
+++ b/libpiano/src/CMakeLists.txt
@@ -7,6 +7,6 @@ include_directories (${CMAKE_CURRENT_SOURCE_DIR}/../../libwaitress/src
         ${CMAKE_CURRENT_BINARY_DIR}
 	${CMAKE_CURRENT_SOURCE_DIR}/../../libezxml/src)
 
-add_library (piano STATIC crypt.c http.c main.c xml.c)
+add_library (piano STATIC crypt.c http.c piano.c xml.c)
 target_link_libraries (piano waitress ezxml)
 
diff --git a/libpiano/src/crypt.c b/libpiano/src/crypt.c
index 9b21516..9691aeb 100644
--- a/libpiano/src/crypt.c
+++ b/libpiano/src/crypt.c
@@ -28,7 +28,7 @@ THE SOFTWARE.
 
 #include "crypt_key_output.h"
 #include "crypt_key_input.h"
-#include "main.h"
+#include "piano_private.h"
 
 #define byteswap32(x) ((((x) >> 24) & 0x000000ff) | \
 		(((x) >> 8) & 0x0000ff00) | \
diff --git a/libpiano/src/http.c b/libpiano/src/http.c
index 4af5ccc..10de2a5 100644
--- a/libpiano/src/http.c
+++ b/libpiano/src/http.c
@@ -27,7 +27,7 @@ THE SOFTWARE.
 
 #include <waitress.h>
 
-#include "main.h"
+#include "piano_private.h"
 #include "crypt.h"
 #include "http.h"
 
diff --git a/libpiano/src/main.c b/libpiano/src/main.c
deleted file mode 100644
index 2bf8426..0000000
--- a/libpiano/src/main.c
+++ /dev/null
@@ -1,908 +0,0 @@
-/*
-Copyright (c) 2008-2009
-	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 "main.h"
-#include "piano.h"
-#include "http.h"
-#include "xml.h"
-#include "crypt.h"
-#include "config.h"
-
-#define PIANO_PROTOCOL_VERSION "25"
-#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
-
-/* prototypes */
-static PianoReturn_t PianoAddFeedback (PianoHandle_t *, const char *, const char *,
-		const char *, const char *, const char *, PianoSongRating_t);
-const char *PianoAudioFormatToString (PianoAudioFormat_t);
-
-/*	more "secure" free version
- *	@param free this pointer
- *	@param zero n bytes; 0 disables zeroing (for strings with unknown size,
- *			e.g.)
- */
-void PianoFree (void *ptr, size_t size) {
-	if (ptr != NULL) {
-		if (size > 0) {
-			/* avoid reuse of freed memory */
-			memset ((char *) ptr, 0, size);
-		}
-		free (ptr);
-	}
-}
-
-/*	initialize piano handle
- *	@param piano handle
- *	@return nothing
- */
-void PianoInit (PianoHandle_t *ph) {
-	memset (ph, 0, sizeof (*ph));
-
-	WaitressInit (&ph->waith);
-	strncpy (ph->waith.host, PIANO_RPC_HOST, sizeof (ph->waith.host)-1);
-	strncpy (ph->waith.port, PIANO_RPC_PORT, sizeof (ph->waith.port)-1);
-
-	/* route-id seems to be random. we're using time anyway... */
-	snprintf (ph->routeId, sizeof (ph->routeId), "%07liP", 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) {
-		PianoFree (curArtist->name, 0);
-		PianoFree (curArtist->musicId, 0);
-		lastArtist = curArtist;
-		curArtist = curArtist->next;
-		PianoFree (lastArtist, sizeof (*lastArtist));
-	}
-
-	curSong = searchResult->songs;
-	while (curSong != NULL) {
-		PianoFree (curSong->title, 0);
-		PianoFree (curSong->artist, 0);
-		PianoFree (curSong->musicId, 0);
-		lastSong = curSong;
-		curSong = curSong->next;
-		PianoFree (lastSong, sizeof (*lastSong));
-	}
-}
-
-/*	free single station
- *	@param station
- */
-void PianoDestroyStation (PianoStation_t *station) {
-	PianoFree (station->name, 0);
-	PianoFree (station->id, 0);
-	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);
-		PianoFree (lastStation, sizeof (*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) {
-		PianoFree (curSong->audioUrl, 0);
-		PianoFree (curSong->artist, 0);
-		PianoFree (curSong->focusTraitId, 0);
-		PianoFree (curSong->matchingSeed, 0);
-		PianoFree (curSong->musicId, 0);
-		PianoFree (curSong->title, 0);
-		PianoFree (curSong->userSeed, 0);
-		PianoFree (curSong->identity, 0);
-		PianoFree (curSong->stationId, 0);
-		PianoFree (curSong->album, 0);
-		lastSong = curSong;
-		curSong = curSong->next;
-		PianoFree (lastSong, sizeof (*lastSong));
-	}
-}
-
-/*	frees the whole piano handle structure
- *	@param piano handle
- *	@return nothing
- */
-void PianoDestroy (PianoHandle_t *ph) {
-	WaitressFree (&ph->waith);
-
-	PianoFree (ph->user.webAuthToken, 0);
-	PianoFree (ph->user.authToken, 0);
-	PianoFree (ph->user.listenerId, 0);
-
-	PianoDestroyStations (ph->stations);
-	/* destroy genre stations */
-	PianoGenreCategory_t *curGenreCat = ph->genreStations, *lastGenreCat;
-	while (curGenreCat != NULL) {
-		PianoDestroyStations (curGenreCat->stations);
-		PianoFree (curGenreCat->name, 0);
-		lastGenreCat = curGenreCat;
-		curGenreCat = curGenreCat->next;
-		PianoFree (lastGenreCat, sizeof (*lastGenreCat));
-	}
-	memset (ph, 0, sizeof (*ph));
-}
-
-/*	authenticates user
- *	@param piano handle
- *	@param username (utf-8 encoded)
- *	@param password (plaintext, utf-8 encoded)
- *	@param use ssl when logging in (1 = on, 0 = off), note that the password
- *			is not hashed and will be sent as plain-text!
- */
-PianoReturn_t PianoConnect (PianoHandle_t *ph, const char *user,
-		const char *password) {
-	char *retStr, xmlSendBuf[PIANO_SEND_BUFFER_SIZE];
-	PianoReturn_t ret;
-
-	/* sync and throw away result (it's an encrypted timestamp, decrypt with
-	 * PianoDecryptString) */
-	snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
-			"<methodCall><methodName>misc.sync</methodName>"
-			"<params></params></methodCall>");
-	snprintf (ph->waith.path, sizeof (ph->waith.path), PIANO_RPC_PATH
-			"rid=%s&method=sync", ph->routeId);
-	ret = PianoHttpPost (&ph->waith, xmlSendBuf, &retStr);
-	PianoFree (retStr, 0);
-
-	if (ret != PIANO_RET_OK) {
-		return ret;
-	}
-
-	/* authenticate */
-	snprintf (xmlSendBuf, sizeof (xmlSendBuf), 
-			"<?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);
-	snprintf (ph->waith.path, sizeof (ph->waith.path), PIANO_RPC_PATH
-			"rid=%s&method=authenticateListener", ph->routeId);
-
-	if ((ret = PianoHttpPost (&ph->waith, xmlSendBuf, &retStr)) ==
-			PIANO_RET_OK) {
-		ret = PianoXmlParseUserinfo (ph, retStr);
-		PianoFree (retStr, 0);
-	}
-
-	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
- */
-PianoReturn_t PianoGetStations (PianoHandle_t *ph) {
-	char xmlSendBuf[PIANO_SEND_BUFFER_SIZE], *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);
-	snprintf (ph->waith.path, sizeof (ph->waith.path), PIANO_RPC_PATH
-			"rid=%s&lid=%s&method=getStations", ph->routeId,
-			ph->user.listenerId);
-
-	if ((ret = PianoHttpPost (&ph->waith, xmlSendBuf, &retStr)) ==
-			PIANO_RET_OK) {
-		ret = PianoXmlParseStations (ph, retStr);
-		PianoFree (retStr, 0);
-	}
-
-	return ret;
-}
-
-/*	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;
-}
-
-/*	love or ban track (you cannot remove your rating, so PIANO_RATE_NONE is
- *	not allowed)
- *	@public yes
- *	@param piano handle
- *	@param rate this track
- *	@param your rating
- */
-PianoReturn_t PianoRateTrack (PianoHandle_t *ph, PianoSong_t *song,
-		PianoSongRating_t rating) {
-	PianoReturn_t ret;
-
-	ret = PianoAddFeedback (ph, song->stationId, song->musicId,
-			song->matchingSeed, song->userSeed, song->focusTraitId, rating);
-
-	if (ret == PIANO_RET_OK) {
-		song->rating = rating;
-	}
-
-	return ret;
-}
-
-/*	move song to another station
- *	@param piano handle
- *	@param move from
- *	@param move here
- *	@param song to move
- */
-PianoReturn_t PianoMoveSong (PianoHandle_t *ph,
-		const PianoStation_t *stationFrom, const PianoStation_t *stationTo,
-		const PianoSong_t *song) {
-	PianoReturn_t ret;
-
-	/* ban from current station */
-	if ((ret = PianoAddFeedback (ph, stationFrom->id, song->musicId, "", "",
-			"", PIANO_RATE_BAN)) == PIANO_RET_OK) {
-		/* love at new station */
-		return PianoAddFeedback (ph, stationTo->id, song->musicId, "",
-				"", "", PIANO_RATE_LOVE);
-	}
-	return ret;
-}
-
-/*	add feedback
- *	@param piano handle
- *	@param station id
- *	@param song id
- *	@param song matching seed or NULL
- *	@param song user seed or NULL
- *	@param song focus trait id or NULL
- *	@param rating
- */
-static PianoReturn_t PianoAddFeedback (PianoHandle_t *ph, const char *stationId,
-		const char *songMusicId, const char *songMatchingSeed,
-		const char *songUserSeed, const char *songFocusTraitId,
-		PianoSongRating_t rating) {
-	char xmlSendBuf[PIANO_SEND_BUFFER_SIZE], *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></value></param>"
-			"<param><value><boolean>%i</boolean></value></param>"
-			"<param><value><boolean>0</boolean></value></param>"
-			"</params></methodCall>", time (NULL), ph->user.authToken,
-			stationId, songMusicId,
-			(songMatchingSeed == NULL) ? "" : songMatchingSeed,
-			(songUserSeed == NULL) ? "" : songUserSeed,
-			(songFocusTraitId == NULL) ? "" : songFocusTraitId,
-			(rating == PIANO_RATE_LOVE) ? 1 : 0);
-	snprintf (ph->waith.path, sizeof (ph->waith.path), PIANO_RPC_PATH
-			"rid=%s&lid=%s&method=addFeedback&arg1=%s&arg2=%s"
-			"&arg3=%s&arg4=%s&arg5=%s&arg6=&arg7=%s&arg8=false", ph->routeId,
-			ph->user.listenerId, stationId, songMusicId,
-			(songMatchingSeed == NULL) ? "" : songMatchingSeed,
-			(songUserSeed == NULL) ? "" : songUserSeed,
-			(songFocusTraitId == NULL) ? "" : songFocusTraitId,
-			(rating == PIANO_RATE_LOVE) ? "true" : "false");
-
-	if ((ret = PianoHttpPost (&ph->waith, xmlSendBuf, &retStr)) ==
-			PIANO_RET_OK) {
-		ret = PianoXmlParseSimple (retStr);
-		PianoFree (retStr, 0);
-	}
-
-	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,
-		const char *newName) {
-	char xmlSendBuf[PIANO_SEND_BUFFER_SIZE], *retStr;
-	char *urlencodedNewName, *xmlencodedNewName;
-	PianoReturn_t ret = PIANO_RET_ERR;
-
-	if ((xmlencodedNewName = PianoXmlEncodeString (newName)) == NULL) {
-		return PIANO_RET_OUT_OF_MEMORY;
-	}
-
-	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);
-
-	urlencodedNewName = WaitressUrlEncode (newName);
-	snprintf (ph->waith.path, sizeof (ph->waith.path), PIANO_RPC_PATH
-			"rid=%s&lid=%s&method=setStationName&arg1=%s&arg2=%s",
-			ph->routeId, ph->user.listenerId, station->id, urlencodedNewName);
-
-	if ((ret = PianoHttpPost (&ph->waith, xmlSendBuf, &retStr)) ==
-			PIANO_RET_OK) {
-		if ((ret = PianoXmlParseSimple (retStr)) == PIANO_RET_OK) {
-			PianoFree (station->name, 0);
-			station->name = strdup (newName);
-		}
-		PianoFree (retStr, 0);
-	}
-
-	PianoFree (urlencodedNewName, 0);
-	PianoFree (xmlencodedNewName, 0);
-
-	return ret;
-}
-
-/*	delete station
- *	@public yes
- *	@param piano handle
- *	@param station you want to delete
- */
-PianoReturn_t PianoDeleteStation (PianoHandle_t *ph, PianoStation_t *station) {
-	char xmlSendBuf[PIANO_SEND_BUFFER_SIZE], *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);
-
-	snprintf (ph->waith.path, sizeof (ph->waith.path), PIANO_RPC_PATH
-			"rid=%s&lid=%s&method=removeStation&arg1=%s", ph->routeId,
-			ph->user.listenerId, station->id);
-	if ((ret = PianoHttpPost (&ph->waith, xmlSendBuf, &retStr)) ==
-			PIANO_RET_OK) {
-		if ((ret = PianoXmlParseSimple (retStr)) == PIANO_RET_OK) {
-			/* 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);
-					PianoFree (curStation, sizeof (*curStation));
-					break;
-				}
-				lastStation = curStation;
-				curStation = curStation->next;
-			}
-		}
-	}
-
-	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
- */
-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;
-}
-
-/*	create new station on server
- *	@public yes
- *	@param piano handle
- *	@param type: "mi" for music id (from music search) or "sh" for
- *			shared station
- *	@param id
- */
-PianoReturn_t PianoCreateStation (PianoHandle_t *ph, const char *type,
-		const char *id) {
-	char xmlSendBuf[PIANO_SEND_BUFFER_SIZE], *retStr;
-	PianoReturn_t ret;
-
-	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>%s%s</string></value></param>"
-			"</params></methodCall>", time (NULL), ph->user.authToken,
-			type, id);
-
-	snprintf (ph->waith.path, sizeof (ph->waith.path), PIANO_RPC_PATH
-			"rid=%s&lid=%s&method=createStation&arg1=%s%s", ph->routeId,
-			ph->user.listenerId, type, id);
-	
-	if ((ret = PianoHttpPost (&ph->waith, xmlSendBuf, &retStr)) ==
-			PIANO_RET_OK) {
-		ret = PianoXmlParseCreateStation (ph, retStr);
-		PianoFree (retStr, 0);
-	}
-
-	return ret;
-}
-
-/* FIXME: update station data instead of replacing them */
-/*	add more music to existing station; multithreaded apps beware! this alters
- *	station data, don't forget to lock the station pointer you passed to this
- *	function
- *	@public yes
- *	@param piano handle
- *	@param add music to this station
- *	@param music id; can be obtained with PianoSearchMusic ()
- */
-PianoReturn_t PianoStationAddMusic (PianoHandle_t *ph,
-		PianoStation_t *station, const char *musicId) {
-	char xmlSendBuf[PIANO_SEND_BUFFER_SIZE], *retStr;
-	PianoReturn_t ret;
-
-	snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
-			"<methodCall><methodName>station.addSeed</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, musicId);
-
-	snprintf (ph->waith.path, sizeof (ph->waith.path), PIANO_RPC_PATH
-			"rid=%s&lid=%s&method=addSeed&arg1=%s&arg2=%s", ph->routeId,
-			ph->user.listenerId, station->id, musicId);
-	
-	if ((ret = PianoHttpPost (&ph->waith, xmlSendBuf, &retStr)) ==
-			PIANO_RET_OK) {
-		ret = PianoXmlParseAddSeed (ph, retStr, station);
-		PianoFree (retStr, 0);
-	}
-
-	return ret;
-}
-
-/*	ban a song temporary (for one month)
- *	@param piano handle
- *	@param song to be banned
- *	@return _OK or error
- */
-PianoReturn_t PianoSongTired (PianoHandle_t *ph, const PianoSong_t *song) {
-	char xmlSendBuf[PIANO_SEND_BUFFER_SIZE], *retStr;
-	PianoReturn_t ret;
-
-	snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
-			"<methodCall><methodName>listener.addTiredSong</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,
-			song->identity);
-
-	snprintf (ph->waith.path, sizeof (ph->waith.path), PIANO_RPC_PATH
-			"rid=%s&lid=%s&method=addTiredSong&arg1=%s", ph->routeId,
-			ph->user.listenerId, song->identity);
-
-	if ((ret = PianoHttpPost (&ph->waith, xmlSendBuf, &retStr)) ==
-			PIANO_RET_OK) {
-		ret = PianoXmlParseSimple (retStr);
-		PianoFree (retStr, 0);
-	}
-
-	return ret;
-}
-
-/*	set stations use by quickmix
- *	@param piano handle
- *	@return _OK or error
- */
-PianoReturn_t PianoSetQuickmix (PianoHandle_t *ph) {
-	char xmlSendBuf[PIANO_SEND_BUFFER_SIZE], valueBuf[1000], urlArgBuf[1000],
-			*retStr;
-	PianoReturn_t ret;
-	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>%li</int></value></param>"
-			"<param><value><string>%s</string></value></param>"
-			"<param><value><string>RANDOM</string></value></param>"
-			"<param><value><array><data>", time (NULL), 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></params></methodCall>",
-			sizeof (xmlSendBuf) - strlen (xmlSendBuf) - 1);
-
-	snprintf (ph->waith.path, sizeof (ph->waith.path), PIANO_RPC_PATH
-			"rid=%s&lid=%s&method=setQuickMix&arg1=RANDOM&arg2=%s",
-			ph->routeId, ph->user.listenerId, urlArgBuf);
-	
-	if ((ret = PianoHttpPost (&ph->waith, xmlSendBuf, &retStr)) ==
-			PIANO_RET_OK) {
-		ret = PianoXmlParseSimple (retStr);
-		PianoFree (retStr, 0);
-	}
-
-	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;
-}
-
-/*	receive genre stations
- *	@param piano handle
- *	@return _OK or error
- */
-PianoReturn_t PianoGetGenreStations (PianoHandle_t *ph) {
-	char *retStr;
-	PianoReturn_t ret;
-
-	snprintf (ph->waith.path, sizeof (ph->waith.path), "/xml/genre?r=%li",
-			time (NULL));
-	
-	if ((ret = PianoHttpGet (&ph->waith, &retStr)) ==
-			PIANO_RET_OK) {
-		ret = PianoXmlParseGenreExplorer (ph, retStr);
-		PianoFree (retStr, 0);
-	}
-
-	return ret;
-}
-
-/*	make shared stations private, needed to rate songs played on shared
- *	stations
- *	@param piano handle
- *	@param station to transform
- *	@return _OK or error
- */
-PianoReturn_t PianoTransformShared (PianoHandle_t *ph,
-		PianoStation_t *station) {
-	char xmlSendBuf[PIANO_SEND_BUFFER_SIZE], *retStr;
-	PianoReturn_t ret;
-
-	snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
-			"<methodCall><methodName>station.transformShared</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);
-
-	snprintf (ph->waith.path, sizeof (ph->waith.path), PIANO_RPC_PATH
-			"rid=%s&lid=%s&method=transformShared&arg1=%s", ph->routeId,
-			ph->user.listenerId, station->id);
-	
-	if ((ret = PianoHttpPost (&ph->waith, xmlSendBuf, &retStr)) ==
-			PIANO_RET_OK) {
-		ret = PianoXmlParseTranformStation (retStr);
-		/* though this call returns a bunch of "new" data only this one is
-		 * changed and important (at the moment) */
-		if (ret == PIANO_RET_OK) {
-			station->isCreator = 1;
-		}
-		PianoFree (retStr, 0);
-	}
-
-	return ret;
-}
-
-/*	"why dit you play this song?"
- *	@param piano handle
- *	@param song (from playlist)
- *	@param return allocated string; you have to free it yourself
- *	@return _OK or error
- */
-PianoReturn_t PianoExplain (PianoHandle_t *ph, const PianoSong_t *song,
-		char **retExplain) {
-	char xmlSendBuf[PIANO_SEND_BUFFER_SIZE], *retStr;
-	PianoReturn_t ret;
-
-	snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
-			"<methodCall><methodName>playlist.narrative</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,
-			song->stationId, song->musicId);
-	
-	snprintf (ph->waith.path, sizeof (ph->waith.path), PIANO_RPC_PATH
-			"rid=%s&lid=%s&method=method=narrative&arg1=%s&arg2=%s",
-			ph->routeId, ph->user.listenerId, song->stationId, song->musicId);
-	
-	if ((ret = PianoHttpPost (&ph->waith, xmlSendBuf, &retStr)) ==
-			PIANO_RET_OK) {
-		ret = PianoXmlParseNarrative (retStr, retExplain);
-		PianoFree (retStr, 0);
-	}
-
-	return ret;
-}
-
-/*	Get seed suggestions by music id
- *	@param piano handle
- *	@param music id
- *	@param max results
- *	@param result buffer
- */
-PianoReturn_t PianoSeedSuggestions (PianoHandle_t *ph, const char *musicId,
-		unsigned int max, PianoSearchResult_t *searchResult) {
-	char xmlSendBuf[PIANO_SEND_BUFFER_SIZE], *retStr;
-	PianoReturn_t ret;
-
-	snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
-			"<methodCall><methodName>music.getSeedSuggestions</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><int>%u</int></value></param>"
-			"</params></methodCall>", time (NULL), ph->user.authToken,
-			musicId, max);
-	
-	snprintf (ph->waith.path, sizeof (ph->waith.path), PIANO_RPC_PATH
-			"rid=%s&lid=%s&method=method=getSeedSuggestions&arg1=%s&arg2=%u",
-			ph->routeId, ph->user.listenerId, musicId, max);
-	
-	if ((ret = PianoHttpPost (&ph->waith, xmlSendBuf, &retStr)) ==
-			PIANO_RET_OK) {
-		ret = PianoXmlParseSeedSuggestions (retStr, searchResult);
-		PianoFree (retStr, 0);
-	}
-
-	return ret;
-}
-
-/*	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_NET_ERROR:
-			return "Connection failed.";
-			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;
-
-		default:
-			return "No error message available.";
-			break;
-	}
-}
-
-/*	convert audio format id to string that can be used in xml requests
- *	@param format id
- *	@return constant string
- */
-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;
-	}
-}
-
diff --git a/libpiano/src/main.h b/libpiano/src/main.h
deleted file mode 100644
index 6c8401e..0000000
--- a/libpiano/src/main.h
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
-Copyright (c) 2008-2009
-	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.
-*/
-
-#ifndef _MAIN_H
-#define _MAIN_H
-
-#include "piano.h"
-
-void PianoFree (void *ptr, size_t size);
-void PianoDestroyStation (PianoStation_t *station);
-
-#endif /* _MAIN_H */
diff --git a/libpiano/src/piano.c b/libpiano/src/piano.c
new file mode 100644
index 0000000..43c675a
--- /dev/null
+++ b/libpiano/src/piano.c
@@ -0,0 +1,908 @@
+/*
+Copyright (c) 2008-2009
+	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 "piano_private.h"
+#include "piano.h"
+#include "http.h"
+#include "xml.h"
+#include "crypt.h"
+#include "config.h"
+
+#define PIANO_PROTOCOL_VERSION "25"
+#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
+
+/* prototypes */
+static PianoReturn_t PianoAddFeedback (PianoHandle_t *, const char *, const char *,
+		const char *, const char *, const char *, PianoSongRating_t);
+const char *PianoAudioFormatToString (PianoAudioFormat_t);
+
+/*	more "secure" free version
+ *	@param free this pointer
+ *	@param zero n bytes; 0 disables zeroing (for strings with unknown size,
+ *			e.g.)
+ */
+void PianoFree (void *ptr, size_t size) {
+	if (ptr != NULL) {
+		if (size > 0) {
+			/* avoid reuse of freed memory */
+			memset ((char *) ptr, 0, size);
+		}
+		free (ptr);
+	}
+}
+
+/*	initialize piano handle
+ *	@param piano handle
+ *	@return nothing
+ */
+void PianoInit (PianoHandle_t *ph) {
+	memset (ph, 0, sizeof (*ph));
+
+	WaitressInit (&ph->waith);
+	strncpy (ph->waith.host, PIANO_RPC_HOST, sizeof (ph->waith.host)-1);
+	strncpy (ph->waith.port, PIANO_RPC_PORT, sizeof (ph->waith.port)-1);
+
+	/* route-id seems to be random. we're using time anyway... */
+	snprintf (ph->routeId, sizeof (ph->routeId), "%07liP", 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) {
+		PianoFree (curArtist->name, 0);
+		PianoFree (curArtist->musicId, 0);
+		lastArtist = curArtist;
+		curArtist = curArtist->next;
+		PianoFree (lastArtist, sizeof (*lastArtist));
+	}
+
+	curSong = searchResult->songs;
+	while (curSong != NULL) {
+		PianoFree (curSong->title, 0);
+		PianoFree (curSong->artist, 0);
+		PianoFree (curSong->musicId, 0);
+		lastSong = curSong;
+		curSong = curSong->next;
+		PianoFree (lastSong, sizeof (*lastSong));
+	}
+}
+
+/*	free single station
+ *	@param station
+ */
+void PianoDestroyStation (PianoStation_t *station) {
+	PianoFree (station->name, 0);
+	PianoFree (station->id, 0);
+	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);
+		PianoFree (lastStation, sizeof (*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) {
+		PianoFree (curSong->audioUrl, 0);
+		PianoFree (curSong->artist, 0);
+		PianoFree (curSong->focusTraitId, 0);
+		PianoFree (curSong->matchingSeed, 0);
+		PianoFree (curSong->musicId, 0);
+		PianoFree (curSong->title, 0);
+		PianoFree (curSong->userSeed, 0);
+		PianoFree (curSong->identity, 0);
+		PianoFree (curSong->stationId, 0);
+		PianoFree (curSong->album, 0);
+		lastSong = curSong;
+		curSong = curSong->next;
+		PianoFree (lastSong, sizeof (*lastSong));
+	}
+}
+
+/*	frees the whole piano handle structure
+ *	@param piano handle
+ *	@return nothing
+ */
+void PianoDestroy (PianoHandle_t *ph) {
+	WaitressFree (&ph->waith);
+
+	PianoFree (ph->user.webAuthToken, 0);
+	PianoFree (ph->user.authToken, 0);
+	PianoFree (ph->user.listenerId, 0);
+
+	PianoDestroyStations (ph->stations);
+	/* destroy genre stations */
+	PianoGenreCategory_t *curGenreCat = ph->genreStations, *lastGenreCat;
+	while (curGenreCat != NULL) {
+		PianoDestroyStations (curGenreCat->stations);
+		PianoFree (curGenreCat->name, 0);
+		lastGenreCat = curGenreCat;
+		curGenreCat = curGenreCat->next;
+		PianoFree (lastGenreCat, sizeof (*lastGenreCat));
+	}
+	memset (ph, 0, sizeof (*ph));
+}
+
+/*	authenticates user
+ *	@param piano handle
+ *	@param username (utf-8 encoded)
+ *	@param password (plaintext, utf-8 encoded)
+ *	@param use ssl when logging in (1 = on, 0 = off), note that the password
+ *			is not hashed and will be sent as plain-text!
+ */
+PianoReturn_t PianoConnect (PianoHandle_t *ph, const char *user,
+		const char *password) {
+	char *retStr, xmlSendBuf[PIANO_SEND_BUFFER_SIZE];
+	PianoReturn_t ret;
+
+	/* sync and throw away result (it's an encrypted timestamp, decrypt with
+	 * PianoDecryptString) */
+	snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
+			"<methodCall><methodName>misc.sync</methodName>"
+			"<params></params></methodCall>");
+	snprintf (ph->waith.path, sizeof (ph->waith.path), PIANO_RPC_PATH
+			"rid=%s&method=sync", ph->routeId);
+	ret = PianoHttpPost (&ph->waith, xmlSendBuf, &retStr);
+	PianoFree (retStr, 0);
+
+	if (ret != PIANO_RET_OK) {
+		return ret;
+	}
+
+	/* authenticate */
+	snprintf (xmlSendBuf, sizeof (xmlSendBuf), 
+			"<?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);
+	snprintf (ph->waith.path, sizeof (ph->waith.path), PIANO_RPC_PATH
+			"rid=%s&method=authenticateListener", ph->routeId);
+
+	if ((ret = PianoHttpPost (&ph->waith, xmlSendBuf, &retStr)) ==
+			PIANO_RET_OK) {
+		ret = PianoXmlParseUserinfo (ph, retStr);
+		PianoFree (retStr, 0);
+	}
+
+	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
+ */
+PianoReturn_t PianoGetStations (PianoHandle_t *ph) {
+	char xmlSendBuf[PIANO_SEND_BUFFER_SIZE], *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);
+	snprintf (ph->waith.path, sizeof (ph->waith.path), PIANO_RPC_PATH
+			"rid=%s&lid=%s&method=getStations", ph->routeId,
+			ph->user.listenerId);
+
+	if ((ret = PianoHttpPost (&ph->waith, xmlSendBuf, &retStr)) ==
+			PIANO_RET_OK) {
+		ret = PianoXmlParseStations (ph, retStr);
+		PianoFree (retStr, 0);
+	}
+
+	return ret;
+}
+
+/*	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;
+}
+
+/*	love or ban track (you cannot remove your rating, so PIANO_RATE_NONE is
+ *	not allowed)
+ *	@public yes
+ *	@param piano handle
+ *	@param rate this track
+ *	@param your rating
+ */
+PianoReturn_t PianoRateTrack (PianoHandle_t *ph, PianoSong_t *song,
+		PianoSongRating_t rating) {
+	PianoReturn_t ret;
+
+	ret = PianoAddFeedback (ph, song->stationId, song->musicId,
+			song->matchingSeed, song->userSeed, song->focusTraitId, rating);
+
+	if (ret == PIANO_RET_OK) {
+		song->rating = rating;
+	}
+
+	return ret;
+}
+
+/*	move song to another station
+ *	@param piano handle
+ *	@param move from
+ *	@param move here
+ *	@param song to move
+ */
+PianoReturn_t PianoMoveSong (PianoHandle_t *ph,
+		const PianoStation_t *stationFrom, const PianoStation_t *stationTo,
+		const PianoSong_t *song) {
+	PianoReturn_t ret;
+
+	/* ban from current station */
+	if ((ret = PianoAddFeedback (ph, stationFrom->id, song->musicId, "", "",
+			"", PIANO_RATE_BAN)) == PIANO_RET_OK) {
+		/* love at new station */
+		return PianoAddFeedback (ph, stationTo->id, song->musicId, "",
+				"", "", PIANO_RATE_LOVE);
+	}
+	return ret;
+}
+
+/*	add feedback
+ *	@param piano handle
+ *	@param station id
+ *	@param song id
+ *	@param song matching seed or NULL
+ *	@param song user seed or NULL
+ *	@param song focus trait id or NULL
+ *	@param rating
+ */
+static PianoReturn_t PianoAddFeedback (PianoHandle_t *ph, const char *stationId,
+		const char *songMusicId, const char *songMatchingSeed,
+		const char *songUserSeed, const char *songFocusTraitId,
+		PianoSongRating_t rating) {
+	char xmlSendBuf[PIANO_SEND_BUFFER_SIZE], *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></value></param>"
+			"<param><value><boolean>%i</boolean></value></param>"
+			"<param><value><boolean>0</boolean></value></param>"
+			"</params></methodCall>", time (NULL), ph->user.authToken,
+			stationId, songMusicId,
+			(songMatchingSeed == NULL) ? "" : songMatchingSeed,
+			(songUserSeed == NULL) ? "" : songUserSeed,
+			(songFocusTraitId == NULL) ? "" : songFocusTraitId,
+			(rating == PIANO_RATE_LOVE) ? 1 : 0);
+	snprintf (ph->waith.path, sizeof (ph->waith.path), PIANO_RPC_PATH
+			"rid=%s&lid=%s&method=addFeedback&arg1=%s&arg2=%s"
+			"&arg3=%s&arg4=%s&arg5=%s&arg6=&arg7=%s&arg8=false", ph->routeId,
+			ph->user.listenerId, stationId, songMusicId,
+			(songMatchingSeed == NULL) ? "" : songMatchingSeed,
+			(songUserSeed == NULL) ? "" : songUserSeed,
+			(songFocusTraitId == NULL) ? "" : songFocusTraitId,
+			(rating == PIANO_RATE_LOVE) ? "true" : "false");
+
+	if ((ret = PianoHttpPost (&ph->waith, xmlSendBuf, &retStr)) ==
+			PIANO_RET_OK) {
+		ret = PianoXmlParseSimple (retStr);
+		PianoFree (retStr, 0);
+	}
+
+	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,
+		const char *newName) {
+	char xmlSendBuf[PIANO_SEND_BUFFER_SIZE], *retStr;
+	char *urlencodedNewName, *xmlencodedNewName;
+	PianoReturn_t ret = PIANO_RET_ERR;
+
+	if ((xmlencodedNewName = PianoXmlEncodeString (newName)) == NULL) {
+		return PIANO_RET_OUT_OF_MEMORY;
+	}
+
+	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);
+
+	urlencodedNewName = WaitressUrlEncode (newName);
+	snprintf (ph->waith.path, sizeof (ph->waith.path), PIANO_RPC_PATH
+			"rid=%s&lid=%s&method=setStationName&arg1=%s&arg2=%s",
+			ph->routeId, ph->user.listenerId, station->id, urlencodedNewName);
+
+	if ((ret = PianoHttpPost (&ph->waith, xmlSendBuf, &retStr)) ==
+			PIANO_RET_OK) {
+		if ((ret = PianoXmlParseSimple (retStr)) == PIANO_RET_OK) {
+			PianoFree (station->name, 0);
+			station->name = strdup (newName);
+		}
+		PianoFree (retStr, 0);
+	}
+
+	PianoFree (urlencodedNewName, 0);
+	PianoFree (xmlencodedNewName, 0);
+
+	return ret;
+}
+
+/*	delete station
+ *	@public yes
+ *	@param piano handle
+ *	@param station you want to delete
+ */
+PianoReturn_t PianoDeleteStation (PianoHandle_t *ph, PianoStation_t *station) {
+	char xmlSendBuf[PIANO_SEND_BUFFER_SIZE], *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);
+
+	snprintf (ph->waith.path, sizeof (ph->waith.path), PIANO_RPC_PATH
+			"rid=%s&lid=%s&method=removeStation&arg1=%s", ph->routeId,
+			ph->user.listenerId, station->id);
+	if ((ret = PianoHttpPost (&ph->waith, xmlSendBuf, &retStr)) ==
+			PIANO_RET_OK) {
+		if ((ret = PianoXmlParseSimple (retStr)) == PIANO_RET_OK) {
+			/* 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);
+					PianoFree (curStation, sizeof (*curStation));
+					break;
+				}
+				lastStation = curStation;
+				curStation = curStation->next;
+			}
+		}
+	}
+
+	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
+ */
+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;
+}
+
+/*	create new station on server
+ *	@public yes
+ *	@param piano handle
+ *	@param type: "mi" for music id (from music search) or "sh" for
+ *			shared station
+ *	@param id
+ */
+PianoReturn_t PianoCreateStation (PianoHandle_t *ph, const char *type,
+		const char *id) {
+	char xmlSendBuf[PIANO_SEND_BUFFER_SIZE], *retStr;
+	PianoReturn_t ret;
+
+	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>%s%s</string></value></param>"
+			"</params></methodCall>", time (NULL), ph->user.authToken,
+			type, id);
+
+	snprintf (ph->waith.path, sizeof (ph->waith.path), PIANO_RPC_PATH
+			"rid=%s&lid=%s&method=createStation&arg1=%s%s", ph->routeId,
+			ph->user.listenerId, type, id);
+	
+	if ((ret = PianoHttpPost (&ph->waith, xmlSendBuf, &retStr)) ==
+			PIANO_RET_OK) {
+		ret = PianoXmlParseCreateStation (ph, retStr);
+		PianoFree (retStr, 0);
+	}
+
+	return ret;
+}
+
+/* FIXME: update station data instead of replacing them */
+/*	add more music to existing station; multithreaded apps beware! this alters
+ *	station data, don't forget to lock the station pointer you passed to this
+ *	function
+ *	@public yes
+ *	@param piano handle
+ *	@param add music to this station
+ *	@param music id; can be obtained with PianoSearchMusic ()
+ */
+PianoReturn_t PianoStationAddMusic (PianoHandle_t *ph,
+		PianoStation_t *station, const char *musicId) {
+	char xmlSendBuf[PIANO_SEND_BUFFER_SIZE], *retStr;
+	PianoReturn_t ret;
+
+	snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
+			"<methodCall><methodName>station.addSeed</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, musicId);
+
+	snprintf (ph->waith.path, sizeof (ph->waith.path), PIANO_RPC_PATH
+			"rid=%s&lid=%s&method=addSeed&arg1=%s&arg2=%s", ph->routeId,
+			ph->user.listenerId, station->id, musicId);
+	
+	if ((ret = PianoHttpPost (&ph->waith, xmlSendBuf, &retStr)) ==
+			PIANO_RET_OK) {
+		ret = PianoXmlParseAddSeed (ph, retStr, station);
+		PianoFree (retStr, 0);
+	}
+
+	return ret;
+}
+
+/*	ban a song temporary (for one month)
+ *	@param piano handle
+ *	@param song to be banned
+ *	@return _OK or error
+ */
+PianoReturn_t PianoSongTired (PianoHandle_t *ph, const PianoSong_t *song) {
+	char xmlSendBuf[PIANO_SEND_BUFFER_SIZE], *retStr;
+	PianoReturn_t ret;
+
+	snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
+			"<methodCall><methodName>listener.addTiredSong</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,
+			song->identity);
+
+	snprintf (ph->waith.path, sizeof (ph->waith.path), PIANO_RPC_PATH
+			"rid=%s&lid=%s&method=addTiredSong&arg1=%s", ph->routeId,
+			ph->user.listenerId, song->identity);
+
+	if ((ret = PianoHttpPost (&ph->waith, xmlSendBuf, &retStr)) ==
+			PIANO_RET_OK) {
+		ret = PianoXmlParseSimple (retStr);
+		PianoFree (retStr, 0);
+	}
+
+	return ret;
+}
+
+/*	set stations use by quickmix
+ *	@param piano handle
+ *	@return _OK or error
+ */
+PianoReturn_t PianoSetQuickmix (PianoHandle_t *ph) {
+	char xmlSendBuf[PIANO_SEND_BUFFER_SIZE], valueBuf[1000], urlArgBuf[1000],
+			*retStr;
+	PianoReturn_t ret;
+	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>%li</int></value></param>"
+			"<param><value><string>%s</string></value></param>"
+			"<param><value><string>RANDOM</string></value></param>"
+			"<param><value><array><data>", time (NULL), 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></params></methodCall>",
+			sizeof (xmlSendBuf) - strlen (xmlSendBuf) - 1);
+
+	snprintf (ph->waith.path, sizeof (ph->waith.path), PIANO_RPC_PATH
+			"rid=%s&lid=%s&method=setQuickMix&arg1=RANDOM&arg2=%s",
+			ph->routeId, ph->user.listenerId, urlArgBuf);
+	
+	if ((ret = PianoHttpPost (&ph->waith, xmlSendBuf, &retStr)) ==
+			PIANO_RET_OK) {
+		ret = PianoXmlParseSimple (retStr);
+		PianoFree (retStr, 0);
+	}
+
+	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;
+}
+
+/*	receive genre stations
+ *	@param piano handle
+ *	@return _OK or error
+ */
+PianoReturn_t PianoGetGenreStations (PianoHandle_t *ph) {
+	char *retStr;
+	PianoReturn_t ret;
+
+	snprintf (ph->waith.path, sizeof (ph->waith.path), "/xml/genre?r=%li",
+			time (NULL));
+	
+	if ((ret = PianoHttpGet (&ph->waith, &retStr)) ==
+			PIANO_RET_OK) {
+		ret = PianoXmlParseGenreExplorer (ph, retStr);
+		PianoFree (retStr, 0);
+	}
+
+	return ret;
+}
+
+/*	make shared stations private, needed to rate songs played on shared
+ *	stations
+ *	@param piano handle
+ *	@param station to transform
+ *	@return _OK or error
+ */
+PianoReturn_t PianoTransformShared (PianoHandle_t *ph,
+		PianoStation_t *station) {
+	char xmlSendBuf[PIANO_SEND_BUFFER_SIZE], *retStr;
+	PianoReturn_t ret;
+
+	snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
+			"<methodCall><methodName>station.transformShared</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);
+
+	snprintf (ph->waith.path, sizeof (ph->waith.path), PIANO_RPC_PATH
+			"rid=%s&lid=%s&method=transformShared&arg1=%s", ph->routeId,
+			ph->user.listenerId, station->id);
+	
+	if ((ret = PianoHttpPost (&ph->waith, xmlSendBuf, &retStr)) ==
+			PIANO_RET_OK) {
+		ret = PianoXmlParseTranformStation (retStr);
+		/* though this call returns a bunch of "new" data only this one is
+		 * changed and important (at the moment) */
+		if (ret == PIANO_RET_OK) {
+			station->isCreator = 1;
+		}
+		PianoFree (retStr, 0);
+	}
+
+	return ret;
+}
+
+/*	"why dit you play this song?"
+ *	@param piano handle
+ *	@param song (from playlist)
+ *	@param return allocated string; you have to free it yourself
+ *	@return _OK or error
+ */
+PianoReturn_t PianoExplain (PianoHandle_t *ph, const PianoSong_t *song,
+		char **retExplain) {
+	char xmlSendBuf[PIANO_SEND_BUFFER_SIZE], *retStr;
+	PianoReturn_t ret;
+
+	snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
+			"<methodCall><methodName>playlist.narrative</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,
+			song->stationId, song->musicId);
+	
+	snprintf (ph->waith.path, sizeof (ph->waith.path), PIANO_RPC_PATH
+			"rid=%s&lid=%s&method=method=narrative&arg1=%s&arg2=%s",
+			ph->routeId, ph->user.listenerId, song->stationId, song->musicId);
+	
+	if ((ret = PianoHttpPost (&ph->waith, xmlSendBuf, &retStr)) ==
+			PIANO_RET_OK) {
+		ret = PianoXmlParseNarrative (retStr, retExplain);
+		PianoFree (retStr, 0);
+	}
+
+	return ret;
+}
+
+/*	Get seed suggestions by music id
+ *	@param piano handle
+ *	@param music id
+ *	@param max results
+ *	@param result buffer
+ */
+PianoReturn_t PianoSeedSuggestions (PianoHandle_t *ph, const char *musicId,
+		unsigned int max, PianoSearchResult_t *searchResult) {
+	char xmlSendBuf[PIANO_SEND_BUFFER_SIZE], *retStr;
+	PianoReturn_t ret;
+
+	snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
+			"<methodCall><methodName>music.getSeedSuggestions</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><int>%u</int></value></param>"
+			"</params></methodCall>", time (NULL), ph->user.authToken,
+			musicId, max);
+	
+	snprintf (ph->waith.path, sizeof (ph->waith.path), PIANO_RPC_PATH
+			"rid=%s&lid=%s&method=method=getSeedSuggestions&arg1=%s&arg2=%u",
+			ph->routeId, ph->user.listenerId, musicId, max);
+	
+	if ((ret = PianoHttpPost (&ph->waith, xmlSendBuf, &retStr)) ==
+			PIANO_RET_OK) {
+		ret = PianoXmlParseSeedSuggestions (retStr, searchResult);
+		PianoFree (retStr, 0);
+	}
+
+	return ret;
+}
+
+/*	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_NET_ERROR:
+			return "Connection failed.";
+			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;
+
+		default:
+			return "No error message available.";
+			break;
+	}
+}
+
+/*	convert audio format id to string that can be used in xml requests
+ *	@param format id
+ *	@return constant string
+ */
+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;
+	}
+}
+
diff --git a/libpiano/src/piano_private.h b/libpiano/src/piano_private.h
new file mode 100644
index 0000000..6c8401e
--- /dev/null
+++ b/libpiano/src/piano_private.h
@@ -0,0 +1,32 @@
+/*
+Copyright (c) 2008-2009
+	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.
+*/
+
+#ifndef _MAIN_H
+#define _MAIN_H
+
+#include "piano.h"
+
+void PianoFree (void *ptr, size_t size);
+void PianoDestroyStation (PianoStation_t *station);
+
+#endif /* _MAIN_H */
diff --git a/libpiano/src/xml.c b/libpiano/src/xml.c
index ad0cf7d..35a8606 100644
--- a/libpiano/src/xml.c
+++ b/libpiano/src/xml.c
@@ -31,7 +31,7 @@ THE SOFTWARE.
 #include "piano.h"
 #include "crypt.h"
 #include "config.h"
-#include "main.h"
+#include "piano_private.h"
 
 static void PianoXmlStructParser (const ezxml_t,
 		void (*callback) (const char *, const ezxml_t, void *), void *);
-- 
cgit v1.2.3