Пример #1
0
static int win32_net_resolve_redirect(mpg123_string *response, mpg123_string *request_url, mpg123_string *purl)
{
	debug1("request_url:%s", request_url->p);
	/* initialized with full old url */
	if(!mpg123_copy_string(request_url, purl)) return FALSE;

	/* We may strip it down to a prefix ot totally. */
	if(strncasecmp(response->p, "Location: http://", 17))
	{ /* OK, only partial strip, need prefix for relative path. */
		char* ptmp = NULL;
		/* though it's not RFC (?), accept relative URIs as wget does */
		fprintf(stderr, "NOTE: no complete URL in redirect, constructing one\n");
		/* not absolute uri, could still be server-absolute */
		/* I prepend a part of the request... out of the request */
		if(response->p[10] == '/')
		{
			/* only prepend http://server/ */
			/* I null the first / after http:// */
			ptmp = strchr(purl->p+7,'/');
			if(ptmp != NULL){ purl->fill = ptmp-purl->p+1; purl->p[purl->fill-1] = 0; }
		}
		else
		{
			/* prepend http://server/path/ */
			/* now we want the last / */
			ptmp = strrchr(purl->p+7, '/');
			if(ptmp != NULL){ purl->fill = ptmp-purl->p+2; purl->p[purl->fill-1] = 0; }
		}
	}
	else purl->fill = 0;

	debug1("prefix=%s", purl->fill ? purl->p : "");
	if(!mpg123_add_string(purl, response->p+10)) return FALSE;

	debug1("           purl: %s", purl->p);
	debug1("old request_url: %s", request_url->p);

	return TRUE;
}
Пример #2
0
/* Print tags... limiting the UTF-8 to ASCII, if necessary. */
void print_id3_tag(mpg123_handle *mh, int long_id3, FILE *out)
{
	char genre_from_v1 = 0;
	enum tagcode ti;
	mpg123_string tag[FIELDS];
	size_t len[FIELDS];
	mpg123_id3v1 *v1;
	mpg123_id3v2 *v2;
	/* no memory allocated here, so return is safe */
	for(ti=0; ti<FIELDS; ++ti){ len[ti]=0; mpg123_init_string(&tag[ti]); }
	/* extract the data */
	mpg123_id3(mh, &v1, &v2);
	/* Only work if something there... */
	if(v1 == NULL && v2 == NULL) return;
	if(v2 != NULL) /* fill from ID3v2 data */
	{
		len[TITLE]   = transform(&tag[TITLE],   v2->title);
		len[ARTIST]  = transform(&tag[ARTIST],  v2->artist);
		len[ALBUM]   = transform(&tag[ALBUM],   v2->album);
		len[COMMENT] = transform(&tag[COMMENT], v2->comment);
		len[YEAR]    = transform(&tag[YEAR],    v2->year);
		len[GENRE]   = transform(&tag[GENRE],   v2->genre);
	}
	if(v1 != NULL) /* fill gaps with ID3v1 data */
	{
		/* I _could_ skip the recalculation of fill ... */
		id3_gap(&tag[TITLE],   30, v1->title,   &len[TITLE]);
		id3_gap(&tag[ARTIST],  30, v1->artist,  &len[ARTIST]);
		id3_gap(&tag[ALBUM],   30, v1->album,   &len[ALBUM]);
		id3_gap(&tag[COMMENT], 30, v1->comment, &len[COMMENT]);
		id3_gap(&tag[YEAR],    4,  v1->year,    &len[YEAR]);
		/*
			genre is special... v1->genre holds an index, id3v2 genre may contain indices in textual form and raw textual genres...
		*/
		if(!tag[GENRE].fill)
		{
			if(tag[GENRE].size >= 31 || mpg123_resize_string(&tag[GENRE],31))
			{
				if(v1->genre <= genre_count)
				{
					strncpy(tag[GENRE].p, genre_table[v1->genre], 30);
				}
				else
				{
					strncpy(tag[GENRE].p,"Unknown",30);
				}
				tag[GENRE].p[30] = 0;
				/* V1 was plain ASCII, so strlen is fine. */
				len[GENRE] = strlen(tag[GENRE].p);
				tag[GENRE].fill = len[GENRE] + 1;
				genre_from_v1 = 1;
			}
		}
	}

	if(tag[GENRE].fill && !genre_from_v1)
	{
		/*
			id3v2.3 says (id)(id)blabla and in case you want ot have (blabla) write ((blabla)
			also, there is
			(RX) Remix
			(CR) Cover
			id3v2.4 says
			"one or several of the ID3v1 types as numerical strings"
			or define your own (write strings), RX and CR 

			Now I am very sure that I'll encounter hellishly mixed up id3v2 frames, so try to parse both at once.
		*/
		mpg123_string tmp;
		mpg123_init_string(&tmp);
		debug1("interpreting genre: %s\n", tag[GENRE].p);
		if(mpg123_copy_string(&tag[GENRE], &tmp))
		{
			size_t num = 0;
			size_t nonum = 0;
			size_t i;
			enum { nothing, number, outtahere } state = nothing;
			tag[GENRE].fill = 0; /* going to be refilled */
			/* number\n -> id3v1 genre */
			/* (number) -> id3v1 genre */
			/* (( -> ( */
			for(i = 0; i < tmp.fill; ++i)
			{
				debug1("i=%lu", (unsigned long) i);
				switch(state)
				{
					case nothing:
						nonum = i;
						if(tmp.p[i] == '(')
						{
							num = i+1; /* number starting as next? */
							state = number;
							debug1("( before number at %lu?", (unsigned long) num);
						}
						/* you know an encoding where this doesn't work? */
						else if(tmp.p[i] >= '0' && tmp.p[i] <= '9')
						{
							num = i;
							state = number;
							debug1("direct number at %lu", (unsigned long) num);
						}
						else state = outtahere;
					break;
					case number:
						/* fake number alert: (( -> ( */
						if(tmp.p[i] == '(')
						{
							nonum = i;
							state = outtahere;
							debug("no, it was ((");
						}
						else if(tmp.p[i] == ')' || tmp.p[i] == '\n' || tmp.p[i] == 0)
						{
							if(i-num > 0)
							{
								/* we really have a number */
								int gid;
								char* genre = "Unknown";
								tmp.p[i] = 0;
								gid = atoi(tmp.p+num);

								/* get that genre */
								if(gid >= 0 && gid <= genre_count) genre = genre_table[gid];
								debug1("found genre: %s", genre);

								if(tag[GENRE].fill) mpg123_add_string(&tag[GENRE], ", ");
								mpg123_add_string(&tag[GENRE], genre);
								nonum = i+1; /* next possible stuff */
								state = nothing;
								debug1("had a number: %i", gid);
							}
							else
							{
								/* wasn't a number, nonum is set */
								state = outtahere;
								debug("no (num) thing...");
							}
						}
						else if(!(tmp.p[i] >= '0' && tmp.p[i] <= '9'))
						{
							/* no number at last... */
							state = outtahere;
							debug("nothing numeric here");
						}
						else
						{
							debug("still number...");
						}
					break;
					default: break;
				}
				if(state == outtahere) break;
			}
			if(nonum < tmp.fill-1)
			{
				if(tag[GENRE].fill) mpg123_add_string(&tag[GENRE], ", ");
				mpg123_add_string(&tag[GENRE], tmp.p+nonum);
			}
			/* Do not like that ... assumes plain ASCII ... */
			len[GENRE] = strlen(tag[GENRE].p);
		}
		mpg123_free_string(&tmp);
	}

	if(long_id3)
	{
		fprintf(out,"\n");
		/* print id3v2 */
		print_oneline(out, tag, TITLE,   TRUE);
		print_oneline(out, tag, ARTIST,  TRUE);
		print_oneline(out, tag, ALBUM,   TRUE);
		print_oneline(out, tag, YEAR,    TRUE);
		print_oneline(out, tag, GENRE,   TRUE);
		print_oneline(out, tag, COMMENT, TRUE);
		fprintf(out,"\n");
	}
	else
	{
		/* We are trying to be smart here and conserve some vertical space.
		   So we will skip tags not set, and try to show them in two parallel
		   columns if they are short, which is by far the most common case. */
		int linelimit;
		/* Overhead is Name + ": " and also plus "  " for right column. */
		const int overhead[2] = { namelen[0]+2, namelen[1]+4 };
		int climit[2];

		/* Adapt formatting width to terminal if possible. */
		linelimit = term_width(fileno(out));
		if(linelimit < 0)
			linelimit=overhead[0]+30+overhead[1]+30; /* the old style, based on ID3v1 */
		if(linelimit > 200)
			linelimit = 200; /* Not too wide. Also for format string safety. */
		/* Divide the space between the two columns, not wasting any. */
		climit[1] = linelimit/2-overhead[0];
		climit[0] = linelimit-linelimit/2-overhead[1];
		debug3("linelimits: %i  < %i | %i >", linelimit, climit[0], climit[1]);

		if(climit[0] <= 0 || climit[1] <= 0)
		{
			/* Ensure disabled column printing, no play with signedness in comparisons. */
			climit[0] = 0;
			climit[1] = 0;
		}
		fprintf(out,"\n"); /* Still use one separator line. Too ugly without. */
		print_pair(out, climit, tag, len, TITLE,   ARTIST);
		print_pair(out, climit, tag, len, COMMENT, ALBUM );
		print_pair(out, climit, tag, len, YEAR,    GENRE );
	}
	for(ti=0; ti<FIELDS; ++ti) mpg123_free_string(&tag[ti]);

	if(v2 != NULL && APPFLAG(MPG123APP_LYRICS))
	{
		/* find and print texts that have USLT IDs */
		size_t i;
		for(i=0; i<v2->texts; ++i)
		{
			if(!memcmp(v2->text[i].id, "USLT", 4))
			{
				/* split into lines, ensure usage of proper local line end */
				size_t a=0;
				size_t b=0;
				char lang[4]; /* just a 3-letter ASCII code, no fancy encoding */
				mpg123_string innline;
				mpg123_string outline;
				mpg123_string *uslt = &v2->text[i].text;

				memcpy(lang, &v2->text[i].lang, 3);
				lang[3] = 0;
				printf("Lyrics begin, language: %s; %s\n\n", lang,  v2->text[i].description.fill ? v2->text[i].description.p : "");

				mpg123_init_string(&innline);
				mpg123_init_string(&outline);
				while(a < uslt->fill)
				{
					b = a;
					while(b < uslt->fill && uslt->p[b] != '\n' && uslt->p[b] != '\r') ++b;
					/* Either found end of a line or end of the string (null byte) */
					mpg123_set_substring(&innline, uslt->p, a, b-a);
					transform(&outline, &innline);
					printf(" %s\n", outline.p);

					if(uslt->p[b] == uslt->fill) break; /* nothing more */

					/* Swallow CRLF */
					if(uslt->fill-b > 1 && uslt->p[b] == '\r' && uslt->p[b+1] == '\n') ++b;

					a = b + 1; /* next line beginning */
				}
				mpg123_free_string(&innline);
				mpg123_free_string(&outline);

				printf("\nLyrics end.\n");
			}
		}
	}
}
Пример #3
0
/* print tags... limiting the UTF-8 to ASCII */
void print_id3_tag(mpg123_handle *mh, int long_id3, FILE *out)
{
    char genre_from_v1 = 0;
    enum { TITLE=0, ARTIST, ALBUM, COMMENT, YEAR, GENRE, FIELDS } ti;
    mpg123_string tag[FIELDS];
    size_t len[FIELDS];
    mpg123_id3v1 *v1;
    mpg123_id3v2 *v2;
    /* no memory allocated here, so return is safe */
    for(ti=0; ti<FIELDS; ++ti) mpg123_init_string(&tag[ti]);
    /* extract the data */
    mpg123_id3(mh, &v1, &v2);
    /* Only work if something there... */
    if(v1 == NULL && v2 == NULL) return;
    if(v2 != NULL) /* fill from ID3v2 data */
    {
        len[TITLE]   = transform(&tag[TITLE],   v2->title);
        len[ARTIST]  = transform(&tag[ARTIST],  v2->artist);
        len[ALBUM]   = transform(&tag[ALBUM],   v2->album);
        len[COMMENT] = transform(&tag[COMMENT], v2->comment);
        len[YEAR]    = transform(&tag[YEAR],    v2->year);
        len[GENRE]   = transform(&tag[GENRE],   v2->genre);
    }
    if(v1 != NULL) /* fill gaps with ID3v1 data */
    {
        /* I _could_ skip the recalculation of fill ... */
        id3_gap(&tag[TITLE],   30, v1->title,   &len[TITLE]);
        id3_gap(&tag[ARTIST],  30, v1->artist,  &len[ARTIST]);
        id3_gap(&tag[ALBUM],   30, v1->album,   &len[ALBUM]);
        id3_gap(&tag[COMMENT], 30, v1->comment, &len[COMMENT]);
        id3_gap(&tag[YEAR],    4,  v1->year,    &len[YEAR]);
        /*
        	genre is special... v1->genre holds an index, id3v2 genre may contain indices in textual form and raw textual genres...
        */
        if(!tag[GENRE].fill)
        {
            if(tag[GENRE].size >= 31 || mpg123_resize_string(&tag[GENRE],31))
            {
                if(v1->genre <= genre_count)
                {
                    strncpy(tag[GENRE].p, genre_table[v1->genre], 30);
                }
                else
                {
                    strncpy(tag[GENRE].p,"Unknown",30);
                }
                tag[GENRE].p[30] = 0;
                tag[GENRE].fill = strlen(tag[GENRE].p) + 1;
                genre_from_v1 = 1;
            }
        }
    }

    if(tag[GENRE].fill && !genre_from_v1)
    {
        /*
        	id3v2.3 says (id)(id)blabla and in case you want ot have (blabla) write ((blabla)
        	also, there is
        	(RX) Remix
        	(CR) Cover
        	id3v2.4 says
        	"one or several of the ID3v1 types as numerical strings"
        	or define your own (write strings), RX and CR

        	Now I am very sure that I'll encounter hellishly mixed up id3v2 frames, so try to parse both at once.
        */
        mpg123_string tmp;
        mpg123_init_string(&tmp);
        debug1("interpreting genre: %s\n", tag[GENRE].p);
        if(mpg123_copy_string(&tag[GENRE], &tmp))
        {
            size_t num = 0;
            size_t nonum = 0;
            size_t i;
            enum { nothing, number, outtahere } state = nothing;
            tag[GENRE].fill = 0; /* going to be refilled */
            /* number\n -> id3v1 genre */
            /* (number) -> id3v1 genre */
            /* (( -> ( */
            for(i = 0; i < tmp.fill; ++i)
            {
                debug1("i=%lu", (unsigned long) i);
                switch(state)
                {
                case nothing:
                    nonum = i;
                    if(tmp.p[i] == '(')
                    {
                        num = i+1; /* number starting as next? */
                        state = number;
                        debug1("( before number at %lu?", (unsigned long) num);
                    }
                    /* you know an encoding where this doesn't work? */
                    else if(tmp.p[i] >= '0' && tmp.p[i] <= '9')
                    {
                        num = i;
                        state = number;
                        debug1("direct number at %lu", (unsigned long) num);
                    }
                    else state = outtahere;
                    break;
                case number:
                    /* fake number alert: (( -> ( */
                    if(tmp.p[i] == '(')
                    {
                        nonum = i;
                        state = outtahere;
                        debug("no, it was ((");
                    }
                    else if(tmp.p[i] == ')' || tmp.p[i] == '\n' || tmp.p[i] == 0)
                    {
                        if(i-num > 0)
                        {
                            /* we really have a number */
                            int gid;
                            char* genre = "Unknown";
                            tmp.p[i] = 0;
                            gid = atoi(tmp.p+num);

                            /* get that genre */
                            if(gid >= 0 && gid <= genre_count) genre = genre_table[gid];
                            debug1("found genre: %s", genre);

                            if(tag[GENRE].fill) mpg123_add_string(&tag[GENRE], ", ");
                            mpg123_add_string(&tag[GENRE], genre);
                            nonum = i+1; /* next possible stuff */
                            state = nothing;
                            debug1("had a number: %i", gid);
                        }
                        else
                        {
                            /* wasn't a number, nonum is set */
                            state = outtahere;
                            debug("no (num) thing...");
                        }
                    }
                    else if(!(tmp.p[i] >= '0' && tmp.p[i] <= '9'))
                    {
                        /* no number at last... */
                        state = outtahere;
                        debug("nothing numeric here");
                    }
                    else
                    {
                        debug("still number...");
                    }
                    break;
                default:
                    break;
                }
                if(state == outtahere) break;
            }
            if(nonum < tmp.fill-1)
            {
                if(tag[GENRE].fill) mpg123_add_string(&tag[GENRE], ", ");
                mpg123_add_string(&tag[GENRE], tmp.p+nonum);
            }
            /* Do not like that ... assumes plain ASCII ... */
            len[GENRE] = strlen(tag[GENRE].p);
        }
        mpg123_free_string(&tmp);
    }

    if(long_id3)
    {
        fprintf(out,"\n");
        /* print id3v2 */
        /* dammed, I use pointers as bool again! It's so convenient... */
        fprintf(out,"\tTitle:   %s\n", tag[TITLE].fill ? tag[TITLE].p : "");
        fprintf(out,"\tArtist:  %s\n", tag[ARTIST].fill ? tag[ARTIST].p : "");
        fprintf(out,"\tAlbum:   %s\n", tag[ALBUM].fill ? tag[ALBUM].p : "");
        fprintf(out,"\tYear:    %s\n", tag[YEAR].fill ? tag[YEAR].p : "");
        fprintf(out,"\tGenre:   %s\n", tag[GENRE].fill ? tag[GENRE].p : "");
        fprintf(out,"\tComment: %s\n", tag[COMMENT].fill ? tag[COMMENT].p : "");
        fprintf(out,"\n");
    }
    else
    {
        char space[31];
        size_t i;
        space[30] = 0;
        for(i=0; i<30; ++i) space[i] = ' ';

        /* We are trying to be smart here and conserve vertical space.
           So we will skip tags not set, and try to show them in two parallel columns if they are short, which is by far the	most common case. */
        /* one _could_ circumvent the strlen calls... */
        if(tag[TITLE].fill && tag[ARTIST].fill && len[TITLE] <= 30 && len[TITLE] <= 30)
        {
            fprintf(out,"Title:   %s%s  Artist: %s\n",tag[TITLE].p, space+len[TITLE], tag[ARTIST].p);
        }
        else
        {
            if(tag[TITLE].fill) fprintf(out,"Title:   %s\n", tag[TITLE].p);
            if(tag[ARTIST].fill) fprintf(out,"Artist:  %s\n", tag[ARTIST].p);
        }
        if(tag[COMMENT].fill && tag[ALBUM].fill && len[COMMENT] <= 30 && len[ALBUM] <= 30)
        {
            fprintf(out,"Comment: %s%s  Album:  %s\n",tag[COMMENT].p, space+len[COMMENT], tag[ALBUM].p);
        }
        else
        {
            if(tag[COMMENT].fill)
                fprintf(out,"Comment: %s\n", tag[COMMENT].p);
            if(tag[ALBUM].fill)
                fprintf(out,"Album:   %s\n", tag[ALBUM].p);
        }
        if(tag[YEAR].fill && tag[GENRE].fill && len[YEAR] <= 30 && len[GENRE] <= 30)
        {
            fprintf(out,"Year:    %s%s  Genre:  %s\n",tag[YEAR].p, space+len[YEAR], tag[GENRE].p);
        }
        else
        {
            if(tag[YEAR].fill)
                fprintf(out,"Year:    %s\n", tag[YEAR].p);
            if(tag[GENRE].fill)
                fprintf(out,"Genre:   %s\n", tag[GENRE].p);
        }
    }
    for(ti=0; ti<FIELDS; ++ti) mpg123_free_string(&tag[ti]);
}
Пример #4
0
int win32_net_http_open(char* url, struct httpdata *hd)
{
	mpg123_string purl, host, port, path;
	mpg123_string request, response, request_url;
	mpg123_string httpauth1;
	ws.local_socket = SOCKET_ERROR;
	int oom  = 0;
	int relocate, numrelocs = 0;
	int got_location = FALSE;
	/*
		workaround for http://www.global24music.com/rautemusik/files/extreme/isdn.pls
		this site's apache gives me a relocation to the same place when I give the port in Host request field
		for the record: Apache/2.0.51 (Fedora)
	*/
	int try_without_port = 0;
	mpg123_init_string(&purl);
	mpg123_init_string(&host);
	mpg123_init_string(&port);
	mpg123_init_string(&path);
	mpg123_init_string(&request);
	mpg123_init_string(&response);
	mpg123_init_string(&request_url);
	mpg123_init_string(&httpauth1);

	/* Get initial info for proxy server. Once. */
	if(hd->proxystate == PROXY_UNKNOWN && !proxy_init(hd)) goto exit;

	if(!translate_url(url, &purl)){ oom=1; goto exit; }

	/* Don't confuse the different auth strings... */
	if(!split_url(&purl, &httpauth1, NULL, NULL, NULL) ){ oom=1; goto exit; }

	/* "GET http://"		11
	 * " HTTP/1.0\r\nUser-Agent: <PACKAGE_NAME>/<PACKAGE_VERSION>\r\n"
	 * 				26 + PACKAGE_NAME + PACKAGE_VERSION
	 * accept header            + accept_length()
	 * "Authorization: Basic \r\n"	23
	 * "\r\n"			 2
	 * ... plus the other predefined header lines
	 */
	/* Just use this estimate as first guess to reduce malloc calls in string library. */
	{
		size_t length_estimate = 62 + strlen(PACKAGE_NAME) + strlen(PACKAGE_VERSION) 
		                       + accept_length() + strlen(CONN_HEAD) + strlen(icy_yes) + purl.fill;
		if(    !mpg123_grow_string(&request, length_estimate)
		    || !mpg123_grow_string(&response,4096) )
		{
			oom=1; goto exit;
		}
	}

	do
	{
		/* Storing the request url, with http:// prepended if needed. */
		/* used to be url here... seemed wrong to me (when loop advanced...) */
		if(strncasecmp(purl.p, "http://", 7) != 0) mpg123_set_string(&request_url, "http://");
		else mpg123_set_string(&request_url, "");

		mpg123_add_string(&request_url, purl.p);

		if (hd->proxystate >= PROXY_HOST)
		{
			/* We will connect to proxy, full URL goes into the request. */
			if(    !mpg123_copy_string(&hd->proxyhost, &host)
			    || !mpg123_copy_string(&hd->proxyport, &port)
			    || !mpg123_set_string(&request, "GET ")
			    || !mpg123_add_string(&request, request_url.p) )
			{
				oom=1; goto exit;
			}
		}
		else
		{
			/* We will connect to the host from the URL and only the path goes into the request. */
			if(!split_url(&purl, NULL, &host, &port, &path)){ oom=1; goto exit; }
			if(    !mpg123_set_string(&request, "GET ")
			    || !mpg123_add_string(&request, path.p) )
			{
				oom=1; goto exit;
			}
		}

		if(!fill_request(&request, &host, &port, &httpauth1, &try_without_port)){ oom=1; goto exit; }

		httpauth1.fill = 0; /* We use the auth data from the URL only once. */
		debug2("attempting to open_connection to %s:%s", host.p, port.p);
		win32_net_open_connection(&host, &port);
		if(ws.local_socket == SOCKET_ERROR)
		{
			error1("Unable to establish connection to %s", host.fill ? host.p : "");
			goto exit;
		}
		debug("win32_net_open_connection succeed");
#define http_failure win32_net_close(ws.local_socket); ws.local_socket=SOCKET_ERROR; goto exit;
		
		if(param.verbose > 2) fprintf(stderr, "HTTP request:\n%s\n",request.p);
		if(!win32_net_writestring(ws.local_socket, &request)){ http_failure; }
		debug("Skipping fdopen for WSA sockets");
		relocate = FALSE;
		/* Arbitrary length limit here... */
#define safe_readstring \
		win32_net_readstring(&response, SIZE_MAX/16, NULL); \
		if(response.fill > SIZE_MAX/16) /* > because of appended zero. */ \
		{ \
			error("HTTP response line exceeds max. length"); \
			http_failure; \
		} \
		else if(response.fill == 0) \
		{ \
			error("readstring failed"); \
			http_failure; \
		} \
		if(param.verbose > 2) fprintf(stderr, "HTTP in: %s", response.p);
		safe_readstring;

		{
			char *sptr;
			if((sptr = strchr(response.p, ' ')))
			{
				if(response.fill > sptr-response.p+2)
				switch (sptr[1])
				{
					case '3':
						relocate = TRUE;
					case '2':
						break;
					default:
						fprintf (stderr, "HTTP request failed: %s", sptr+1); /* '\n' is included */
						http_failure;
				}
				else{ error("Too short response,"); http_failure; }
			}
		}

		/* If we are relocated, we need to look out for a Location header. */
		got_location = FALSE;

		do
		{
			safe_readstring; /* Think about that: Should we really error out when we get nothing? Could be that the server forgot the trailing empty line... */
			if (!strncasecmp(response.p, "Location: ", 10))
			{ /* It is a redirection! */
				if(!win32_net_resolve_redirect(&response, &request_url, &purl)){ oom=1, http_failure; }

				if(!strcmp(purl.p, request_url.p))
				{
					warning("relocated to very same place! trying request again without host port");
					try_without_port = 1;
				}
				got_location = TRUE;
			}
			else
			{ /* We got a header line (or the closing empty line). */
				char *tmp;
				debug1("searching for header values... %s", response.p);
				/* Not sure if I want to bail out on error here. */
				/* Also: What text encoding are these strings in? Doesn't need to be plain ASCII... */
				get_header_string(&response, "content-type", &hd->content_type);
				get_header_string(&response, "icy-name",     &hd->icy_name);
				get_header_string(&response, "icy-url",      &hd->icy_url);

				/* watch out for icy-metaint */
				if((tmp = get_header_val("icy-metaint", &response)))
				{
					hd->icy_interval = (off_t) atol(tmp); /* atoll ? */
					debug1("got icy-metaint %li", (long int)hd->icy_interval);
				}
			}
		} while(response.p[0] != '\r' && response.p[0] != '\n');
	} while(relocate && got_location && purl.fill && numrelocs++ < HTTP_MAX_RELOCATIONS);
	if(relocate)
	{
		if(!got_location)
		error("Server meant to redirect but failed to provide a location!");
		else
		error1("Too many HTTP relocations (%i).", numrelocs);

		http_failure;
	}

exit: /* The end as well as the exception handling point... */
	if(oom) error("Apparently, I ran out of memory or had some bad input data...");

	mpg123_free_string(&purl);
	mpg123_free_string(&host);
	mpg123_free_string(&port);
	mpg123_free_string(&path);
	mpg123_free_string(&request);
	mpg123_free_string(&response);
	mpg123_free_string(&request_url);
	mpg123_free_string(&httpauth1);
	if (ws.local_socket == SOCKET_ERROR || oom)
	return -1;
	else
	return 1;
}