Blame src/shout.c

Packit eed494
/* -*- c-basic-offset: 8; -*- */
Packit eed494
/* shout.c: Implementation of public libshout interface shout.h
Packit eed494
 *
Packit eed494
 *  Copyright (C) 2002-2004 the Icecast team <team@icecast.org>
Packit eed494
 *
Packit eed494
 *  This library is free software; you can redistribute it and/or
Packit eed494
 *  modify it under the terms of the GNU Library General Public
Packit eed494
 *  License as published by the Free Software Foundation; either
Packit eed494
 *  version 2 of the License, or (at your option) any later version.
Packit eed494
 *
Packit eed494
 *  This library is distributed in the hope that it will be useful,
Packit eed494
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit eed494
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit eed494
 *  Library General Public License for more details.
Packit eed494
 *
Packit eed494
 *  You should have received a copy of the GNU Library General Public
Packit eed494
 *  License along with this library; if not, write to the Free
Packit eed494
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
Packit eed494
 *
Packit eed494
 * $Id: shout.c 11554 2006-06-09 23:09:33Z brendan $
Packit eed494
 */
Packit eed494
Packit eed494
#ifdef HAVE_CONFIG_H
Packit eed494
 #include <config.h>
Packit eed494
#endif
Packit eed494
Packit eed494
#include <stdio.h>
Packit eed494
#include <stdlib.h>
Packit eed494
#include <string.h>
Packit eed494
#include <errno.h>
Packit eed494
Packit eed494
#include <shout/shout.h>
Packit eed494
#include <net/sock.h>
Packit eed494
#include "timing/timing.h"
Packit eed494
#include "httpp/httpp.h"
Packit eed494
Packit eed494
#include "shout_private.h"
Packit eed494
#include "util.h"
Packit eed494
Packit eed494
/* -- local prototypes -- */
Packit eed494
static int queue_data(shout_queue_t *queue, const unsigned char *data, size_t len);
Packit eed494
static int queue_str(shout_t *self, const char *str);
Packit eed494
static int queue_printf(shout_t *self, const char *fmt, ...);
Packit eed494
static inline void queue_free(shout_queue_t *queue);
Packit eed494
static int send_queue(shout_t *self);
Packit eed494
static int get_response(shout_t *self);
Packit eed494
static int try_connect (shout_t *self);
Packit eed494
static int try_write (shout_t *self, const void *data, size_t len);
Packit eed494
Packit eed494
static int create_request(shout_t *self);
Packit eed494
static int create_http_request(shout_t *self);
Packit eed494
static int create_xaudiocast_request(shout_t *self);
Packit eed494
static int create_icy_request(shout_t *self);
Packit eed494
static int parse_response(shout_t *self);
Packit eed494
static int parse_http_response(shout_t *self);
Packit eed494
static int parse_xaudiocast_response(shout_t *self);
Packit eed494
Packit eed494
static char *http_basic_authorization(shout_t *self);
Packit eed494
Packit eed494
/* -- static data -- */
Packit eed494
static int _initialized = 0;
Packit eed494
Packit eed494
/* -- public functions -- */
Packit eed494
Packit eed494
void shout_init(void)
Packit eed494
{
Packit eed494
	if (_initialized)
Packit eed494
		return;
Packit eed494
Packit eed494
	sock_initialize();
Packit eed494
	_initialized = 1;
Packit eed494
}
Packit eed494
Packit eed494
void shout_shutdown(void)
Packit eed494
{
Packit eed494
	if (!_initialized)
Packit eed494
		return;
Packit eed494
Packit eed494
	sock_shutdown();
Packit eed494
	_initialized = 0;
Packit eed494
}
Packit eed494
Packit eed494
shout_t *shout_new(void)
Packit eed494
{
Packit eed494
	shout_t *self;
Packit eed494
Packit eed494
	/* in case users haven't done this explicitly. Should we error
Packit eed494
	 * if not initialized instead? */
Packit eed494
	shout_init();
Packit eed494
	
Packit eed494
	if (!(self = (shout_t *)calloc(1, sizeof(shout_t)))) {
Packit eed494
		return NULL;
Packit eed494
	}
Packit eed494
Packit eed494
	if (shout_set_host(self, LIBSHOUT_DEFAULT_HOST) != SHOUTERR_SUCCESS) {
Packit eed494
		shout_free(self);
Packit eed494
Packit eed494
		return NULL;
Packit eed494
	}
Packit eed494
	if (shout_set_user(self, LIBSHOUT_DEFAULT_USER) != SHOUTERR_SUCCESS) {
Packit eed494
		shout_free(self);
Packit eed494
Packit eed494
		return NULL;
Packit eed494
	}
Packit eed494
	if (shout_set_agent(self, LIBSHOUT_DEFAULT_USERAGENT) != SHOUTERR_SUCCESS) {
Packit eed494
		shout_free(self);
Packit eed494
Packit eed494
		return NULL;
Packit eed494
	}
Packit eed494
	if (!(self->audio_info = _shout_util_dict_new())) {
Packit eed494
		shout_free(self);
Packit eed494
Packit eed494
		return NULL;
Packit eed494
	}
Packit eed494
Packit eed494
	self->port = LIBSHOUT_DEFAULT_PORT;
Packit eed494
	self->format = LIBSHOUT_DEFAULT_FORMAT;
Packit eed494
	self->protocol = LIBSHOUT_DEFAULT_PROTOCOL;
Packit eed494
Packit eed494
	return self;
Packit eed494
}
Packit eed494
Packit eed494
void shout_free(shout_t *self)
Packit eed494
{
Packit eed494
	if (!self) return;
Packit eed494
Packit eed494
	if (self->host) free(self->host);
Packit eed494
	if (self->password) free(self->password);
Packit eed494
	if (self->mount) free(self->mount);
Packit eed494
	if (self->name) free(self->name);
Packit eed494
	if (self->url) free(self->url);
Packit eed494
	if (self->genre) free(self->genre);
Packit eed494
	if (self->description) free(self->description);
Packit eed494
	if (self->user) free(self->user);
Packit eed494
	if (self->useragent) free(self->useragent);
Packit eed494
	if (self->audio_info) _shout_util_dict_free (self->audio_info);
Packit eed494
Packit eed494
	free(self);
Packit eed494
}
Packit eed494
Packit eed494
int shout_open(shout_t *self)
Packit eed494
{
Packit eed494
	/* sanity check */
Packit eed494
	if (!self)
Packit eed494
		return SHOUTERR_INSANE;
Packit eed494
	if (self->state != SHOUT_STATE_UNCONNECTED)
Packit eed494
		return SHOUTERR_CONNECTED;
Packit eed494
	if (!self->host || !self->password || !self->port)
Packit eed494
		return self->error = SHOUTERR_INSANE;
Packit eed494
	if (self->format == SHOUT_FORMAT_OGG && self->protocol != SHOUT_PROTOCOL_HTTP)
Packit eed494
		return self->error = SHOUTERR_UNSUPPORTED;
Packit eed494
Packit eed494
	return self->error = try_connect(self);
Packit eed494
}
Packit eed494
Packit eed494
Packit eed494
int shout_close(shout_t *self)
Packit eed494
{
Packit eed494
	if (!self)
Packit eed494
		return SHOUTERR_INSANE;
Packit eed494
Packit eed494
	if (self->state == SHOUT_STATE_UNCONNECTED)
Packit eed494
		return self->error = SHOUTERR_UNCONNECTED;
Packit eed494
Packit eed494
	if (self->state == SHOUT_STATE_CONNECTED && self->close)
Packit eed494
		self->close(self);
Packit eed494
Packit eed494
	sock_close(self->socket);
Packit eed494
	self->state = SHOUT_STATE_UNCONNECTED;
Packit eed494
	self->starttime = 0;
Packit eed494
	self->senttime = 0;
Packit eed494
	queue_free(&self->rqueue);
Packit eed494
	queue_free(&self->wqueue);
Packit eed494
Packit eed494
	return self->error = SHOUTERR_SUCCESS;
Packit eed494
}
Packit eed494
Packit eed494
int shout_send(shout_t *self, const unsigned char *data, size_t len)
Packit eed494
{
Packit eed494
	if (!self)
Packit eed494
		return SHOUTERR_INSANE;
Packit eed494
Packit eed494
	if (self->state != SHOUT_STATE_CONNECTED)
Packit eed494
		return self->error = SHOUTERR_UNCONNECTED;
Packit eed494
Packit eed494
	if (self->starttime <= 0)
Packit eed494
		self->starttime = timing_get_time();
Packit eed494
Packit eed494
	if (!len)
Packit eed494
		return send_queue(self);
Packit eed494
Packit eed494
	return self->send(self, data, len);
Packit eed494
}
Packit eed494
Packit eed494
ssize_t shout_send_raw(shout_t *self, const unsigned char *data, size_t len)
Packit eed494
{
Packit eed494
	ssize_t ret;
Packit eed494
Packit eed494
	if (!self) 
Packit eed494
		return SHOUTERR_INSANE;
Packit eed494
Packit eed494
	if (self->state != SHOUT_STATE_CONNECTED)
Packit eed494
		return SHOUTERR_UNCONNECTED;
Packit eed494
Packit eed494
	self->error = SHOUTERR_SUCCESS;
Packit eed494
Packit eed494
	/* send immediately if possible (should be the common case) */
Packit eed494
	if (len && ! self->wqueue.len) {
Packit eed494
		if ((ret = try_write(self, data, len)) < 0)
Packit eed494
			return self->error;
Packit eed494
		if (ret < len) {
Packit eed494
			self->error = queue_data(&self->wqueue, data + ret, len - ret);
Packit eed494
			if (self->error != SHOUTERR_SUCCESS)
Packit eed494
				return self->error;
Packit eed494
		}
Packit eed494
Packit eed494
		return len;
Packit eed494
	}
Packit eed494
Packit eed494
	self->error = queue_data(&self->wqueue, data, len);
Packit eed494
	if (self->error != SHOUTERR_SUCCESS)
Packit eed494
		return self->error;
Packit eed494
Packit eed494
	ret = send_queue(self);
Packit eed494
	if (ret == SHOUTERR_SUCCESS || (len && ret == SHOUTERR_BUSY))
Packit eed494
		return len;
Packit eed494
Packit eed494
	return ret;
Packit eed494
}
Packit eed494
Packit eed494
ssize_t shout_queuelen(shout_t *self)
Packit eed494
{
Packit eed494
	if (!self)
Packit eed494
		return SHOUTERR_INSANE;
Packit eed494
Packit eed494
	return (ssize_t)self->wqueue.len;
Packit eed494
}
Packit eed494
Packit eed494
Packit eed494
void shout_sync(shout_t *self)
Packit eed494
{
Packit eed494
	int64_t sleep;
Packit eed494
Packit eed494
	if (!self)
Packit eed494
		return;
Packit eed494
Packit eed494
	if (self->senttime == 0)
Packit eed494
		return;
Packit eed494
Packit eed494
	sleep = self->senttime / 1000 - (timing_get_time() - self->starttime);
Packit eed494
	if (sleep > 0)
Packit eed494
		timing_sleep((uint64_t)sleep);
Packit eed494
		
Packit eed494
}
Packit eed494
Packit eed494
int shout_delay(shout_t *self)
Packit eed494
{
Packit eed494
Packit eed494
	if (!self)
Packit eed494
		return 0;
Packit eed494
Packit eed494
	if (self->senttime == 0)
Packit eed494
		return 0;
Packit eed494
Packit eed494
	/* Is this cast to double needed? */
Packit eed494
	return self->senttime / 1000 - (timing_get_time() - self->starttime);
Packit eed494
}
Packit eed494
  
