[icecast-dev] Missing headers in Icecast2

Macsym macsym69 at yahoo.fr
Fri Dec 5 22:14:31 PST 2003


OOPS, I forgot to attach the source code!
MAX

-----Original Message-----
From: owner-icecast-dev at xiph.org [mailto:owner-icecast-dev at xiph.org] On
Behalf Of Macsym
Sent: Saturday, December 06, 2003 7:12 AM
To: icecast-dev at xiph.org
Subject: RE: [icecast-dev] Missing headers in Icecast2

Hi Karl,

I just checked in Icecast1 source, the line:

if (client_wants_content_length (con))
            sock_write (con->sock, "Cache-Control: no-cache\r\nPragma:
no-cache\r\nConnection: close\r\nContent-Length: 54000000\r\n");
          

is located in "client.c". Shouldn't I add this line in "client.c" of
Icecast2 instead of "format_mp3.c" as you advised me? If so, what should I
add exactly and where in the code (of Icecast2's client.c)?

Thanks in advance,

MAX

<p><p><p><p><p><p><p><p><p><p><p>-----Original Message-----
From: owner-icecast-dev at xiph.org [mailto:owner-icecast-dev at xiph.org] On
Behalf Of Macsym
Sent: Saturday, December 06, 2003 5:42 AM
To: icecast-dev at xiph.org
Subject: RE: [icecast-dev] Missing headers in Icecast2

Hi Karl,

Thanks for your help,
About the "Connection:" header, you are right, it's:
"Connection: close" and NOT "Connection: keep-alive". The protocol when the
SERVER sends the data is http 1.0. It's http 1.1 when the browser requests
the data.

I don't understand the "Content-Length: 54000000" header either. Also I
noticed the flash player on Windows/IE seems to stop after 52734KB are
loaded (54000000 bytes/1024 = 52734KB). I tried to change the
"Content-Length:" value but it didn't change anything...

I'll try changing the source code of Icecast2 as you advised, and compile
the modified code.

Thanks again,
MAX

<p>-----Original Message-----
From: owner-icecast-dev at xiph.org [mailto:owner-icecast-dev at xiph.org] On
Behalf Of Karl Heyes
Sent: Saturday, December 06, 2003 3:21 AM
To: icecast-dev at xiph.org
Subject: Re: [icecast-dev] Missing headers in Icecast2

On Sat, 2003-12-06 at 01:43, Macsym wrote:

> For Windows (Internet Explorer or AOL) some headers sent by Icecast2 are
> missing. These headers are sent by Icecast1 but NOT Icecast2 (it is the
> reason why it always works with Icecast1). These necessary headers are:

> -Cache-Control: no-cache
possibly

> -Pragma: no-cache
Isn't this from the client.

> -Connection: keep-alive
http 1.0 and http 1.1 differ on this. Sending "Connection: close" should
be ok though.  keep-alive isn't going to do anything with these types of
connections.

> -Content-Length: 54000000

well you don't know the length.

> Now that we identified the exact problem, we would like to force Icecast2
to
> send these headers to the client because it would avoid passing through
the
> PHP script (which is less reliable, slower the connection and uses more
> CPU). It is why I am contacting the dev-list today.

<p>> Can anybody advise us the best solution to force Icecast2 to send these
> headers to the browser? Should we build a patch or simply change some
source
> codes and compile the modified source?

add a couple of lines to verify it

in format_mp3.c at the end of function format_mp3_send_headers

Add something like

ock_write(client->con->sock, "Cache-Control: no-cache\r\n");

and whatever else.

karl.

<p>--- >8 ----
List archives:  http://www.xiph.org/archives/
icecast project homepage: http://www.icecast.org/
To unsubscribe from this list, send a message to
'icecast-dev-request at xiph.org'
containing only the word 'unsubscribe' in the body.  No subject is needed.
Unsubscribe messages sent to the list will be ignored/filtered.

<p>--- >8 ----
List archives:  http://www.xiph.org/archives/
icecast project homepage: http://www.icecast.org/
To unsubscribe from this list, send a message to
'icecast-dev-request at xiph.org'
containing only the word 'unsubscribe' in the body.  No subject is needed.
Unsubscribe messages sent to the list will be ignored/filtered.

<p>--- >8 ----
List archives:  http://www.xiph.org/archives/
icecast project homepage: http://www.icecast.org/
To unsubscribe from this list, send a message to
'icecast-dev-request at xiph.org'
containing only the word 'unsubscribe' in the body.  No subject is needed.
Unsubscribe messages sent to the list will be ignored/filtered.

-------------- next part --------------
/* client.c
 * - Client functions
 * Copyright (c) 1999 Jack Moffitt, Barath Raghavan, and Alexander Haväng
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 */

/* Last overhauled 990420 <eel at musiknet.se> */

#ifdef HAVE_CONFIG_H
#ifdef _WIN32
#include <win32config.h>
#else
#include <config.h>
#endif
#endif

#include "definitions.h"
#include <stdio.h>
#include "definitions.h"

#ifdef HAVE_SIGNAL_H
#include <signal.h>
#endif

#include <stdlib.h>
#include <stdarg.h>
# ifndef __USE_BSD
#  define __USE_BSD
# endif
#include <string.h>

#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

#include <sys/types.h>
#include <ctype.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>

#if defined (_WIN32)
#include <windows.h>
#define strncasecmp strnicmp
#else
#include <sys/socket.h> 
#include <sys/wait.h>
#include <netinet/in.h>
#endif

#include "avl.h"
#include "avl_functions.h"
#include "threads.h"
#include "icetypes.h"
#include "icecast.h"
#include "utility.h"
#include "ice_string.h"
#include "client.h"
#include "threads.h"
#include "connection.h"
#include "log.h"
#include "source.h"
#include "sock.h"
#include "restrict.h"
#include "static.h"
#include "memory.h"
#include "admin.h"
#include "http.h"
#include "vars.h"
#include "commands.h"
#include "authenticate/basic.h"
#include "match.h"
#include "pool.h"

#include <signal.h>

extern server_info_t info;

static void client_send_fake_file (connection_t *con);

/* Brand new client. Check what he wants, and either add him to
   the correct tree of clients (inside a source), or kill him off */
void client_login(connection_t *con, char *expr)
{
	char line[BUFSIZE];
	int go_on = 1;
	connection_t *source;
	request_t req;

	xa_debug(3, "Client login...\n");

	if (!con || !expr) {
		write_log(LOG_DEFAULT, "WARNING: client_login called with NULL pointer");
		return;
	}

	if (info.throttle_on) {
		sock_write(con->sock, "HTTP/1.0 406 Not Acceptable (using too much bandwidth)");
		kick_not_connected(con, "Bandwidth usage too high (throttling)");
		return;
	}

#ifdef HAVE_LIBWRAP
	if (!sock_check_libwrap (con->sock, client_e)) {
		write_http_code_page (con, 403, "Forbidden");
		kick_not_connected (con, "Access Denied (libwrap (client connection))");
		return;
	}
#endif
	if (!allowed(con, client_e)) {
		write_http_code_page (con, 403, "Forbidden");
		kick_not_connected (con, "Access Denied (internal acl list (client connection))");
		return;
	}

	zero_request(&req);
	
	con->headervars = create_header_vars ();

	do {
		if (splitc(line, expr, '\n') == NULL) {
			strncpy(line, expr, BUFSIZE);
			go_on = 0;
		}
      
		if (ice_strncmp(line, "GET", 3) == 0) {
			build_request(line, &req);
		} else {
                        if (ice_strncmp(line, "Host:", 5) == 0 || (ice_strncmp(line, "HOST:", 5) == 0))
                                build_request(line, &req);
			else
				extract_header_vars (line, con->headervars);
		}
	} while (go_on);

	if (need_authentication (&req))
	{
		if (!authenticate_user_request (con, &req))
		{
			write_401 (con, req.path);
			kick_not_connected (con, "Not authorized");
			return;
		}
	}
	
	if (ice_strcmp(req.path, "/list.cgi") == 0) {
		show_mountlist(con, &req);
		kick_not_connected(con, "Displayed mountlist");
		return;
	}
	
	if (ice_strncmp(req.path, "/playlist.pls", 13) == 0) {
		gen_playlist(con, &req);
		kick_not_connected (con, "Generated playlist");
		return;
	}

	if (ice_strncmp(req.path, "/admin", 6) == 0)
	{
		int err;
		
		char *secfile = get_icecast_file (info.mountfile, conf_file_e, R_OK);

		if (!secfile && strstr (req.path, "updinfo") == NULL) {
			write_http_code_page (con, 403, "Forbidden");
			write_log (LOG_DEFAULT, "No mountfile found, refusing access to WWW admin for %s", con_host (con));
			kick_not_connected (con, "No mountfile found");
			return;
		}
			
		err = http_admin_command (con, &req);

		if (err)
		{
			xa_debug (2, "DEBUG: kicking %s, executed admin command", con_host (con));
			kick_not_connected (con, NULL);
		} else
			kick_not_connected (con, "Failed to execute admin command");
		return;
	}

	if (ice_strncmp(req.path, "/file/", 6) == 0) {
		thread_rename("Static File Thread");
		if (req.path[ice_strlen (req.path) - 1] == '/')
		{
			list_directory (con, req.path);
			kick_not_connected (con, "Displayed directory list");
		} else {
			send_file(con, &req);
			kick_not_connected(con, "Sent static file");
		}
		return;
	}
	
	xa_debug (1, "Looking for mount [%s:%d%s]", req.host, req.port, req.path);
	
	/* Try to find a mount point with this name */
	thread_mutex_lock (&info.double_mutex);
	thread_mutex_lock (&info.mount_mutex);
	thread_mutex_lock (&info.source_mutex);

	source = find_mount_with_req (&req);
	
	thread_mutex_unlock (&info.source_mutex);
	thread_mutex_unlock (&info.mount_mutex);
	thread_mutex_unlock (&info.double_mutex);

	/* Ok, none found, give him the default mount */
	if (source == NULL && info.mount_fallback)
		source = get_default_mount();
	  
	/* No LOG_DEFAULT, then no encoder, kick him out */
	if (source == NULL) {
		write_http_code_page (con, 404, "Stream not found");
		kick_not_connected (con, "No encoder");
		return;
	}

	if (need_authentication_on_mount (source->food.source->audiocast.mount))
	{
		if (!authenticate_user_request (con, &req))
		{
		  write_401 (con, req.path);
		  kick_not_connected (con, "Not authorized");
		  return;
		}
	}

	if ((info.num_clients >= info.max_clients) 
	    || (source->food.source->num_clients >= info.max_clients_per_source))
	{
		write_http_code_page (con, 504, "Server Full");
		if (info.num_clients >= info.max_clients)
			xa_debug (2, "DEBUG: inc > imc: %lu %lu", info.num_clients, info.max_clients);
		else if (source->food.source->num_clients >= info.max_clients_per_source)
			xa_debug (2, "DEBUG: snc > smc: %lu %lu", source->food.source->num_clients, info.max_clients_per_source);
		else 
			xa_debug (1, "ERROR: Erroneous number of clients, what the hell is going on?");

		kick_not_connected (con, "Server Full (too many listeners)");
		return;
	}

	put_client(con);
	con->food.client->type = listener_e;

	{
		const char *umd = get_con_variable (con, "Icy-MetaData");
		if (umd)
			con->food.client->use_icy_metadata = atoi (umd);
	}

	{
		const char *ref = get_con_variable (con, "Referer");
		if (ref && ice_strcmp (ref, "RELAY") == 0)
			con->food.client->type = pulling_client_e;
	}
			
	/* Change the sockaddr_in for the client to point to the port the client specified */
	if (con->sin)
	{
		const char *sport = get_con_variable (con, "x-audiocast-udpport");
		if (sport)
		{
			con->sin->sin_port = htons (atoi (sport));
			con->food.client->use_udp = 1;
			xa_debug (1, "DEBUG: client_login(): Client listening on udp port %d", atoi (sport));
		}
	}

	if (con->food.client->use_icy_metadata && con->food.client->use_udp)
		con->food.client->use_icy_metadata = 0;
	
	util_increase_total_clients ();
	
	con->food.client->source = source->food.source;

	/* FIXME: Dangerous */
	source->food.source->stats.client_connections++;

	pool_add (con);

	write_log(LOG_DEFAULT, "Accepted client %d from [%s] on mountpoint [%s]. %d clients connected", con->id, 
		  con_host (con), source->food.source->audiocast.mount, info.num_clients);
	greet_client(con, source->food.source);
}

client_t *
create_client()
{
	client_t *client = (client_t *)nmalloc(sizeof(client_t));
	client->type = unknown_client_e;
	return client;
}

void put_client(connection_t *con)
{
	client_t *cli = create_client();
	con->food.client = cli;
	cli->errors = 0;
	cli->type = unknown_client_e;
	cli->write_bytes = 0;
	cli->virgin = -1;
	cli->source = NULL;
	cli->cid = -1;
	cli->offset = 0;
	cli->alive = CLIENT_ALIVE;
	cli->use_icy_metadata = 0;
	cli->metadataoffset = 0;
	cli->metadatalen = 0;
	cli->metadatawritten = 0;
	cli->use_icy = 0;
	cli->use_udp = 0;
	con->type = client_e;
	cli->udpseqnr = 0;
}

void 
gen_playlist (connection_t *con, request_t *req)
{
	const char *mount_point;
	vartree_t *req_vars = avl_create(compare_vars, &info);
	
	extract_vars(req_vars, req->path);
	mount_point = get_variable(req_vars, "mount");

	xa_debug (1, "DEBUG: Generating playlist");

	write_http_header(con->sock, 200, "OK");

	sock_write_line (con->sock, "Connection: close");
	sock_write_line (con->sock, "Content-Type: audio/x-scpls\r\n");


	sock_write_line (con->sock, "[playlist]\r\n");
	sock_write_line (con->sock, "NumberOfEntries=1");
	sock_write_line (con->sock, "File1=http://%s:%i%s", info.server_name, info.port[0], mount_point ? mount_point : "/");

	free_variables(req_vars);
}

void 
show_mountlist(connection_t *con, request_t *req)
{
  char *filename = get_template ("mountlist.html");
  avl_traverser trav = {0};
  connection_t *sourcecon;
  int i = 0;
  
  xa_debug (1, "DEBUG: Displaying mountlist [%s]", filename ? filename : "internal");

  if (filename)
    {
      write_template_parsed_html_page (con, NULL, filename, -1, NULL);
      nfree (filename);
      return;
    }
  
  thread_mutex_lock(&info.source_mutex);
  
  write_http_header(con->sock, 200, "OK");
  
  sock_write_line (con->sock, "Connection: close");
  sock_write_line (con->sock, "Content-Type: text/html\r\n");
  
  sock_write_line (con->sock, "<html><head><title>icecast server, version %s, running on %s</title></head>", VERSION, info.server_name);
  sock_write_line (con->sock, "<body bgcolor=black text=white link=lightblue alink=lightblue vlink=lightblue><h3><tt>icecast server, version %s, running on %s</tt></h3><br>", VERSION, info.server_name);

	for (i = 0; i < MAXLISTEN; i++) 
		if (info.port[i] > 0) 
			sock_write_line (con->sock, "<p>Listening on port %d<br>", info.port[i]);
  
  if (info.location)
    sock_write_line (con->sock, "Server location: %s<br>", info.location);
  if (info.rp_email)
    sock_write_line (con->sock, "Server admin: <a href=\"mailto:%s\">%s</a><br>", info.rp_email, info.rp_email);
  if (info.server_url)
    sock_write_line (con->sock, "Server URL: <a href=\"%s\">%s</a><br></p>", info.server_url, info.server_url);
  
  sock_write_line (con->sock, "<table border=0 cellspacing=0 cellpadding=3><tr><td><font face=\"sans-serif\" size=+2>Stream Link</font></td><td><font face=\"sans-serif\" size=+2>Name</font></td><td><font face=\"sans-serif\" size=+2>URL</font></td><td><font face=\"sans-serif\" size=+2>Genre</font></td><td><font face=\"sans-serif\" size=+2>Description</font></td></tr>");
  
  while ((sourcecon = avl_traverse(info.sources, &trav))) {
    
    i++;
    i % 2 ? sock_write_line (con->sock, "<tr bgcolor=#000000>") : sock_write_line (con->sock, "<tr bgcolor=#333333>");
    
    sock_write_line (con->sock, "<td><tt><a href=\"/playlist.pls?mount=%s&file=dummy.pls\">%s</a></tt></td><td><tt>%s</tt></td><td><tt><a href=\"%s\">%s</a></tt></td><td><tt>%s</tt></td><td><tt>%s</tt></td></tr>",
		     sourcecon->food.source->audiocast.mount, 
		     sourcecon->food.source->audiocast.mount, 
		     sourcecon->food.source->audiocast.name, 
		     sourcecon->food.source->audiocast.url,
		     sourcecon->food.source->audiocast.url,
		     sourcecon->food.source->audiocast.genre, 
		     sourcecon->food.source->audiocast.description);
  }
  
  sock_write_line (con->sock, "</table></body></html>");
  thread_mutex_unlock(&info.source_mutex);
}

void util_increase_total_clients ()
{
	internal_lock_mutex (&info.misc_mutex);
	info.num_clients++;
	info.hourly_stats.client_connections++;
	internal_unlock_mutex (&info.misc_mutex);
}

void
util_decrease_total_clients ()
{
	internal_lock_mutex (&info.misc_mutex);
	info.num_clients--;
	internal_unlock_mutex (&info.misc_mutex);
}

void 
del_client(connection_t *client, source_t *source)
{
	if (!client || !source) {
		write_log(LOG_DEFAULT, "WARNING: del_client() called with NULL pointer");
		return;
	}

	if (source && client->food.client && (client->food.client->virgin != 1) && (client->food.client->virgin != -1)) {
		if (source->num_clients == 0)
			write_log (LOG_DEFAULT, "WARNING: Bloody going below limits on client count!");
		else
			source->num_clients--;
	}
	util_decrease_total_clients ();
}

int
client_wants_udp_info (connection_t *con)
{
	const char *val;

	if (!con)
		return 0;
	
	val = get_con_variable (con, "x-audiocast-udpport");
	if (!val)
		return 0;
	
	return 1;
}

int 
client_wants_content_length(connection_t *con)
{
	const char *val;

	if (!con)
		return 0;

	val = get_user_agent (con);
	if (!val)
		return 0;

	if (strstr(val, "MSIE"))
		return 1;
	if (strstr(val, "RMA/1.0"))
		return 1; 
	if (strstr(val, "NSPlayer"))
		return 1;
	return 0;
}

int 
client_wants_icy_headers (connection_t *con)
{
	const char *val;

	if (!con)
		return 1;

	val = get_user_agent (con);
	if (!val || !val[0] || strcmp (val, "(null)") == 0)
		return 1;

	if (con->food.client->use_icy)
		return 1;
	if (strncasecmp (val, "winamp", 6) == 0)
		return 1;
	if (strncasecmp (val, "Shoutcast", 9) == 0)
		return 1;

	return 0;
}

int
client_wants_metadata (connection_t *con)
{
	if (!con)
		return 0;
	if (con->food.client->use_icy_metadata && info.use_meta_data)
		return 1;
	return 0;
}

void
print_relay_ids (connection_t *con, source_t *source)
{
	avl_traverser trav = {0};
	relay_id_t *rid;

	thread_mutex_lock (&source->mutex);

	while ((rid = avl_traverse (source->relay_tree, &trav)))
	{
		if (!rid || !rid->host)
		{
			write_log (LOG_DEFAULT, "ERROR: Erroneous relay id:s...");
			continue;
		}
		xa_debug (1, "DEBUG: Displaying dir-id: %s:%d to %d", rid->host, rid->id, con->id);
		sock_write_line (con->sock, "x-audiocast-directory: %s:%d", rid->host, rid->id);
	}

	thread_mutex_unlock (&source->mutex);
}


int
client_wants_relay_ids (connection_t *con)
{
	return 0; /* Currently disabled awaiting new interface */
}

void 
greet_client(connection_t *con, source_t *source)
{
#ifdef _WIN32
	int bufsize = 16384; /* Is that a buffer in your pocket, or are you just happy to see me? :) */
#endif

	if (!con) {
		write_log(LOG_DEFAULT, "WARNING: greet_client called with NULL pointer");
		return;
	}

	con->food.client->udpseqnr = source->info.udpseqnr > 0 ? source->info.udpseqnr - 1 : source->info.udpseqnr;

	if (client_wants_icy_headers (con)) {
		  xa_debug (2, "DEBUG: client [%s] wants icy headers", con_host (con));
		  sock_write_line (con->sock, "ICY 200 OK");
		  sock_write_line (con->sock, 
		  "icy-notice1:<BR>This stream requires <a href=\"http://www.winamp.com/\">Winamp</a><BR>");
		  sock_write_line (con->sock, "icy-notice2:SHOUTcast Distributed Network Audio Server/posix v1.6.0rc2<BR>");
		  
		  if (client_wants_metadata (con))
		  sock_write_line (con->sock, "icy-metaint:%u", info.metainterval);
		  
		  if (client_wants_udp_info (con))
		  sock_write_line (con->sock, "x-audiocast-udpport: %d", info.port[0]);
		  
		  sock_write (con->sock, "icy-name:%s\r\nicy-genre:%s\r\nicy-url:%s\r\nicy-pub:%d\nicy-br:%d\r\n\r\n",
		  source->audiocast.name, source->audiocast.genre, source->audiocast.url, source->audiocast.public,
		  source->audiocast.bitrate); 
	} else {
	  xa_debug (2, "DEBUG: client [%s] wants xaudiocast headers", con_host (con));
	  
	  write_http_header (con->sock, 200, "OK");
	  if (source->audiocast.streammimetype)
		  sock_write_line (con->sock, "Content-Type: %s", source->audiocast.streammimetype);
	  else
		  sock_write_line (con->sock, "Content-Type: audio/mpeg");

	  if (client_wants_content_length (con))
	    sock_write (con->sock, "Cache-Control: no-cache\r\nPragma: no-cache\r\nConnection: close\r\nContent-Length: 54000000\r\n");
	  
	  if (info.location)
	    sock_write_line (con->sock, "x-audiocast-location: %s", info.location);
		
	  if (info.rp_email)
	    sock_write_line (con->sock, "x-audiocast-admin: %s", info.rp_email);
		
	  if (info.server_url)
	    sock_write_line (con->sock, "x-audiocast-server-url: %s", info.server_url);
	  
	  if (client_wants_relay_ids (con))
	    print_relay_ids (con, source);
	  
	  if (client_wants_metadata (con))
		  sock_write_line (con->sock, "icy-metaint:%u", info.metainterval);
	  
	  if (client_wants_udp_info (con))
		  sock_write_line (con->sock, "x-audiocast-udpport: %d", info.port[0]);
	  
	  sock_write (con->sock, "x-audiocast-mount:%s\r\nx-audiocast-name:%s\r\nx-audiocast-description:%s\r\nx-audiocast-url:%s\r\nx-audiocast-genre:%s\r\nx-audiocast-bitrate:%d\r\nx-audiocast-public:%d\r\n\r\n", nullcheck_string (source->audiocast.mount), nullcheck_string (source->audiocast.name), nullcheck_string (source->audiocast.description), nullcheck_string (source->audiocast.url), nullcheck_string (source->audiocast.genre), source->audiocast.bitrate, source->audiocast.public);
	}
	
	sock_set_blocking(con->sock, SOCK_NONBLOCK);
	
#ifdef _WIN32
	setsockopt(con->sock, SOL_SOCKET, SO_SNDBUF, (char *)&bufsize, sizeof(int));
#endif
	
	con->food.client->virgin = 1;

	if (con->food.client->type == pulling_client_e) {
	  char* title = NULL;
	  char* msg = NULL;
	  char length[11];
	  char* url = NULL;

	  url_encode (source->info.streamtitle, &title);
	  url_encode (source->info.streammsg, &msg);
	  snprintf (length, sizeof (length), "%ld", source->info.streamlength);
	  url_encode (source->info.streamurl, &url);
	  update_metadata_on_relay (con, source->audiocast.mount, title, msg, length, url);
	  nfree (title);
	  nfree (msg);
	  nfree (url);
	}
}

void
describe_client (const com_request_t *req, const connection_t *clicon)
{
	const client_t *client;

	if (!req || !clicon)
	{
		xa_debug (1, "WARNING: describe_client(): called with NULL pointers");
		return;
	}

	if (clicon->type != client_e)
	{
		xa_debug (1, "WARNING: describe_client(): called with invalid type");
		return;
	}

	describe_connection (req, clicon);
	
	client = clicon->food.client;

	admin_write_line (req, ADMIN_SHOW_DESCRIBE_CLIENT_START, "Misc client info:");
	admin_write_line (req, ADMIN_SHOW_DESCRIBE_CLIENT_MISC, "UDPinfo: %s", client->use_udp ? "yes" : "no");
	if (client->use_udp)
		admin_write_line (req, ADMIN_SHOW_DESCRIBE_CLIENT_MISC, "UDP client port: %d", htons (clicon->sin->sin_port));
	admin_write_line (req, ADMIN_SHOW_DESCRIBE_CLIENT_MISC, "UDP sequence number: %d", client->udpseqnr);
	admin_write_line (req, ADMIN_SHOW_DESCRIBE_CLIENT_MISC, "ICY metadata: %s", client->use_icy_metadata ? "yes" : "no");
	admin_write_line (req, ADMIN_SHOW_DESCRIBE_CLIENT_MISC, "Transfer error balance: %d", client_errors (client));
	admin_write_line (req, ADMIN_SHOW_DESCRIBE_CLIENT_MISC, "Transfer chunk id and offset: %d : %d", client->cid, client->offset);
	admin_write_line (req, ADMIN_SHOW_DESCRIBE_CLIENT_MISC, "Bytes transfered: %lu", client->write_bytes);
	admin_write_line (req, ADMIN_SHOW_DESCRIBE_CLIENT_MISC, "Virgin: %s", client->virgin ? "yes" : "no");
	admin_write_line (req, ADMIN_SHOW_DESCRIBE_CLIENT_MISC, "Client type: %s", client_type (clicon));
	if (client->source && client->source->audiocast.mount)
		admin_write_line (req, ADMIN_SHOW_DESCRIBE_CLIENT_MISC, "Mountpoint: %s", client->source->audiocast.mount);
	admin_write_line (req, ADMIN_SHOW_DESCRIBE_CLIENT_END, "End of client info");
}

const char client_types[4][16] = { "listener", "pusher", "puller", "unknown listener" };

const char *
client_type (const connection_t *clicon)
{
	switch (clicon->food.client->type)
	{
		case listener_e:
			return client_types[0];
			break;
		case pusher_e:
			return client_types[1];
			break;
		case pulling_client_e:
			return client_types[2];
			break;
		default:
			return client_types[3];
			break;
	}
}

int
client_errors (const client_t *client)
{
	if (!client || !client->source)
		return 0;
	
	return (CHUNKLEN - (client->cid - client->source->cid)) % CHUNKLEN;
}

-------------- next part --------------
/* client.c
**
** client interface implementation
**
*/

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdlib.h>
#include <string.h>

#include "thread/thread.h"
#include "avl/avl.h"
#include "httpp/httpp.h"

#include "connection.h"
#include "refbuf.h"

#include "client.h"
#include "logging.h"

client_t *client_create(connection_t *con, http_parser_t *parser)
{
    client_t *client = (client_t *)calloc(1, sizeof(client_t));

    client->con = con;
    client->parser = parser;
    client->queue = NULL;
    client->pos = 0;

    return client;
}

void client_destroy(client_t *client)
{
    refbuf_t *refbuf;

    /* write log entry if ip is set (some things don't set it, like outgoing 
     * slave requests
     */
    if(client->con->ip)
        logging_access(client);
    
    connection_close(client->con);
    httpp_destroy(client->parser);

    while ((refbuf = refbuf_queue_remove(&client->queue)))
        refbuf_release(refbuf);

    free(client);
}

void client_send_400(client_t *client, char *message) {
    int bytes;
    bytes = sock_write(client->con->sock, "HTTP/1.0 404 File Not Found\r\n"
            "Content-Type: text/html\r\n\r\n"
            "<b>%s</b>\r\n", message);
    if(bytes > 0) client->con->sent_bytes = bytes;
    client->respcode = 404;
    client_destroy(client);
}

void client_send_404(client_t *client, char *message) {

    int bytes;
    bytes = sock_write(client->con->sock, "HTTP/1.0 404 File Not Found\r\n"
            "Content-Type: text/html\r\n\r\n"
            "<b>%s</b>\r\n", message);
    if(bytes > 0) client->con->sent_bytes = bytes;
    client->respcode = 404;
    client_destroy(client);
}

void client_send_504(client_t *client, char *message) {
    int bytes;
    client->respcode = 504;
    bytes = sock_write(client->con->sock, 
            "HTTP/1.0 504 Server Full\r\n"
            "Content-Type: text/html\r\n\r\n"
            "<b>%s</b>\r\n", message);
       if (bytes > 0) client->con->sent_bytes = bytes;
    client_destroy(client);
}

void client_send_401(client_t *client) {
    int bytes = sock_write(client->con->sock, 
            "HTTP/1.0 401 Authentication Required\r\n"
            "WWW-Authenticate: Basic realm=\"Icecast2 Server\"\r\n"
            "\r\n"
            "You need to authenticate\r\n");
    if(bytes > 0) client->con->sent_bytes = bytes;
    client->respcode = 401;
    client_destroy(client);
}



More information about the Icecast-dev mailing list