Blame src/httpget.c

Packit c32a2d
/*
Packit c32a2d
	httpget.c: http communication
Packit c32a2d
Packit c32a2d
	copyright ?-2011 by the mpg123 project - free software under the terms of the LGPL 2.1
Packit c32a2d
	see COPYING and AUTHORS files in distribution or http://mpg123.org
Packit c32a2d
	initially written Oliver Fromme
Packit c32a2d
	old timestamp: Wed Apr  9 20:57:47 MET DST 1997
Packit c32a2d
Packit c32a2d
	Thomas' notes:
Packit c32a2d
	;
Packit c32a2d
	I used to do 
Packit c32a2d
	GET http://server/path HTTP/1.0
Packit c32a2d
Packit c32a2d
	But RFC 1945 says: The absoluteURI form is only allowed when the request is being made to a proxy.
Packit c32a2d
Packit c32a2d
	so I should not do that. Since name based virtual hosts need the hostname in the request, I still need to provide that info.
Packit c32a2d
	Enter HTTP/1.1... there is a Host eader field to use (that mpg123 supposedly has used since some time anyway - but did it really work with my vhost test server)?
Packit c32a2d
	Now
Packit c32a2d
	GET /path/bla HTTP/1.1\r\nHost: host[:port]
Packit c32a2d
	Should work, but as a funny sidenote:
Packit c32a2d
	
Packit c32a2d
	RFC2616: To allow for transition to absoluteURIs in all requests in future versions of HTTP, all HTTP/1.1 servers MUST accept the absoluteURI form in requests, even though HTTP/1.1 clients will only generate them in requests to proxies.
Packit c32a2d
	
Packit c32a2d
	I was already full-on HTTP/1.1 as I recognized that mpg123 then would have to accept the chunked transfer encoding.
Packit c32a2d
	That is not desireable for its purpose... maybe when interleaving of shoutcasts with metadata chunks is supported, we can upgrade to 1.1.
Packit c32a2d
	Funny aspect there is that shoutcast servers do not do HTTP/1.1 chunked transfer but implement some different chunking themselves...
Packit c32a2d
*/
Packit c32a2d
Packit c32a2d
#include "mpg123app.h"
Packit c32a2d
#include "httpget.h"
Packit c32a2d
Packit c32a2d
#ifdef NETWORK
Packit c32a2d
#include "resolver.h"
Packit c32a2d
Packit c32a2d
#include <errno.h>
Packit c32a2d
#include "true.h"
Packit c32a2d
#endif
Packit c32a2d
Packit c32a2d
#include <ctype.h>
Packit c32a2d
Packit c32a2d
#include "debug.h"
Packit c32a2d
Packit c32a2d
void httpdata_init(struct httpdata *e)
Packit c32a2d
{
Packit c32a2d
	mpg123_init_string(&e->content_type);
Packit c32a2d
	mpg123_init_string(&e->icy_url);
Packit c32a2d
	mpg123_init_string(&e->icy_name);
Packit c32a2d
	e->icy_interval = 0;
Packit c32a2d
	e->proxystate = PROXY_UNKNOWN;
Packit c32a2d
	mpg123_init_string(&e->proxyhost);
Packit c32a2d
	mpg123_init_string(&e->proxyport);
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
void httpdata_reset(struct httpdata *e)
Packit c32a2d
{
Packit c32a2d
	mpg123_free_string(&e->content_type);
Packit c32a2d
	mpg123_free_string(&e->icy_url);
Packit c32a2d
	mpg123_free_string(&e->icy_name);
Packit c32a2d
	e->icy_interval = 0;
Packit c32a2d
	/* the other stuff shall persist */
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
void httpdata_free(struct httpdata *e)
Packit c32a2d
{
Packit c32a2d
	httpdata_reset(e);
Packit c32a2d
	mpg123_free_string(&e->proxyhost);
Packit c32a2d
	mpg123_free_string(&e->proxyport);
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
/* mime type classes */
Packit c32a2d
#define M_FILE 0
Packit c32a2d
#define M_M3U  1
Packit c32a2d
#define M_PLS  2
Packit c32a2d
static const char* mime_file[] =
Packit c32a2d
{
Packit c32a2d
	"audio/mpeg",  "audio/x-mpeg",
Packit c32a2d
	"audio/mp3",   "audio/x-mp3", 
Packit c32a2d
	"audio/mpeg3", "audio/x-mpeg3",
Packit c32a2d
	"audio/mpg",   "audio/x-mpg",
Packit c32a2d
	"audio/x-mpegaudio",
Packit c32a2d
	"application/octet-stream", /* Assume raw binary data is some MPEG data. */
Packit c32a2d
	NULL
Packit c32a2d
};
Packit c32a2d
static const char* mime_m3u[] = { "audio/mpegurl", "audio/mpeg-url", "audio/x-mpegurl", NULL };
Packit c32a2d
static const char* mime_pls[]	=
Packit c32a2d
{
Packit c32a2d
	"audio/x-scpls"
Packit c32a2d
,	"audio/scpls"
Packit c32a2d
,	"application/pls"
Packit c32a2d
,	"application/x-scpls"
Packit c32a2d
,	"application/pls+xml"
Packit c32a2d
,	NULL
Packit c32a2d
};
Packit c32a2d
static const char** mimes[] = { mime_file, mime_m3u, mime_pls, NULL };
Packit c32a2d
Packit c32a2d
int debunk_mime(const char* mime)
Packit c32a2d
{
Packit c32a2d
	int i,j;
Packit c32a2d
	size_t len;
Packit c32a2d
	int r = 0;
Packit c32a2d
	char *aux;
Packit c32a2d
	/* Watch out for such: "audio/x-mpegurl; charset=utf-8" */
Packit c32a2d
	aux = strchr(mime, ';');
Packit c32a2d
	if(aux != NULL)
Packit c32a2d
	{
Packit c32a2d
		if(!param.quiet)
Packit c32a2d
			fprintf(stderr, "Warning: additional info in content-type ignored (%s)\n", aux+1);
Packit c32a2d
		/* Just compare up to before the ";" */
Packit c32a2d
		len = aux-mime;
Packit c32a2d
	}
Packit c32a2d
	/* Else, compare the whole string -- including the end. */
Packit c32a2d
	else len = strlen(mime)+1;
Packit c32a2d
Packit c32a2d
	/* Skip trailing whitespace, to ne nice to strange folks. */
Packit c32a2d
	while(len && isspace(mime[len-1])) --len;
Packit c32a2d
Packit c32a2d
	for(i=0; mimes[i]    != NULL; ++i)
Packit c32a2d
	for(j=0; mimes[i][j] != NULL; ++j)
Packit c32a2d
	if(!strncasecmp(mimes[i][j], mime, len)) goto debunk_result;
Packit c32a2d
Packit c32a2d
debunk_result:
Packit c32a2d
	if(mimes[i] != NULL)
Packit c32a2d
	{
Packit c32a2d
		switch(i)
Packit c32a2d
		{
Packit c32a2d
			case M_FILE: r = IS_FILE;        break;
Packit c32a2d
			case M_M3U:  r = IS_LIST|IS_M3U; break;
Packit c32a2d
			case M_PLS:  r = IS_LIST|IS_PLS; break;
Packit c32a2d
			default: error("unexpected MIME debunk result -- coding error?!");
Packit c32a2d
		}
Packit c32a2d
	}
Packit c32a2d
	return r;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
Packit c32a2d
#ifdef NETWORK
Packit c32a2d
#if !defined (WANT_WIN32_SOCKETS)
Packit c32a2d
static int writestring (int fd, mpg123_string *string)
Packit c32a2d
{
Packit c32a2d
	size_t result, bytes;
Packit c32a2d
	char *ptr = string->p;
Packit c32a2d
	bytes = string->fill ? string->fill-1 : 0;
Packit c32a2d
Packit c32a2d
	while(bytes)
Packit c32a2d
	{
Packit c32a2d
		result = write(fd, ptr, bytes);
Packit c32a2d
		if(result < 0 && errno != EINTR)
Packit c32a2d
		{
Packit c32a2d
			perror ("writing http string");
Packit c32a2d
			return FALSE;
Packit c32a2d
		}
Packit c32a2d
		else if(result == 0)
Packit c32a2d
		{
Packit c32a2d
			error("write: socket closed unexpectedly");
Packit c32a2d
			return FALSE;
Packit c32a2d
		}
Packit c32a2d
		ptr   += result;
Packit c32a2d
		bytes -= result;
Packit c32a2d
	}
Packit c32a2d
	return TRUE;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
static size_t readstring (mpg123_string *string, size_t maxlen, int fd)
Packit c32a2d
{
Packit c32a2d
	int err;
Packit c32a2d
	debug2("Attempting readstring on %d for %"SIZE_P" bytes", fd, (size_p)maxlen);
Packit c32a2d
	string->fill = 0;
Packit c32a2d
	while(maxlen == 0 || string->fill < maxlen)
Packit c32a2d
	{
Packit c32a2d
		if(string->size-string->fill < 1)
Packit c32a2d
		if(!mpg123_grow_string(string, string->fill+4096))
Packit c32a2d
		{
Packit c32a2d
			error("Cannot allocate memory for reading.");
Packit c32a2d
			string->fill = 0;
Packit c32a2d
			return 0;
Packit c32a2d
		}
Packit c32a2d
		err = read(fd,string->p+string->fill,1);
Packit c32a2d
		/* Whoa... reading one byte at a time... one could ensure the line break in another way, but more work. */
Packit c32a2d
		if( err == 1)
Packit c32a2d
		{
Packit c32a2d
			string->fill++;
Packit c32a2d
			if(string->p[string->fill-1] == '\n') break;
Packit c32a2d
		}
Packit c32a2d
		else if(errno != EINTR)
Packit c32a2d
		{
Packit c32a2d
			error("Error reading from socket or unexpected EOF.");
Packit c32a2d
			string->fill = 0;
Packit c32a2d
			/* bail out to prevent endless loop */
Packit c32a2d
			return 0;
Packit c32a2d
		}
Packit c32a2d
	}
Packit c32a2d
Packit c32a2d
	if(!mpg123_grow_string(string, string->fill+1))
Packit c32a2d
	{
Packit c32a2d
		string->fill=0;
Packit c32a2d
	}
Packit c32a2d
	else
Packit c32a2d
	{
Packit c32a2d
		string->p[string->fill] = 0;
Packit c32a2d
		string->fill++;
Packit c32a2d
	}
Packit c32a2d
	return string->fill;
Packit c32a2d
}
Packit c32a2d
#endif /* WANT_WIN32_SOCKETS */
Packit c32a2d
Packit c32a2d
void encode64 (char *source,char *destination)
Packit c32a2d
{
Packit c32a2d
  static char *Base64Digits =
Packit c32a2d
    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
Packit c32a2d
  int n = 0;
Packit c32a2d
  int ssiz=strlen(source);
Packit c32a2d
  int i;
Packit c32a2d
Packit c32a2d
  for (i = 0 ; i < ssiz ; i += 3) {
Packit c32a2d
    unsigned int buf;
Packit c32a2d
    buf = ((unsigned char *)source)[i] << 16;
Packit c32a2d
    if (i+1 < ssiz)
Packit c32a2d
      buf |= ((unsigned char *)source)[i+1] << 8;
Packit c32a2d
    if (i+2 < ssiz)
Packit c32a2d
      buf |= ((unsigned char *)source)[i+2];
Packit c32a2d
Packit c32a2d
    destination[n++] = Base64Digits[(buf >> 18) % 64];
Packit c32a2d
    destination[n++] = Base64Digits[(buf >> 12) % 64];
Packit c32a2d
    if (i+1 < ssiz)
Packit c32a2d
      destination[n++] = Base64Digits[(buf >> 6) % 64];
Packit c32a2d
    else
Packit c32a2d
      destination[n++] = '=';
Packit c32a2d
    if (i+2 < ssiz)
Packit c32a2d
      destination[n++] = Base64Digits[buf % 64];
Packit c32a2d
    else
Packit c32a2d
      destination[n++] = '=';
Packit c32a2d
  }
Packit c32a2d
  destination[n++] = 0;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
/* Look out for HTTP header field to parse, construct C string with the value.
Packit c32a2d
   Attention: Modifies argument, since it's so convenient... */
Packit c32a2d
char *get_header_val(const char *hname, mpg123_string *response)
Packit c32a2d
{
Packit c32a2d
	char *tmp = NULL;
Packit c32a2d
	size_t prelen = strlen(hname);
Packit c32a2d
	/* if header name found, next char is at least something, so just check for : */
Packit c32a2d
	if(!strncasecmp(hname, response->p, prelen) && (response->p[prelen] == ':'))
Packit c32a2d
	{
Packit c32a2d
		++prelen;
Packit c32a2d
		if((tmp = strchr(response->p, '\r')) != NULL ) tmp[0] = 0;
Packit c32a2d
		if((tmp = strchr(response->p, '\n')) != NULL ) tmp[0] = 0;
Packit c32a2d
		tmp = response->p+prelen;
Packit c32a2d
		/* I _know_ that there is a terminating zero, so this loop is safe. */
Packit c32a2d
		while((tmp[0] == ' ') || (tmp[0] == '\t'))
Packit c32a2d
		{
Packit c32a2d
			++tmp;
Packit c32a2d
		}
Packit c32a2d
	}
Packit c32a2d
	return tmp;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
/* Iterate over header field names and storage locations, to possibly get those values. */
Packit c32a2d
void get_header_string(mpg123_string *response, const char *fieldname, mpg123_string *store)
Packit c32a2d
{
Packit c32a2d
	char *tmp;
Packit c32a2d
	if((tmp = get_header_val(fieldname, response)))
Packit c32a2d
	{
Packit c32a2d
		if(mpg123_set_string(store, tmp)){ debug2("got %s %s", fieldname, store->p); return; }
Packit c32a2d
		else{ error2("unable to set %s to %s!", fieldname, tmp); }
Packit c32a2d
	}
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
/* shoutcsast meta data: 1=on, 0=off */
Packit c32a2d
Packit c32a2d
char *httpauth = NULL;
Packit c32a2d
Packit c32a2d
size_t accept_length(void)
Packit c32a2d
{
Packit c32a2d
	int i,j;
Packit c32a2d
	static size_t l = 0;
Packit c32a2d
	if(l) return l;
Packit c32a2d
	l += strlen("Accept: ");
Packit c32a2d
	for(i=0; mimes[i]    != NULL; ++i)
Packit c32a2d
	for(j=0; mimes[i][j] != NULL; ++j){ l += strlen(mimes[i][j]) + strlen(", "); }
Packit c32a2d
	l += strlen("*/*\r\n");
Packit c32a2d
	debug1("initial computation of accept header length: %lu", (unsigned long)l);
Packit c32a2d
	return l;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
/* Returns TRUE or FALSE for success. */
Packit c32a2d
int proxy_init(struct httpdata *hd)
Packit c32a2d
{
Packit c32a2d
	int ret = TRUE;
Packit c32a2d
	/* If we don't have explicit proxy given, probe the environment. */
Packit c32a2d
	if (!param.proxyurl)
Packit c32a2d
		if (!(param.proxyurl = getenv("MP3_HTTP_PROXY")))
Packit c32a2d
			if (!(param.proxyurl = getenv("http_proxy")))
Packit c32a2d
				param.proxyurl = getenv("HTTP_PROXY");
Packit c32a2d
	/* Now continue if we have something. */
Packit c32a2d
	if (param.proxyurl && param.proxyurl[0] && strcmp(param.proxyurl, "none"))
Packit c32a2d
	{
Packit c32a2d
		mpg123_string proxyurl;
Packit c32a2d
		mpg123_init_string(&proxyurl);
Packit c32a2d
		if(   !mpg123_set_string(&proxyurl, param.proxyurl)
Packit c32a2d
		   || !split_url(&proxyurl, NULL, &hd->proxyhost, &hd->proxyport, NULL))
Packit c32a2d
		{
Packit c32a2d
			error("splitting proxy URL");
Packit c32a2d
			ret = FALSE;
Packit c32a2d
		}
Packit c32a2d
		else if(param.verbose > 1) fprintf(stderr, "Note: Using proxy %s\n", hd->proxyhost.p);
Packit c32a2d
#if 0 /* not yet there */
Packit c32a2d
		if(!try_host_lookup(proxyhost))
Packit c32a2d
		{
Packit c32a2d
			error("Unknown proxy host \"%s\".\n", proxyhost.p);
Packit c32a2d
			ret = FALSE;
Packit c32a2d
		}
Packit c32a2d
#endif
Packit c32a2d
		mpg123_free_string(&proxyurl);
Packit c32a2d
		if(ret) hd->proxystate = PROXY_HOST; /* We got hostname and port settled. */
Packit c32a2d
		else hd->proxystate = PROXY_NONE;
Packit c32a2d
	}
Packit c32a2d
	else hd->proxystate = PROXY_NONE;
Packit c32a2d
Packit c32a2d
	return ret;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
static int append_accept(mpg123_string *s)
Packit c32a2d
{
Packit c32a2d
	size_t i,j;
Packit c32a2d
	if(!mpg123_add_string(s, "Accept: ")) return FALSE;
Packit c32a2d
Packit c32a2d
	/* We prefer what we know. */
Packit c32a2d
	for(i=0; mimes[i]    != NULL; ++i)
Packit c32a2d
	for(j=0; mimes[i][j] != NULL; ++j)
Packit c32a2d
	{
Packit c32a2d
		if(   !mpg123_add_string(s, mimes[i][j])
Packit c32a2d
			 || !mpg123_add_string(s, ", ") )
Packit c32a2d
		return FALSE;
Packit c32a2d
	}
Packit c32a2d
	/* Well... in the end, we accept everything, trying to make sense with reality. */
Packit c32a2d
	if(!mpg123_add_string(s, "*/*\r\n")) return FALSE;
Packit c32a2d
Packit c32a2d
	return TRUE;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
Packit c32a2d
/*
Packit c32a2d
	Converts spaces to "%20" ... actually, I have to ask myself why.
Packit c32a2d
	What about converting them to "+" instead? Would make things a lot easier.
Packit c32a2d
	Or, on the other hand... what about avoiding HTML encoding at all?
Packit c32a2d
*/
Packit c32a2d
int translate_url(const char *url, mpg123_string *purl)
Packit c32a2d
{
Packit c32a2d
	const char *sptr;
Packit c32a2d
	/* The length of purl is upper bound by 3*strlen(url) + 1 if
Packit c32a2d
	 * everything in it is a space (%20) - or any encoded character */
Packit c32a2d
	if (strlen(url) >= SIZE_MAX/3)
Packit c32a2d
	{
Packit c32a2d
		error("URL too long. Skipping...");
Packit c32a2d
		return FALSE;
Packit c32a2d
	}
Packit c32a2d
	/* Prepare purl in one chunk, to minimize mallocs. */
Packit c32a2d
	if(!mpg123_resize_string(purl, strlen(url) + 31)) return FALSE;
Packit c32a2d
	/*
Packit c32a2d
	 * 2000-10-21:
Packit c32a2d
	 * We would like spaces to be automatically converted to %20's when
Packit c32a2d
	 * fetching via HTTP.
Packit c32a2d
	 * -- Martin Sjögren <md9ms@mdstud.chalmers.se>
Packit c32a2d
	 * Hm, why only spaces? Maybe one should do this http stuff more properly...
Packit c32a2d
	 */
Packit c32a2d
	if ((sptr = strchr(url, ' ')) == NULL)
Packit c32a2d
	mpg123_set_string(purl, url);
Packit c32a2d
	else
Packit c32a2d
	{ /* Note that sptr is set from the if to this else... */
Packit c32a2d
		const char *urlptr = url;
Packit c32a2d
		mpg123_set_string(purl, "");
Packit c32a2d
		do {
Packit c32a2d
			if(! ( mpg123_add_substring(purl, urlptr, 0, sptr-urlptr)
Packit c32a2d
			       && mpg123_add_string(purl, "%20") ) )
Packit c32a2d
			return FALSE;
Packit c32a2d
			urlptr = sptr + 1;
Packit c32a2d
		} while ((sptr = strchr (urlptr, ' ')) != NULL);
Packit c32a2d
		if(!mpg123_add_string(purl, urlptr)) return FALSE;
Packit c32a2d
	}
Packit c32a2d
	/* now see if a terminating / may be needed */
Packit c32a2d
	if(strchr(purl->p+(strncmp("http://", purl->p, 7) ? 0 : 7), '/') == NULL
Packit c32a2d
	    && !mpg123_add_string(purl, "/"))
Packit c32a2d
	return FALSE;
Packit c32a2d
Packit c32a2d
	return TRUE;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
int fill_request(mpg123_string *request, mpg123_string *host, mpg123_string *port, mpg123_string *httpauth1, int *try_without_port)
Packit c32a2d
{
Packit c32a2d
	char* ttemp;
Packit c32a2d
	int ret = TRUE;
Packit c32a2d
	const char *icy = param.talk_icy ? icy_yes : icy_no;
Packit c32a2d
Packit c32a2d
	/* hm, my test redirection had troubles with line break before HTTP/1.0 */
Packit c32a2d
	if((ttemp = strchr(request->p,'\r')) != NULL){ *ttemp = 0; request->fill = ttemp-request->p+1; }
Packit c32a2d
Packit c32a2d
	if((ttemp = strchr(request->p,'\n')) != NULL){ *ttemp = 0; request->fill = ttemp-request->p+1; }
Packit c32a2d
Packit c32a2d
	/* Fill out the request further... */
Packit c32a2d
	if(   !mpg123_add_string(request, " HTTP/1.0\r\nUser-Agent: ")
Packit c32a2d
		 || !mpg123_add_string(request, PACKAGE_NAME)
Packit c32a2d
		 || !mpg123_add_string(request, "/")
Packit c32a2d
		 || !mpg123_add_string(request, PACKAGE_VERSION)
Packit c32a2d
		 || !mpg123_add_string(request, "\r\n") )
Packit c32a2d
	return FALSE;
Packit c32a2d
Packit c32a2d
	if(host->fill)
Packit c32a2d
	{ /* Give virtual hosting a chance... adding the "Host: ... " line. */
Packit c32a2d
		debug2("Host: %s:%s", host->p, port->p);
Packit c32a2d
		if(    mpg123_add_string(request, "Host: ")
Packit c32a2d
			  && mpg123_add_string(request, host->p)
Packit c32a2d
			  && ( *try_without_port || (
Packit c32a2d
			         mpg123_add_string(request, ":")
Packit c32a2d
			      && mpg123_add_string(request, port->p) ))
Packit c32a2d
			  && mpg123_add_string(request, "\r\n") )
Packit c32a2d
		{
Packit c32a2d
			if(*try_without_port) *try_without_port = 0;
Packit c32a2d
		}
Packit c32a2d
		else return FALSE;
Packit c32a2d
	}
Packit c32a2d
Packit c32a2d
	/* Acceptance, stream setup. */
Packit c32a2d
	if(   !append_accept(request)
Packit c32a2d
		 || !mpg123_add_string(request, CONN_HEAD)
Packit c32a2d
		 || !mpg123_add_string(request, icy) )
Packit c32a2d
	return FALSE;
Packit c32a2d
Packit c32a2d
	/* Authorization. */
Packit c32a2d
	if (httpauth1->fill || httpauth) {
Packit c32a2d
		char *buf;
Packit c32a2d
		if(!mpg123_add_string(request,"Authorization: Basic ")) return FALSE;
Packit c32a2d
		if(httpauth1->fill) {
Packit c32a2d
			if(httpauth1->fill > SIZE_MAX / 4) return FALSE;
Packit c32a2d
Packit c32a2d
			buf=(char *)malloc(httpauth1->fill * 4);
Packit c32a2d
			if(!buf)
Packit c32a2d
			{
Packit c32a2d
				error("malloc() failed for http auth, out of memory.");
Packit c32a2d
				return FALSE;
Packit c32a2d
			}
Packit c32a2d
			encode64(httpauth1->p,buf);
Packit c32a2d
		} else {
Packit c32a2d
			if(strlen(httpauth) > SIZE_MAX / 4 - 4 ) return FALSE;
Packit c32a2d
Packit c32a2d
			buf=(char *)malloc((strlen(httpauth) + 1) * 4);
Packit c32a2d
			if(!buf)
Packit c32a2d
			{
Packit c32a2d
				error("malloc() for http auth failed, out of memory.");
Packit c32a2d
				return FALSE;
Packit c32a2d
			}
Packit c32a2d
			encode64(httpauth,buf);
Packit c32a2d
		}
Packit c32a2d
Packit c32a2d
		if( !mpg123_add_string(request, buf) || !mpg123_add_string(request, "\r\n"))
Packit c32a2d
		ret = FALSE;
Packit c32a2d
Packit c32a2d
		free(buf); /* Watch out for leaking if you introduce returns before this line. */
Packit c32a2d
	}
Packit c32a2d
	if(ret) ret = mpg123_add_string(request, "\r\n");
Packit c32a2d
Packit c32a2d
	return ret;
Packit c32a2d
}
Packit c32a2d
#if !defined (WANT_WIN32_SOCKETS)
Packit c32a2d
static int resolve_redirect(mpg123_string *response, mpg123_string *request_url, mpg123_string *purl)
Packit c32a2d
{
Packit c32a2d
	debug1("request_url:%s", request_url->p);
Packit c32a2d
	/* initialized with full old url */
Packit c32a2d
	if(!mpg123_copy_string(request_url, purl)) return FALSE;
Packit c32a2d
Packit c32a2d
	/* We may strip it down to a prefix ot totally. */
Packit c32a2d
	if(strncasecmp(response->p, "Location: http://", 17))
Packit c32a2d
	{ /* OK, only partial strip, need prefix for relative path. */
Packit c32a2d
		char* ptmp = NULL;
Packit c32a2d
		/* though it's not RFC (?), accept relative URIs as wget does */
Packit c32a2d
		fprintf(stderr, "NOTE: no complete URL in redirect, constructing one\n");
Packit c32a2d
		/* not absolute uri, could still be server-absolute */
Packit c32a2d
		/* I prepend a part of the request... out of the request */
Packit c32a2d
		if(response->p[10] == '/')
Packit c32a2d
		{
Packit c32a2d
			/* only prepend http://server/ */
Packit c32a2d
			/* I null the first / after http:// */
Packit c32a2d
			ptmp = strchr(purl->p+7,'/');
Packit c32a2d
			if(ptmp != NULL){ purl->fill = ptmp-purl->p+1; purl->p[purl->fill-1] = 0; }
Packit c32a2d
		}
Packit c32a2d
		else
Packit c32a2d
		{
Packit c32a2d
			/* prepend http://server/path/ */
Packit c32a2d
			/* now we want the last / */
Packit c32a2d
			ptmp = strrchr(purl->p+7, '/');
Packit c32a2d
			if(ptmp != NULL){ purl->fill = ptmp-purl->p+2; purl->p[purl->fill-1] = 0; }
Packit c32a2d
		}
Packit c32a2d
	}
Packit c32a2d
	else purl->fill = 0;
Packit c32a2d
Packit c32a2d
	debug1("prefix=%s", purl->fill ? purl->p : "");
Packit c32a2d
	if(!mpg123_add_string(purl, response->p+10)) return FALSE;
Packit c32a2d
Packit c32a2d
	debug1("           purl: %s", purl->p);
Packit c32a2d
	debug1("old request_url: %s", request_url->p);
Packit c32a2d
Packit c32a2d
	return TRUE;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
int http_open(char* url, struct httpdata *hd)
Packit c32a2d
{
Packit c32a2d
	mpg123_string purl, host, port, path;
Packit c32a2d
	mpg123_string request, response, request_url;
Packit c32a2d
	mpg123_string httpauth1;
Packit c32a2d
	int sock = -1;
Packit c32a2d
	int oom  = 0;
Packit c32a2d
	int relocate, numrelocs = 0;
Packit c32a2d
	int got_location = FALSE;
Packit c32a2d
	/*
Packit c32a2d
		workaround for http://www.global24music.com/rautemusik/files/extreme/isdn.pls
Packit c32a2d
		this site's apache gives me a relocation to the same place when I give the port in Host request field
Packit c32a2d
		for the record: Apache/2.0.51 (Fedora)
Packit c32a2d
	*/
Packit c32a2d
	int try_without_port = 0;
Packit c32a2d
	mpg123_init_string(&purl);
Packit c32a2d
	mpg123_init_string(&host);
Packit c32a2d
	mpg123_init_string(&port);
Packit c32a2d
	mpg123_init_string(&path);
Packit c32a2d
	mpg123_init_string(&request);
Packit c32a2d
	mpg123_init_string(&response);
Packit c32a2d
	mpg123_init_string(&request_url);
Packit c32a2d
	mpg123_init_string(&httpauth1);
Packit c32a2d
Packit c32a2d
	/* Get initial info for proxy server. Once. */
Packit c32a2d
	if(hd->proxystate == PROXY_UNKNOWN && !proxy_init(hd)) goto exit;
Packit c32a2d
Packit c32a2d
	if(!translate_url(url, &purl)){ oom=1; goto exit; }
Packit c32a2d
Packit c32a2d
	/* Don't confuse the different auth strings... */
Packit c32a2d
	if(!split_url(&purl, &httpauth1, NULL, NULL, NULL) ){ oom=1; goto exit; }
Packit c32a2d
Packit c32a2d
	/* "GET http://"		11
Packit c32a2d
	 * " HTTP/1.0\r\nUser-Agent: <PACKAGE_NAME>/<PACKAGE_VERSION>\r\n"
Packit c32a2d
	 * 				26 + PACKAGE_NAME + PACKAGE_VERSION
Packit c32a2d
	 * accept header            + accept_length()
Packit c32a2d
	 * "Authorization: Basic \r\n"	23
Packit c32a2d
	 * "\r\n"			 2
Packit c32a2d
	 * ... plus the other predefined header lines
Packit c32a2d
	 */
Packit c32a2d
	/* Just use this estimate as first guess to reduce malloc calls in string library. */
Packit c32a2d
	{
Packit c32a2d
		size_t length_estimate = 62 + strlen(PACKAGE_NAME) + strlen(PACKAGE_VERSION) 
Packit c32a2d
		                       + accept_length() + strlen(CONN_HEAD) + strlen(icy_yes) + purl.fill;
Packit c32a2d
		if(    !mpg123_grow_string(&request, length_estimate)
Packit c32a2d
		    || !mpg123_grow_string(&response,4096) )
Packit c32a2d
		{
Packit c32a2d
			oom=1; goto exit;
Packit c32a2d
		}
Packit c32a2d
	}
Packit c32a2d
Packit c32a2d
	do
Packit c32a2d
	{
Packit c32a2d
		/* Storing the request url, with http:// prepended if needed. */
Packit c32a2d
		/* used to be url here... seemed wrong to me (when loop advanced...) */
Packit c32a2d
		if(strncasecmp(purl.p, "http://", 7) != 0) mpg123_set_string(&request_url, "http://");
Packit c32a2d
		else mpg123_set_string(&request_url, "");
Packit c32a2d
Packit c32a2d
		mpg123_chomp_string(&purl);
Packit c32a2d
		mpg123_add_string(&request_url, purl.p);
Packit c32a2d
Packit c32a2d
		/* Always store the host and port from the URL for correct host header
Packit c32a2d
		   in the request. Proxy server is used for connection, but never in the
Packit c32a2d
		   host header! */
Packit c32a2d
		if(!split_url(&purl, NULL, &host, &port, &path)){ oom=1; goto exit; }
Packit c32a2d
		if (hd->proxystate >= PROXY_HOST)
Packit c32a2d
		{
Packit c32a2d
			/* We will connect to proxy, full URL goes into the request. */
Packit c32a2d
			if(    !mpg123_set_string(&request, "GET ")
Packit c32a2d
			    || !mpg123_add_string(&request, request_url.p) )
Packit c32a2d
			{
Packit c32a2d
				oom=1; goto exit;
Packit c32a2d
			}
Packit c32a2d
		}
Packit c32a2d
		else
Packit c32a2d
		{
Packit c32a2d
			/* We will connect to the host from the URL and only the path goes into the request. */
Packit c32a2d
			if(    !mpg123_set_string(&request, "GET ")
Packit c32a2d
			    || !mpg123_add_string(&request, path.p) )
Packit c32a2d
			{
Packit c32a2d
				oom=1; goto exit;
Packit c32a2d
			}
Packit c32a2d
		}
Packit c32a2d
Packit c32a2d
		if(!fill_request(&request, &host, &port, &httpauth1, &try_without_port)){ oom=1; goto exit; }
Packit c32a2d
Packit c32a2d
		httpauth1.fill = 0; /* We use the auth data from the URL only once. */
Packit c32a2d
		if (hd->proxystate >= PROXY_HOST)
Packit c32a2d
		{
Packit c32a2d
			/* Only the host:port used for actual connection is replaced by
Packit c32a2d
			   proxy. */
Packit c32a2d
			if(    !mpg123_copy_string(&hd->proxyhost, &host)
Packit c32a2d
			    || !mpg123_copy_string(&hd->proxyport, &port) )
Packit c32a2d
			{
Packit c32a2d
				oom=1; goto exit;
Packit c32a2d
			}
Packit c32a2d
		}
Packit c32a2d
		debug2("attempting to open_connection to %s:%s", host.p, port.p);
Packit c32a2d
		sock = open_connection(&host, &port);
Packit c32a2d
		if(sock < 0)
Packit c32a2d
		{
Packit c32a2d
			error1("Unable to establish connection to %s", host.fill ? host.p : "");
Packit c32a2d
			goto exit;
Packit c32a2d
		}
Packit c32a2d
#define http_failure close(sock); sock=-1; goto exit;
Packit c32a2d
		
Packit c32a2d
		if(param.verbose > 2) fprintf(stderr, "HTTP request:\n%s\n",request.p);
Packit c32a2d
		if(!writestring(sock, &request)){ http_failure; }
Packit c32a2d
		relocate = FALSE;
Packit c32a2d
		/* Arbitrary length limit here... */
Packit c32a2d
#define safe_readstring \
Packit c32a2d
		readstring(&response, SIZE_MAX/16, sock); \
Packit c32a2d
		if(response.fill > SIZE_MAX/16) /* > because of appended zero. */ \
Packit c32a2d
		{ \
Packit c32a2d
			error("HTTP response line exceeds max. length"); \
Packit c32a2d
			http_failure; \
Packit c32a2d
		} \
Packit c32a2d
		else if(response.fill == 0) \
Packit c32a2d
		{ \
Packit c32a2d
			error("readstring failed"); \
Packit c32a2d
			http_failure; \
Packit c32a2d
		} \
Packit c32a2d
		if(param.verbose > 2) fprintf(stderr, "HTTP in: %s", response.p);
Packit c32a2d
		safe_readstring;
Packit c32a2d
Packit c32a2d
		{
Packit c32a2d
			char *sptr;
Packit c32a2d
			if((sptr = strchr(response.p, ' ')))
Packit c32a2d
			{
Packit c32a2d
				if(response.fill > sptr-response.p+2)
Packit c32a2d
				switch (sptr[1])
Packit c32a2d
				{
Packit c32a2d
					case '3':
Packit c32a2d
						relocate = TRUE;
Packit c32a2d
					case '2':
Packit c32a2d
						break;
Packit c32a2d
					default:
Packit c32a2d
						fprintf (stderr, "HTTP request failed: %s", sptr+1); /* '\n' is included */
Packit c32a2d
						http_failure;
Packit c32a2d
				}
Packit c32a2d
				else{ error("Too short response,"); http_failure; }
Packit c32a2d
			}
Packit c32a2d
		}
Packit c32a2d
Packit c32a2d
		/* If we are relocated, we need to look out for a Location header. */
Packit c32a2d
		got_location = FALSE;
Packit c32a2d
Packit c32a2d
		do
Packit c32a2d
		{
Packit c32a2d
			safe_readstring; /* Think about that: Should we really error out when we get nothing? Could be that the server forgot the trailing empty line... */
Packit c32a2d
			if (!strncasecmp(response.p, "Location: ", 10))
Packit c32a2d
			{ /* It is a redirection! */
Packit c32a2d
				if(!resolve_redirect(&response, &request_url, &purl)){ oom=1, http_failure; }
Packit c32a2d
Packit c32a2d
				if(!strcmp(purl.p, request_url.p))
Packit c32a2d
				{
Packit c32a2d
					warning("relocated to very same place! trying request again without host port");
Packit c32a2d
					try_without_port = 1;
Packit c32a2d
				}
Packit c32a2d
				got_location = TRUE;
Packit c32a2d
			}
Packit c32a2d
			else
Packit c32a2d
			{ /* We got a header line (or the closing empty line). */
Packit c32a2d
				char *tmp;
Packit c32a2d
				debug1("searching for header values... %s", response.p);
Packit c32a2d
				/* Not sure if I want to bail out on error here. */
Packit c32a2d
				/* Also: What text encoding are these strings in? Doesn't need to be plain ASCII... */
Packit c32a2d
				get_header_string(&response, "content-type", &hd->content_type);
Packit c32a2d
				get_header_string(&response, "icy-name",     &hd->icy_name);
Packit c32a2d
				get_header_string(&response, "icy-url",      &hd->icy_url);
Packit c32a2d
Packit c32a2d
				/* watch out for icy-metaint */
Packit c32a2d
				if((tmp = get_header_val("icy-metaint", &response)))
Packit c32a2d
				{
Packit c32a2d
					hd->icy_interval = (off_t) atol(tmp); /* atoll ? */
Packit c32a2d
					debug1("got icy-metaint %li", (long int)hd->icy_interval);
Packit c32a2d
				}
Packit c32a2d
			}
Packit c32a2d
		} while(response.p[0] != '\r' && response.p[0] != '\n');
Packit c32a2d
		if(relocate)
Packit c32a2d
		{
Packit c32a2d
			close(sock);
Packit c32a2d
			sock = -1;
Packit c32a2d
			/* Forget content type, might just relate to a displayed error page,
Packit c32a2d
			   not the resource being redirected to. */
Packit c32a2d
			mpg123_free_string(&hd->content_type);
Packit c32a2d
			mpg123_init_string(&hd->content_type);
Packit c32a2d
		}
Packit c32a2d
	} while(relocate && got_location && purl.fill && numrelocs++ < HTTP_MAX_RELOCATIONS);
Packit c32a2d
	if(relocate)
Packit c32a2d
	{
Packit c32a2d
		if(!got_location)
Packit c32a2d
		error("Server meant to redirect but failed to provide a location!");
Packit c32a2d
		else
Packit c32a2d
		error1("Too many HTTP relocations (%i).", numrelocs);
Packit c32a2d
Packit c32a2d
		http_failure;
Packit c32a2d
	}
Packit c32a2d
Packit c32a2d
exit: /* The end as well as the exception handling point... */
Packit c32a2d
	if(oom) error("Apparently, I ran out of memory or had some bad input data...");
Packit c32a2d
Packit c32a2d
	mpg123_free_string(&purl);
Packit c32a2d
	mpg123_free_string(&host);
Packit c32a2d
	mpg123_free_string(&port);
Packit c32a2d
	mpg123_free_string(&path);
Packit c32a2d
	mpg123_free_string(&request);
Packit c32a2d
	mpg123_free_string(&response);
Packit c32a2d
	mpg123_free_string(&request_url);
Packit c32a2d
	mpg123_free_string(&httpauth1);
Packit c32a2d
	return sock;
Packit c32a2d
}
Packit c32a2d
#endif /*WANT_WIN32_SOCKETS*/
Packit c32a2d
Packit c32a2d
#else /* NETWORK */
Packit c32a2d
Packit c32a2d
/* stub */
Packit c32a2d
int http_open (char* url, struct httpdata *hd)
Packit c32a2d
{
Packit c32a2d
	if(!param.quiet)
Packit c32a2d
		error("HTTP support not built in.");
Packit c32a2d
	return -1;
Packit c32a2d
}
Packit c32a2d
#endif
Packit c32a2d
Packit c32a2d
/* EOF */
Packit c32a2d