Packit eed494
shout_metadata_t *shout_metadata_new(void)
Packit eed494
{
Packit eed494
	return _shout_util_dict_new();
Packit eed494
}
Packit eed494
Packit eed494
void shout_metadata_free(shout_metadata_t *self)
Packit eed494
{
Packit eed494
	if (!self)
Packit eed494
		return;
Packit eed494
Packit eed494
	_shout_util_dict_free(self);
Packit eed494
}
Packit eed494
Packit eed494
int shout_metadata_add(shout_metadata_t *self, const char *name, const char *value)
Packit eed494
{
Packit eed494
	if (!self || !name)
Packit eed494
		return SHOUTERR_INSANE;
Packit eed494
Packit eed494
	return _shout_util_dict_set(self, name, value);
Packit eed494
}
Packit eed494
Packit eed494
/* open second socket to server, send HTTP request to change metadata.
Packit eed494
 * TODO: prettier error-handling. */
Packit eed494
int shout_set_metadata(shout_t *self, shout_metadata_t *metadata)
Packit eed494
{
Packit eed494
	sock_t socket;
Packit eed494
	int rv;
Packit eed494
	char *encvalue;
Packit eed494
Packit eed494
	if (!self || !metadata)
Packit eed494
		return SHOUTERR_INSANE;
Packit eed494
Packit eed494
	if (!(encvalue = _shout_util_dict_urlencode(metadata, '&')))
Packit eed494
		return SHOUTERR_MALLOC;
Packit eed494
Packit eed494
	if ((socket = sock_connect(self->host, self->port)) <= 0)
Packit eed494
		return SHOUTERR_NOCONNECT;
Packit eed494
Packit eed494
	if (self->protocol == SHOUT_PROTOCOL_ICY)
Packit eed494
		rv = sock_write(socket, "GET /admin.cgi?mode=updinfo&pass=%s&%s HTTP/1.0\r\nUser-Agent: %s (Mozilla compatible)\r\n\r\n",
Packit eed494
		  self->password, encvalue, shout_get_agent(self));
Packit eed494
	else if (self->protocol == SHOUT_PROTOCOL_HTTP) {
Packit eed494
		char *auth = http_basic_authorization(self);
Packit eed494
Packit eed494
		rv = sock_write(socket, "GET /admin/metadata?mode=updinfo&mount=%s&%s HTTP/1.0\r\nUser-Agent: %s\r\n%s\r\n",
Packit eed494
		  self->mount, encvalue, shout_get_agent(self), auth ? auth : "");
Packit eed494
                free(auth);
Packit eed494
	} else
Packit eed494
		rv = sock_write(socket, "GET /admin.cgi?mode=updinfo&pass=%s&mount=%s&%s HTTP/1.0\r\nUser-Agent: %s\r\n\r\n",
Packit eed494
		  self->password, self->mount, encvalue, shout_get_agent(self));
Packit eed494
	free(encvalue);
Packit eed494
	if (!rv) {
Packit eed494
		sock_close(socket);
Packit eed494
		return SHOUTERR_SOCKET;
Packit eed494
	}
Packit eed494
Packit eed494
	sock_close(socket);
Packit eed494
Packit eed494
	return SHOUTERR_SUCCESS;
Packit eed494
}
Packit eed494
Packit eed494
/* getters/setters */
Packit eed494
const char *shout_version(int *major, int *minor, int *patch)
Packit eed494
{
Packit eed494
	if (major)
Packit eed494
		*major = LIBSHOUT_MAJOR;
Packit eed494
	if (minor)
Packit eed494
		*minor = LIBSHOUT_MINOR;
Packit eed494
	if (patch)
Packit eed494
		*patch = LIBSHOUT_MICRO;
Packit eed494
Packit eed494
	return VERSION;
Packit eed494
}
Packit eed494
Packit eed494
int shout_get_errno(shout_t *self)
Packit eed494
{
Packit eed494
	return self->error;
Packit eed494
}
Packit eed494
Packit eed494
const char *shout_get_error(shout_t *self)
Packit eed494
{
Packit eed494
	if (!self)
Packit eed494
		return "Invalid shout_t";
Packit eed494
Packit eed494
	switch (self->error) {
Packit eed494
	case SHOUTERR_SUCCESS:
Packit eed494
		return "No error";
Packit eed494
	case SHOUTERR_INSANE:
Packit eed494
		return "Nonsensical arguments";
Packit eed494
	case SHOUTERR_NOCONNECT:
Packit eed494
		return "Couldn't connect";
Packit eed494
	case SHOUTERR_NOLOGIN:
Packit eed494
		return "Login failed";
Packit eed494
	case SHOUTERR_SOCKET:
Packit eed494
		return "Socket error";
Packit eed494
	case SHOUTERR_MALLOC:
Packit eed494
		return "Out of memory";
Packit eed494
	case SHOUTERR_CONNECTED:
Packit eed494
		return "Cannot set parameter while connected";
Packit eed494
	case SHOUTERR_UNCONNECTED:
Packit eed494
		return "Not connected";
Packit eed494
        case SHOUTERR_BUSY:
Packit eed494
                return "Socket is busy";
Packit eed494
	case SHOUTERR_UNSUPPORTED:
Packit eed494
		return "This libshout doesn't support the requested option";
Packit eed494
	default:
Packit eed494
		return "Unknown error";
Packit eed494
	}
Packit eed494
}
Packit eed494
Packit eed494
/* Returns:
Packit eed494
 *   SHOUTERR_CONNECTED if the connection is open,
Packit eed494
 *   SHOUTERR_UNCONNECTED if it has not yet been opened,
Packit eed494
 *   or an error from try_connect, including SHOUTERR_BUSY
Packit eed494
 */
Packit eed494
int shout_get_connected(shout_t *self)
Packit eed494
{
Packit eed494
	int rc;
Packit eed494
Packit eed494
	if (!self)
Packit eed494
		return SHOUTERR_INSANE;
Packit eed494
Packit eed494
	if (self->state == SHOUT_STATE_CONNECTED)
Packit eed494
		return SHOUTERR_CONNECTED;
Packit eed494
	if (self->state != SHOUT_STATE_UNCONNECTED) {
Packit eed494
		if ((rc = try_connect(self)) == SHOUTERR_SUCCESS)
Packit eed494
			return SHOUTERR_CONNECTED;
Packit eed494
		return rc;
Packit eed494
	}
Packit eed494
Packit eed494
	return SHOUTERR_UNCONNECTED;
Packit eed494
}
Packit eed494
Packit eed494
int shout_set_host(shout_t *self, const char *host)
Packit eed494
{
Packit eed494
	if (!self)
Packit eed494
		return SHOUTERR_INSANE;
Packit eed494
Packit eed494
	if (self->state != SHOUT_STATE_UNCONNECTED)
Packit eed494
		return self->error = SHOUTERR_CONNECTED;
Packit eed494
Packit eed494
	if (self->host)
Packit eed494
		free(self->host);
Packit eed494
Packit eed494
	if (!(self->host = _shout_util_strdup(host)))
Packit eed494
		return self->error = SHOUTERR_MALLOC;
Packit eed494
Packit eed494
	return self->error = SHOUTERR_SUCCESS;
Packit eed494
}
Packit eed494
Packit eed494
const char *shout_get_host(shout_t *self)
Packit eed494
{
Packit eed494
	if (!self)
Packit eed494
		return NULL;
Packit eed494
Packit eed494
	return self->host;
Packit eed494
}
Packit eed494
Packit eed494
int shout_set_port(shout_t *self, unsigned short port)
Packit eed494
{
Packit eed494
	if (!self)
Packit eed494
		return SHOUTERR_INSANE;
Packit eed494
Packit eed494
	if (self->state != SHOUT_STATE_UNCONNECTED)
Packit eed494
		return self->error = SHOUTERR_CONNECTED;
Packit eed494
Packit eed494
	self->port = port;
Packit eed494
Packit eed494
	return self->error = SHOUTERR_SUCCESS;
Packit eed494
}
Packit eed494
Packit eed494
unsigned short shout_get_port(shout_t *self)
Packit eed494
{
Packit eed494
	if (!self)
Packit eed494
		return 0;
Packit eed494
Packit eed494
	return self->port;
Packit eed494
}
Packit eed494
Packit eed494
int shout_set_password(shout_t *self, const char *password)
Packit eed494
{
Packit eed494
	if (!self)
Packit eed494
		return SHOUTERR_INSANE;
Packit eed494
Packit eed494
	if (self->state != SHOUT_STATE_UNCONNECTED)
Packit eed494
		return self->error = SHOUTERR_CONNECTED;
Packit eed494
Packit eed494
	if (self->password)
Packit eed494
		free (self->password);
Packit eed494
Packit eed494
	if (!(self->password = _shout_util_strdup(password)))
Packit eed494
		return self->error = SHOUTERR_MALLOC;
Packit eed494
Packit eed494
	return self->error = SHOUTERR_SUCCESS;
Packit eed494
}
Packit eed494
Packit eed494
const char* shout_get_password(shout_t *self)
Packit eed494
{
Packit eed494
	if (!self)
Packit eed494
		return NULL;
Packit eed494
Packit eed494
	return self->password;
Packit eed494
}
Packit eed494
Packit eed494
int shout_set_mount(shout_t *self, const char *mount)
Packit eed494
{
Packit eed494
	size_t len;
Packit eed494
Packit eed494
	if (!self || !mount)
Packit eed494
		return SHOUTERR_INSANE;
Packit eed494
Packit eed494
	if (self->state != SHOUT_STATE_UNCONNECTED)
Packit eed494
		return self->error = SHOUTERR_CONNECTED;
Packit eed494
	
Packit eed494
	if (self->mount)
Packit eed494
		free(self->mount);
Packit eed494
Packit eed494
	len = strlen (mount) + 1;
Packit eed494
	if (mount[0] != '/')
Packit eed494
		len++;
Packit eed494
Packit eed494
	if (!(self->mount = malloc(len)))
Packit eed494
		return self->error = SHOUTERR_MALLOC;
Packit eed494
Packit eed494
	sprintf (self->mount, "%s%s", mount[0] == '/' ? "" : "/", mount);
Packit eed494
Packit eed494
	return self->error = SHOUTERR_SUCCESS;
Packit eed494
}
Packit eed494
Packit eed494
const char *shout_get_mount(shout_t *self)
Packit eed494
{
Packit eed494
	if (!self)
Packit eed494
		return NULL;
Packit eed494
Packit eed494
	return self->mount;
Packit eed494
}
Packit eed494
Packit eed494
int shout_set_name(shout_t *self, const char *name)
Packit eed494
{
Packit eed494
	if (!self)
Packit eed494
		return SHOUTERR_INSANE;
Packit eed494
Packit eed494
	if (self->state != SHOUT_STATE_UNCONNECTED)
Packit eed494
		return self->error = SHOUTERR_CONNECTED;
Packit eed494
Packit eed494
	if (self->name)
Packit eed494
		free(self->name);
Packit eed494
Packit eed494
	if (!(self->name = _shout_util_strdup(name)))
Packit eed494
		return self->error = SHOUTERR_MALLOC;
Packit eed494
Packit eed494
	return self->error = SHOUTERR_SUCCESS;
Packit eed494
}
Packit eed494
Packit eed494
const char *shout_get_name(shout_t *self)
Packit eed494
{
Packit eed494
	if (!self)
Packit eed494
		return NULL;
Packit eed494
Packit eed494
	return self->name;
Packit eed494
}
Packit eed494
Packit eed494
int shout_set_url(shout_t *self, const char *url)
Packit eed494
{
Packit eed494
	if (!self)
Packit eed494
		return SHOUTERR_INSANE;
Packit eed494
Packit eed494
	if (self->state != SHOUT_STATE_UNCONNECTED)
Packit eed494
		return self->error = SHOUTERR_CONNECTED;
Packit eed494
Packit eed494
	if (self->url)
Packit eed494
		free(self->url);
Packit eed494
Packit eed494
	if (!(self->url = _shout_util_strdup(url)))
Packit eed494
		return self->error = SHOUTERR_MALLOC;
Packit eed494
Packit eed494
	return self->error = SHOUTERR_SUCCESS;
Packit eed494
}
Packit eed494
Packit eed494
const char *shout_get_url(shout_t *self)
Packit eed494
{
Packit eed494
	if (!self)
Packit eed494
		return NULL;
Packit eed494
Packit eed494
	return self->url;
Packit eed494
}
Packit eed494
Packit eed494
int shout_set_genre(shout_t *self, const char *genre)
Packit eed494
{
Packit eed494
	if (!self)
Packit eed494
		return SHOUTERR_INSANE;
Packit eed494
Packit eed494
	if (self->state != SHOUT_STATE_UNCONNECTED)
Packit eed494
		return self->error = SHOUTERR_CONNECTED;
Packit eed494
Packit eed494
	if (self->genre)
Packit eed494
		free(self->genre);
Packit eed494
Packit eed494
	if (! (self->genre = _shout_util_strdup (genre)))
Packit eed494
		return self->error = SHOUTERR_MALLOC;
Packit eed494
Packit eed494
	return self->error = SHOUTERR_SUCCESS;
Packit eed494
}
Packit eed494
Packit eed494
const char *shout_get_genre(shout_t *self)
Packit eed494
{
Packit eed494
	if (!self)
Packit eed494
		return NULL;
Packit eed494
Packit eed494
	return self->genre;
Packit eed494
}
Packit eed494
Packit eed494
int shout_set_agent(shout_t *self, const char *agent)
Packit eed494
{
Packit eed494
	if (!self)
Packit eed494
		return SHOUTERR_INSANE;
Packit eed494
Packit eed494
	if (self->state != SHOUT_STATE_UNCONNECTED)
Packit eed494
		return self->error = SHOUTERR_CONNECTED;
Packit eed494
Packit eed494
	if (self->useragent)
Packit eed494
		free(self->useragent);
Packit eed494
Packit eed494
	if (! (self->useragent = _shout_util_strdup (agent)))
Packit eed494
		return self->error = SHOUTERR_MALLOC;
Packit eed494
Packit eed494
	return self->error = SHOUTERR_SUCCESS;
Packit eed494
}
Packit eed494
Packit eed494
const char *shout_get_agent(shout_t *self)
Packit eed494
{
Packit eed494
	if (!self)
Packit eed494
		return NULL;
Packit eed494
Packit eed494
	return self->useragent;
Packit eed494
}
Packit eed494
Packit eed494
Packit eed494
int shout_set_user(shout_t *self, const char *username)
Packit eed494
{
Packit eed494
	if (!self)
Packit eed494
		return SHOUTERR_INSANE;
Packit eed494
Packit eed494
	if (self->state != SHOUT_STATE_UNCONNECTED)
Packit eed494
		return self->error = SHOUTERR_CONNECTED;
Packit eed494
Packit eed494
	if (self->user)
Packit eed494
		free(self->user);
Packit eed494
Packit eed494
	if (! (self->user = _shout_util_strdup (username)))
Packit eed494
		return self->error = SHOUTERR_MALLOC;
Packit eed494
Packit eed494
	return self->error = SHOUTERR_SUCCESS;
Packit eed494
}
Packit eed494
Packit eed494
const char *shout_get_user(shout_t *self)
Packit eed494
{
Packit eed494
	if (!self)
Packit eed494
		return NULL;
Packit eed494
Packit eed494
	return self->user;
Packit eed494
}
Packit eed494
Packit eed494
int shout_set_description(shout_t *self, const char *description)
Packit eed494
{
Packit eed494
	if (!self)
Packit eed494
		return SHOUTERR_INSANE;
Packit eed494
Packit eed494
	if (self->state != SHOUT_STATE_UNCONNECTED)
Packit eed494
		return self->error = SHOUTERR_CONNECTED;
Packit eed494
Packit eed494
	if (self->description)
Packit eed494
		free(self->description);
Packit eed494
Packit eed494
	if (! (self->description = _shout_util_strdup (description)))
Packit eed494
		return self->error = SHOUTERR_MALLOC;
Packit eed494
Packit eed494
	return self->error = SHOUTERR_SUCCESS;
Packit eed494
}
Packit eed494
Packit eed494
const char *shout_get_description(shout_t *self)
Packit eed494
{
Packit eed494
	if (!self)
Packit eed494
		return NULL;
Packit eed494
Packit eed494
	return self->description;
Packit eed494
}
Packit eed494
Packit eed494
int shout_set_dumpfile(shout_t *self, const char *dumpfile)
Packit eed494
{
Packit eed494
	if (!self)
Packit eed494
		return SHOUTERR_INSANE;
Packit eed494
Packit eed494
	if (self->state != SHOUT_STATE_UNCONNECTED)
Packit eed494
		return SHOUTERR_CONNECTED;
Packit eed494
Packit eed494
	if (self->dumpfile)
Packit eed494
		free(self->dumpfile);
Packit eed494
Packit eed494
	if (! (self->dumpfile = _shout_util_strdup (dumpfile)))
Packit eed494
		return self->error = SHOUTERR_MALLOC;
Packit eed494
Packit eed494
	return self->error = SHOUTERR_SUCCESS;
Packit eed494
}
Packit eed494
Packit eed494
const char *shout_get_dumpfile(shout_t *self)
Packit eed494
{
Packit eed494
	if (!self)
Packit eed494
		return NULL;
Packit eed494
Packit eed494
	return self->dumpfile;
Packit eed494
}
Packit eed494
Packit eed494
int shout_set_audio_info(shout_t *self, const char *name, const char *value)
Packit eed494
{
Packit eed494
	return self->error = _shout_util_dict_set(self->audio_info, name, value);
Packit eed494
}
Packit eed494
Packit eed494
const char *shout_get_audio_info(shout_t *self, const char *name)
Packit eed494
{
Packit eed494
	return _shout_util_dict_get(self->audio_info, name);
Packit eed494
}
Packit eed494
Packit eed494
int shout_set_public(shout_t *self, unsigned int public)
Packit eed494
{
Packit eed494
	if (!self || (public != 0 && public != 1))
Packit eed494
		return SHOUTERR_INSANE;
Packit eed494
Packit eed494
	if (self->state != SHOUT_STATE_UNCONNECTED)
Packit eed494
		return self->error = SHOUTERR_CONNECTED;
Packit eed494
Packit eed494
	self->public = public;
Packit eed494
Packit eed494
	return self->error = SHOUTERR_SUCCESS;
Packit eed494
}
Packit eed494
Packit eed494
unsigned int shout_get_public(shout_t *self)
Packit eed494
{
Packit eed494
	if (!self)
Packit eed494
		return 0;
Packit eed494
Packit eed494
	return self->public;
Packit eed494
}
Packit eed494
Packit eed494
int shout_set_format(shout_t *self, unsigned int format)
Packit eed494
{
Packit eed494
	if (!self)
Packit eed494
		return SHOUTERR_INSANE;
Packit eed494
Packit eed494
	if (self->state != SHOUT_STATE_UNCONNECTED)
Packit eed494
		return self->error = SHOUTERR_CONNECTED;
Packit eed494
Packit eed494
	if (format != SHOUT_FORMAT_OGG && format != SHOUT_FORMAT_MP3)
Packit eed494
		return self->error = SHOUTERR_UNSUPPORTED;
Packit eed494
Packit eed494
	self->format = format;
Packit eed494
Packit eed494
	return self->error = SHOUTERR_SUCCESS;
Packit eed494
}
Packit eed494
Packit eed494
unsigned int shout_get_format(shout_t* self)
Packit eed494
{
Packit eed494
	if (!self)
Packit eed494
		return 0;
Packit eed494
Packit eed494
	return self->format;
Packit eed494
}
Packit eed494
Packit eed494
int shout_set_protocol(shout_t *self, unsigned int protocol)
Packit eed494
{
Packit eed494
	if (!self)
Packit eed494
		return SHOUTERR_INSANE;
Packit eed494
Packit eed494
	if (self->state != SHOUT_STATE_UNCONNECTED)
Packit eed494
		return self->error = SHOUTERR_CONNECTED;
Packit eed494
Packit eed494
	if (protocol != SHOUT_PROTOCOL_HTTP &&
Packit eed494
	    protocol != SHOUT_PROTOCOL_XAUDIOCAST &&
Packit eed494
	    protocol != SHOUT_PROTOCOL_ICY)
Packit eed494
		return self->error = SHOUTERR_UNSUPPORTED;
Packit eed494
Packit eed494
	self->protocol = protocol;
Packit eed494
Packit eed494
	return self->error = SHOUTERR_SUCCESS;
Packit eed494
}
Packit eed494
Packit eed494
unsigned int shout_get_protocol(shout_t *self)
Packit eed494
{
Packit eed494
	if (!self)
Packit eed494
		return 0;
Packit eed494
Packit eed494
	return self->protocol;
Packit eed494
}
Packit eed494
Packit eed494
int shout_set_nonblocking(shout_t *self, unsigned int nonblocking)
Packit eed494
{
Packit eed494
	if (!self || (nonblocking != 0 && nonblocking != 1))
Packit eed494
		return SHOUTERR_INSANE;
Packit eed494
Packit eed494
	if (self->state != SHOUT_STATE_UNCONNECTED)
Packit eed494
		return self->error = SHOUTERR_CONNECTED;
Packit eed494
Packit eed494
	self->nonblocking = nonblocking;
Packit eed494
Packit eed494
	return SHOUTERR_SUCCESS;
Packit eed494
}
Packit eed494
Packit eed494
unsigned int shout_get_nonblocking(shout_t *self)
Packit eed494
{
Packit eed494
	if (!self)
Packit eed494
		return 0;
Packit eed494
Packit eed494
	return self->nonblocking;
Packit eed494
}
Packit eed494
Packit eed494
/* -- static function definitions -- */
Packit eed494
Packit eed494
/* queue data in pages of SHOUT_BUFSIZE bytes */
Packit eed494
static int queue_data(shout_queue_t *queue, const unsigned char *data, size_t len)
Packit eed494
{
Packit eed494
	shout_buf_t *buf;
Packit eed494
	size_t plen;
Packit eed494
Packit eed494
	if (!len)
Packit eed494
		return SHOUTERR_SUCCESS;
Packit eed494
Packit eed494
	if (!queue->len) {
Packit eed494
		queue->head = calloc(1, sizeof (shout_buf_t));
Packit eed494
		if (! queue->head)
Packit eed494
			return SHOUTERR_MALLOC;
Packit eed494
	}
Packit eed494
Packit eed494
	for (buf = queue->head; buf->next; buf = buf->next);
Packit eed494
Packit eed494
	/* Maybe any added data should be freed if we hit a malloc error?
Packit eed494
	 * Otherwise it'd be impossible to tell where to start requeueing.
Packit eed494
	 * (As if anyone ever tried to recover from a malloc error.) */
Packit eed494
	while (len > 0) {
Packit eed494
		if (buf->len == SHOUT_BUFSIZE) {
Packit eed494
			buf->next = calloc(1, sizeof (shout_buf_t));
Packit eed494
			if (! buf->next)
Packit eed494
				return SHOUTERR_MALLOC;
Packit eed494
			buf->next->prev = buf;
Packit eed494
			buf = buf->next;
Packit eed494
		}
Packit eed494
Packit eed494
		plen = len > SHOUT_BUFSIZE - buf->len ? SHOUT_BUFSIZE - buf->len : len;
Packit eed494
		memcpy (buf->data + buf->len, data, plen);
Packit eed494
		buf->len += plen;
Packit eed494
		data += plen;
Packit eed494
		len -= plen;
Packit eed494
		queue->len += plen;
Packit eed494
	}
Packit eed494
Packit eed494
	return SHOUTERR_SUCCESS;
Packit eed494
}
Packit eed494
Packit eed494
static inline int queue_str(shout_t *self, const char *str)
Packit eed494
{
Packit eed494
	return queue_data(&self->wqueue, (const unsigned char*)str, strlen(str));
Packit eed494
}
Packit eed494
Packit eed494
/* this should be shared with sock_write. Create libicecommon. */
Packit eed494
static int queue_printf(shout_t *self, const char *fmt, ...)
Packit eed494
{
Packit eed494
	char buffer[1024];
Packit eed494
	char *buf;
Packit eed494
	va_list ap, ap_retry;
Packit eed494
	int len;
Packit eed494
Packit eed494
	buf = buffer;
Packit eed494
Packit eed494
	va_start(ap, fmt);
Packit eed494
	va_copy(ap_retry, ap);
Packit eed494
Packit eed494
	len = vsnprintf(buf, sizeof(buffer), fmt, ap);
Packit eed494
Packit eed494
	self->error = SHOUTERR_SUCCESS;
Packit eed494
	if (len > 0) {
Packit eed494
		if ((size_t)len < sizeof(buffer))
Packit eed494
			queue_data(&self->wqueue, (unsigned char*)buf, len);
Packit eed494
		else {
Packit eed494
			buf = malloc(++len);
Packit eed494
			if (buf) {
Packit eed494
				len = vsnprintf(buf, len, fmt, ap_retry);
Packit eed494
				queue_data(&self->wqueue, (unsigned char*)buf, len);
Packit eed494
				free(buf);
Packit eed494
			} else
Packit eed494
				self->error = SHOUTERR_MALLOC;
Packit eed494
		}
Packit eed494
	}
Packit eed494
Packit eed494
	va_end(ap_retry);
Packit eed494
	va_end(ap);
Packit eed494
Packit eed494
	return self->error;
Packit eed494
}
Packit eed494
Packit eed494
static inline void queue_free(shout_queue_t *queue)
Packit eed494
{
Packit eed494
	shout_buf_t *prev;
Packit eed494
Packit eed494
	while (queue->head) {
Packit eed494
		prev = queue->head;
Packit eed494
		queue->head = queue->head->next;
Packit eed494
		free(prev);
Packit eed494
	}
Packit eed494
	queue->len = 0;
Packit eed494
}
Packit eed494
Packit eed494
static int get_response(shout_t *self)
Packit eed494
{
Packit eed494
	char buf[1024];
Packit eed494
	int rc, blen;
Packit eed494
	char *pc;
Packit eed494
	shout_buf_t *queue;
Packit eed494
	int newlines = 0;
Packit eed494
Packit eed494
	rc = sock_read_bytes(self->socket, buf, sizeof(buf));
Packit eed494
Packit eed494
	if (rc < 0 && sock_recoverable(sock_error()))
Packit eed494
		return SHOUTERR_BUSY;
Packit eed494
	if (rc <= 0)
Packit eed494
		return SHOUTERR_SOCKET;
Packit eed494
Packit eed494
	if ((rc = queue_data(&self->rqueue, (unsigned char*)buf, rc)))
Packit eed494
		return rc;
Packit eed494
Packit eed494
	/* work from the back looking for \r?\n\r?\n. Anything else means more
Packit eed494
	 * is coming. */
Packit eed494
	for (queue = self->rqueue.head; queue->next; queue = queue->next);
Packit eed494
	pc = (char*)queue->data + queue->len - 1;
Packit eed494
	blen = queue->len;
Packit eed494
	while (blen) {
Packit eed494
		if (*pc == '\n')
Packit eed494
			newlines++;
Packit eed494
		/* we may have to scan the entire queue if we got a response with
Packit eed494
		 * data after the head line (this can happen with eg 401) */
Packit eed494
		else if (*pc != '\r')
Packit eed494
			newlines = 0;
Packit eed494
Packit eed494
		if (newlines == 2)
Packit eed494
			return SHOUTERR_SUCCESS;
Packit eed494
Packit eed494
		blen--;
Packit eed494
		pc--;
Packit eed494
Packit eed494
		if (!blen && queue->prev) {
Packit eed494
			queue = queue->prev;
Packit eed494
			pc = (char*)queue->data + queue->len - 1;
Packit eed494
			blen = queue->len;
Packit eed494
		}
Packit eed494
	}
Packit eed494
Packit eed494
	return SHOUTERR_BUSY;
Packit eed494
}
Packit eed494
Packit eed494
static int try_connect (shout_t *self)
Packit eed494
{
Packit eed494
	int rc;
Packit eed494
	int port;
Packit eed494
Packit eed494
	/* the breaks between cases are omitted intentionally */
Packit eed494
	switch (self->state) {
Packit eed494
	case SHOUT_STATE_UNCONNECTED:
Packit eed494
		port = self->port;
Packit eed494
		if (shout_get_protocol(self) == SHOUT_PROTOCOL_ICY)
Packit eed494
			port++;
Packit eed494
Packit eed494
		if (shout_get_nonblocking(self)) {
Packit eed494
			if ((self->socket = sock_connect_non_blocking(self->host, port)) < 0)
Packit eed494
				return self->error = SHOUTERR_NOCONNECT;
Packit eed494
			self->state = SHOUT_STATE_CONNECT_PENDING;
Packit eed494
		} else {
Packit eed494
			if ((self->socket = sock_connect(self->host, port)) < 0)
Packit eed494
				return self->error = SHOUTERR_NOCONNECT;
Packit eed494
			if ((rc = create_request(self)) != SHOUTERR_SUCCESS)
Packit eed494
				return rc;
Packit eed494
			self->state = SHOUT_STATE_REQ_PENDING;
Packit eed494
		}
Packit eed494
Packit eed494
	case SHOUT_STATE_CONNECT_PENDING:
Packit eed494
		if (shout_get_nonblocking(self)) {
Packit eed494
			if ((rc = sock_connected(self->socket, 0)) < 1) {
Packit eed494
				if (rc == SOCK_ERROR) {
Packit eed494
                                        rc = SHOUTERR_SOCKET;
Packit eed494
                                        goto failure;
Packit eed494
				} else
Packit eed494
					return SHOUTERR_BUSY;
Packit eed494
			}
Packit eed494
			if ((rc = create_request(self)) != SHOUTERR_SUCCESS)
Packit eed494
                                goto failure;
Packit eed494
		}
Packit eed494
		self->state = SHOUT_STATE_REQ_PENDING;
Packit eed494
Packit eed494
	case SHOUT_STATE_REQ_PENDING:
Packit eed494
		do
Packit eed494
			rc = send_queue(self);
Packit eed494
		while (!shout_get_nonblocking(self) && rc == SHOUTERR_BUSY);
Packit eed494
                if (rc == SHOUTERR_BUSY)
Packit eed494
                        return rc;
Packit eed494
		if (rc != SHOUTERR_SUCCESS)
Packit eed494
                        goto failure;
Packit eed494
		self->state = SHOUT_STATE_RESP_PENDING;
Packit eed494
Packit eed494
	case SHOUT_STATE_RESP_PENDING:
Packit eed494
		do
Packit eed494
			rc = get_response(self);
Packit eed494
		while (!shout_get_nonblocking(self) && rc == SHOUTERR_BUSY);
Packit eed494
                if (rc == SHOUTERR_BUSY)
Packit eed494
                        return rc;
Packit eed494
Packit eed494
		if (rc != SHOUTERR_SUCCESS)
Packit eed494
                        goto failure;
Packit eed494
Packit eed494
		if ((rc = parse_response(self)) != SHOUTERR_SUCCESS)
Packit eed494
                        goto failure;
Packit eed494
Packit eed494
		if (self->format == SHOUT_FORMAT_OGG) {
Packit eed494
			if ((rc = self->error = shout_open_ogg(self)) != SHOUTERR_SUCCESS)
Packit eed494
                                goto failure;
Packit eed494
		} else if (self->format == SHOUT_FORMAT_MP3) {
Packit eed494
			if ((rc = self->error = shout_open_mp3(self)) != SHOUTERR_SUCCESS)
Packit eed494
                                goto failure;
Packit eed494
		} else {
Packit eed494
                        rc = SHOUTERR_INSANE;
Packit eed494
                        goto failure;
Packit eed494
		}
Packit eed494
Packit eed494
	case SHOUT_STATE_CONNECTED:
Packit eed494
		self->state = SHOUT_STATE_CONNECTED;
Packit eed494
	}
Packit eed494
	
Packit eed494
	return SHOUTERR_SUCCESS;
Packit eed494
Packit eed494
failure:
Packit eed494
        shout_close(self);
Packit eed494
	return rc;
Packit eed494
}
Packit eed494
Packit eed494
static int try_write (shout_t *self, const void *data, size_t len)
Packit eed494
{
Packit eed494
    int ret;
Packit eed494
    size_t pos = 0;
Packit eed494
Packit eed494
    /* loop until whole buffer is written (unless it would block) */
Packit eed494
    do {
Packit eed494
        ret = sock_write_bytes (self->socket, data + pos, len - pos);
Packit eed494
        if (ret > 0)
Packit eed494
            pos += ret;
Packit eed494
    } while (pos < len && ret >= 0);
Packit eed494
Packit eed494
    if (ret < 0)
Packit eed494
    {
Packit eed494
        if (sock_recoverable (sock_error()))
Packit eed494
        {
Packit eed494
            self->error = SHOUTERR_BUSY;
Packit eed494
            return pos;
Packit eed494
        }
Packit eed494
        self->error = SHOUTERR_SOCKET;
Packit eed494
        return ret;
Packit eed494
    }
Packit eed494
Packit eed494
    return pos;
Packit eed494
}
Packit eed494
Packit eed494
/* collect nodes of a queue into a single buffer */
Packit eed494
static int collect_queue(shout_buf_t *queue, char **buf)
Packit eed494
{
Packit eed494
	shout_buf_t *node;
Packit eed494
	int pos = 0;
Packit eed494
	int len = 0;
Packit eed494
Packit eed494
	for (node = queue; node; node = node->next)
Packit eed494
		len += node->len;
Packit eed494
Packit eed494
	if (!(*buf = malloc(len)))
Packit eed494
		return SHOUTERR_MALLOC;
Packit eed494
Packit eed494
	for (node = queue; node; node = node->next) {
Packit eed494
		memcpy(*buf + pos, node->data, node->len);
Packit eed494
		pos += node->len;
Packit eed494
	}
Packit eed494
Packit eed494
	return len;
Packit eed494
}
Packit eed494
Packit eed494
static int send_queue(shout_t *self)
Packit eed494
{
Packit eed494
	shout_buf_t *buf;
Packit eed494
	int ret;
Packit eed494
Packit eed494
	if (!self->wqueue.len)
Packit eed494
		return SHOUTERR_SUCCESS;
Packit eed494
Packit eed494
	buf = self->wqueue.head;
Packit eed494
	while (buf) {
Packit eed494
		ret = try_write (self, buf->data + buf->pos, buf->len - buf->pos);
Packit eed494
		if (ret < 0)
Packit eed494
			return self->error;
Packit eed494
Packit eed494
		buf->pos += ret;
Packit eed494
		self->wqueue.len -= ret;
Packit eed494
		if (buf->pos == buf->len) {
Packit eed494
			self->wqueue.head = buf->next;
Packit eed494
			free(buf);
Packit eed494
			buf = self->wqueue.head;
Packit eed494
			if (buf)
Packit eed494
				buf->prev = NULL;
Packit eed494
		} else /* incomplete write */
Packit eed494
			return SHOUTERR_BUSY;
Packit eed494
	}
Packit eed494
Packit eed494
	return self->error = SHOUTERR_SUCCESS;
Packit eed494
}
Packit eed494
Packit eed494
static int create_request(shout_t *self)
Packit eed494
{
Packit eed494
	if (self->protocol == SHOUT_PROTOCOL_HTTP)
Packit eed494
		return create_http_request(self);
Packit eed494
	else if (self->protocol == SHOUT_PROTOCOL_XAUDIOCAST)
Packit eed494
		return create_xaudiocast_request(self);
Packit eed494
	else if (self->protocol == SHOUT_PROTOCOL_ICY)
Packit eed494
		return create_icy_request(self);
Packit eed494
Packit eed494
	return self->error = SHOUTERR_UNSUPPORTED;
Packit eed494
}
Packit eed494
Packit eed494
static int create_http_request(shout_t *self)
Packit eed494
{
Packit eed494
	char *auth;
Packit eed494
	char *ai;
Packit eed494
	int ret = SHOUTERR_MALLOC;
Packit eed494
Packit eed494
	/* this is lazy code that relies on the only error from queue_* being
Packit eed494
	 * SHOUTERR_MALLOC */
Packit eed494
	do {
Packit eed494
		if (queue_printf(self, "SOURCE %s HTTP/1.0\r\n", self->mount))
Packit eed494
			break;
Packit eed494
		if (self->password) {
Packit eed494
			if (! (auth = http_basic_authorization(self)))
Packit eed494
				break;
Packit eed494
			if (queue_str(self, auth)) {
Packit eed494
				free(auth);
Packit eed494
				break;
Packit eed494
			}
Packit eed494
			free(auth);
Packit eed494
		}
Packit eed494
		if (self->useragent && queue_printf(self, "User-Agent: %s\r\n", self->useragent))
Packit eed494
			break;
Packit eed494
		if (self->format == SHOUT_FORMAT_OGG && queue_printf(self, "Content-Type: application/ogg\r\n"))
Packit eed494
			break;
Packit eed494
		if (self->format == SHOUT_FORMAT_MP3 && queue_printf(self, "Content-Type: audio/mpeg\r\n"))
Packit eed494
			break;
Packit eed494
		if (queue_printf(self, "ice-name: %s\r\n", self->name ? self->name : "no name"))
Packit eed494
			break;
Packit eed494
		if (queue_printf(self, "ice-public: %d\r\n", self->public))
Packit eed494
			break;
Packit eed494
Packit eed494
		if (self->url && queue_printf(self, "ice-url: %s\r\n", self->url))
Packit eed494
			break;
Packit eed494
		if (self->genre && queue_printf(self, "ice-genre: %s\r\n", self->genre))
Packit eed494
			break;
Packit eed494
		if ((ai = _shout_util_dict_urlencode(self->audio_info, ';'))) {
Packit eed494
			if (queue_printf(self, "ice-audio-info: %s\r\n", ai)) {
Packit eed494
				free(ai);
Packit eed494
				break;
Packit eed494
			}
Packit eed494
			free(ai);
Packit eed494
		}
Packit eed494
		if (self->description && queue_printf(self, "ice-description: %s\r\n", self->description))
Packit eed494
			break;
Packit eed494
		if (queue_str(self, "\r\n"))
Packit eed494
			break;
Packit eed494
		
Packit eed494
		ret = SHOUTERR_SUCCESS;
Packit eed494
	} while (0);
Packit eed494
Packit eed494
	return ret;
Packit eed494
}
Packit eed494
Packit eed494
static char *http_basic_authorization(shout_t *self)
Packit eed494
{
Packit eed494
	char *out, *in;
Packit eed494
	int len;
Packit eed494
Packit eed494
	if (!self || !self->user || !self->password)
Packit eed494
		return NULL;
Packit eed494
Packit eed494
	len = strlen(self->user) + strlen(self->password) + 2;
Packit eed494
	if (!(in = malloc(len)))
Packit eed494
		return NULL;
Packit eed494
	sprintf(in, "%s:%s", self->user, self->password);
Packit eed494
	out = _shout_util_base64_encode(in);
Packit eed494
	free(in);
Packit eed494
Packit eed494
	len = strlen(out) + 24;
Packit eed494
	if (!(in = malloc(len))) {
Packit eed494
		free(out);
Packit eed494
		return NULL;
Packit eed494
	}
Packit eed494
	sprintf(in, "Authorization: Basic %s\r\n", out);
Packit eed494
	free(out);
Packit eed494
	
Packit eed494
	return in;
Packit eed494
}
Packit eed494
Packit eed494
static int parse_response(shout_t *self)
Packit eed494
{
Packit eed494
	if (self->protocol == SHOUT_PROTOCOL_HTTP)
Packit eed494
		return parse_http_response(self);
Packit eed494
	else if (self->protocol == SHOUT_PROTOCOL_XAUDIOCAST ||
Packit eed494
		 self->protocol == SHOUT_PROTOCOL_ICY)
Packit eed494
		return parse_xaudiocast_response(self);
Packit eed494
Packit eed494
	return self->error = SHOUTERR_UNSUPPORTED;
Packit eed494
}
Packit eed494
Packit eed494
static int parse_http_response(shout_t *self)
Packit eed494
{
Packit eed494
	http_parser_t *parser;
Packit eed494
	char *header = NULL;
Packit eed494
	int hlen = 0;
Packit eed494
	int code;
Packit eed494
	char *retcode;
Packit eed494
#if 0
Packit eed494
	char *realm;
Packit eed494
#endif
Packit eed494
Packit eed494
	/* all this copying! */
Packit eed494
	hlen = collect_queue(self->rqueue.head, &header);
Packit eed494
	if (hlen <= 0)
Packit eed494
		return SHOUTERR_MALLOC;
Packit eed494
	queue_free(&self->rqueue);
Packit eed494
Packit eed494
	parser = httpp_create_parser();
Packit eed494
	httpp_initialize(parser, NULL);
Packit eed494
	if (httpp_parse_response(parser, header, hlen, self->mount)) {
Packit eed494
		retcode = httpp_getvar(parser, HTTPP_VAR_ERROR_CODE);
Packit eed494
		code = atoi(retcode);
Packit eed494
		if(code >= 200 && code < 300) {
Packit eed494
			httpp_destroy(parser);
Packit eed494
			free (header);
Packit eed494
			return SHOUTERR_SUCCESS;
Packit eed494
		}
Packit eed494
	}
Packit eed494
Packit eed494
	free(header);
Packit eed494
	httpp_destroy(parser);
Packit eed494
	return self->error = SHOUTERR_NOLOGIN;
Packit eed494
}
Packit eed494
Packit eed494
static int create_xaudiocast_request(shout_t *self)
Packit eed494
{
Packit eed494
	const char *bitrate;
Packit eed494
	int ret;
Packit eed494
Packit eed494
	bitrate = shout_get_audio_info(self, SHOUT_AI_BITRATE);
Packit eed494
	if (!bitrate)
Packit eed494
		bitrate = "0";
Packit eed494
Packit eed494
	ret = SHOUTERR_MALLOC;
Packit eed494
	do {
Packit eed494
		if (queue_printf(self, "SOURCE %s %s\n", self->password, self->mount))
Packit eed494
			break;
Packit eed494
		if (queue_printf(self, "x-audiocast-name: %s\n", self->name ? self->name : "unnamed"))
Packit eed494
			break;
Packit eed494
		if (queue_printf(self, "x-audiocast-url: %s\n", self->url ? self->url : "http://www.icecast.org/"))
Packit eed494
			break;
Packit eed494
		if (queue_printf(self, "x-audiocast-genre: %s\n", self->genre ? self->genre : "icecast"))
Packit eed494
			break;
Packit eed494
		if (queue_printf(self, "x-audiocast-bitrate: %s\n", bitrate))
Packit eed494
			break;
Packit eed494
		if (queue_printf(self, "x-audiocast-public: %i\n", self->public))
Packit eed494
			break;
Packit eed494
		if (queue_printf(self, "x-audiocast-description: %s\n", self->description ? self->description : "Broadcasting with the icecast streaming media server!"))
Packit eed494
			break;
Packit eed494
		if (self->dumpfile && queue_printf(self, "x-audiocast-dumpfile: %s\n", self->dumpfile))
Packit eed494
			break;
Packit eed494
		if (queue_str(self, "\n"))
Packit eed494
			break;
Packit eed494
Packit eed494
		ret = SHOUTERR_SUCCESS;
Packit eed494
	} while (0);
Packit eed494
		
Packit eed494
	return ret;
Packit eed494
}
Packit eed494
Packit eed494
static int parse_xaudiocast_response(shout_t *self)
Packit eed494
{
Packit eed494
	char *response;
Packit eed494
Packit eed494
	if (collect_queue(self->rqueue.head, &response) <= 0)
Packit eed494
		return SHOUTERR_MALLOC;
Packit eed494
	queue_free(&self->rqueue);
Packit eed494
Packit eed494
	if (!strstr(response, "OK")) {
Packit eed494
		free(response);
Packit eed494
		return SHOUTERR_NOLOGIN;
Packit eed494
	}
Packit eed494
	free(response);
Packit eed494
Packit eed494
	return SHOUTERR_SUCCESS;
Packit eed494
}
Packit eed494
Packit eed494
static int create_icy_request(shout_t *self)
Packit eed494
{
Packit eed494
	const char *bitrate;
Packit eed494
	int ret;
Packit eed494
Packit eed494
	bitrate = shout_get_audio_info(self, SHOUT_AI_BITRATE);
Packit eed494
	if (!bitrate)
Packit eed494
		bitrate = "0";
Packit eed494
Packit eed494
	ret = SHOUTERR_MALLOC;
Packit eed494
	do {
Packit eed494
		if (queue_printf(self, "%s\n", self->password))
Packit eed494
			break;
Packit eed494
		if (queue_printf(self, "icy-name:%s\n", self->name ? self->name : "unnamed"))
Packit eed494
			break;
Packit eed494
		if (queue_printf(self, "icy-url:%s\n", self->url ? self->url : "http://www.icecast.org/"))
Packit eed494
			break;
Packit eed494
		if (queue_str(self, "icy-irc:\nicy-aim:\nicy-icq:\n"))
Packit eed494
			break;
Packit eed494
		if (queue_printf(self, "icy-pub:%i\n", self->public))
Packit eed494
			break;
Packit eed494
		if (queue_printf(self, "icy-genre:%s\n", self->genre ? self->genre : "icecast"))
Packit eed494
			break;
Packit eed494
		if (queue_printf(self, "icy-br:%s\n\n", bitrate))
Packit eed494
			break;
Packit eed494
Packit eed494
		ret = SHOUTERR_SUCCESS;
Packit eed494
	} while (0);
Packit eed494
Packit eed494
	return ret;
Packit eed494
}