inputStream_http.c 20.4 KB
Newer Older
Warren Dukes's avatar
Warren Dukes committed
1
/* the Music Player Daemon (MPD)
2
 * Copyright (C) 2003-2007 by Warren Dukes (warren.dukes@gmail.com)
Warren Dukes's avatar
Warren Dukes committed
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
 * This project's homepage is: http://www.musicpd.org
 *
 * 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
 */

#include "inputStream_http.h"

#include "utils.h"
Warren Dukes's avatar
Warren Dukes committed
22
#include "log.h"
23
#include "conf.h"
Warren Dukes's avatar
Warren Dukes committed
24 25

#include <stdio.h>
Warren Dukes's avatar
Warren Dukes committed
26
#include <sys/time.h>
Warren Dukes's avatar
Warren Dukes committed
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <sys/param.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>

#define HTTP_CONN_STATE_CLOSED  0
#define HTTP_CONN_STATE_INIT    1
#define HTTP_CONN_STATE_HELLO   2
#define HTTP_CONN_STATE_OPEN    3
#define HTTP_CONN_STATE_REOPEN  4

45 46
#define HTTP_BUFFER_SIZE_DEFAULT        131072
#define HTTP_PREBUFFER_SIZE_DEFAULT	(HTTP_BUFFER_SIZE_DEFAULT >> 2)
Warren Dukes's avatar
Warren Dukes committed
47 48 49

#define HTTP_REDIRECT_MAX    10

50 51 52 53
static char *proxyHost;
static char *proxyPort;
static char *proxyUser;
static char *proxyPassword;
54 55
static int bufferSize = HTTP_BUFFER_SIZE_DEFAULT;
static int prebufferSize = HTTP_PREBUFFER_SIZE_DEFAULT;
56

Warren Dukes's avatar
Warren Dukes committed
57
typedef struct _InputStreemHTTPData {
Avuton Olrich's avatar
Avuton Olrich committed
58 59
	char *host;
	char *path;
60
	char *port;
Avuton Olrich's avatar
Avuton Olrich committed
61 62 63 64 65 66
	int sock;
	int connState;
	char *buffer;
	size_t buflen;
	int timesRedirected;
	int icyMetaint;
67
	int prebuffer;
Warren Dukes's avatar
Warren Dukes committed
68
	int icyOffset;
Avuton Olrich's avatar
Avuton Olrich committed
69 70
	char *proxyAuth;
	char *httpAuth;
Warren Dukes's avatar
Warren Dukes committed
71 72
} InputStreamHTTPData;

Avuton Olrich's avatar
Avuton Olrich committed
73 74 75 76
void inputStream_initHttp(void)
{
	ConfigParam *param = getConfigParam(CONF_HTTP_PROXY_HOST);
	char *test;
77

Avuton Olrich's avatar
Avuton Olrich committed
78
	if (param) {
79 80 81 82
		proxyHost = param->value;

		param = getConfigParam(CONF_HTTP_PROXY_PORT);

Avuton Olrich's avatar
Avuton Olrich committed
83
		if (!param) {
84
			FATAL("%s specified but not %s", CONF_HTTP_PROXY_HOST,
Avuton Olrich's avatar
Avuton Olrich committed
85
			      CONF_HTTP_PROXY_PORT);
86
		}
87
		proxyPort = param->value;
88

89 90
		param = getConfigParam(CONF_HTTP_PROXY_USER);

Avuton Olrich's avatar
Avuton Olrich committed
91
		if (param) {
92
			proxyUser = param->value;
Avuton Olrich's avatar
Avuton Olrich committed
93

94 95
			param = getConfigParam(CONF_HTTP_PROXY_PASSWORD);

Avuton Olrich's avatar
Avuton Olrich committed
96
			if (!param) {
97
				FATAL("%s specified but not %s\n",
Avuton Olrich's avatar
Avuton Olrich committed
98 99
				      CONF_HTTP_PROXY_USER,
				      CONF_HTTP_PROXY_PASSWORD);
100 101
			}

Avuton Olrich's avatar
Avuton Olrich committed
102
			proxyPassword = param->value;
103 104
		} else {
			param = getConfigParam(CONF_HTTP_PROXY_PASSWORD);
105

106
			if (param) {
107
				FATAL("%s specified but not %s\n",
108 109
				      CONF_HTTP_PROXY_PASSWORD, CONF_HTTP_PROXY_USER);
			}
110
		}
Avuton Olrich's avatar
Avuton Olrich committed
111
	} else if ((param = getConfigParam(CONF_HTTP_PROXY_PORT))) {
112
		FATAL("%s specified but not %s, line %i\n",
Avuton Olrich's avatar
Avuton Olrich committed
113 114
		      CONF_HTTP_PROXY_PORT, CONF_HTTP_PROXY_HOST, param->line);
	} else if ((param = getConfigParam(CONF_HTTP_PROXY_USER))) {
115
		FATAL("%s specified but not %s, line %i\n",
Avuton Olrich's avatar
Avuton Olrich committed
116 117
		      CONF_HTTP_PROXY_USER, CONF_HTTP_PROXY_HOST, param->line);
	} else if ((param = getConfigParam(CONF_HTTP_PROXY_PASSWORD))) {
118
		FATAL("%s specified but not %s, line %i\n",
Avuton Olrich's avatar
Avuton Olrich committed
119 120
		      CONF_HTTP_PROXY_PASSWORD, CONF_HTTP_PROXY_HOST,
		      param->line);
121
	}
122 123 124

	param = getConfigParam(CONF_HTTP_BUFFER_SIZE);

Avuton Olrich's avatar
Avuton Olrich committed
125
	if (param) {
126
		bufferSize = strtol(param->value, &test, 10);
Avuton Olrich's avatar
Avuton Olrich committed
127 128

		if (bufferSize <= 0 || *test != '\0') {
129
			FATAL("\"%s\" specified for %s at line %i is not a "
Avuton Olrich's avatar
Avuton Olrich committed
130 131
			      "positive integer\n",
			      param->value, CONF_HTTP_BUFFER_SIZE, param->line);
132 133 134 135
		}

		bufferSize *= 1024;

Avuton Olrich's avatar
Avuton Olrich committed
136 137
		if (prebufferSize > bufferSize)
			prebufferSize = bufferSize;
138 139 140 141
	}

	param = getConfigParam(CONF_HTTP_PREBUFFER_SIZE);

Avuton Olrich's avatar
Avuton Olrich committed
142
	if (param) {
143
		prebufferSize = strtol(param->value, &test, 10);
Avuton Olrich's avatar
Avuton Olrich committed
144 145

		if (prebufferSize <= 0 || *test != '\0') {
146
			FATAL("\"%s\" specified for %s at line %i is not a "
Avuton Olrich's avatar
Avuton Olrich committed
147 148 149
			      "positive integer\n",
			      param->value, CONF_HTTP_PREBUFFER_SIZE,
			      param->line);
150 151 152 153
		}

		prebufferSize *= 1024;
	}
154

Avuton Olrich's avatar
Avuton Olrich committed
155 156
	if (prebufferSize > bufferSize)
		prebufferSize = bufferSize;
157 158
}

159 160 161 162
/* base64 code taken from xmms */

#define BASE64_LENGTH(len) (4 * (((len) + 2) / 3))

Avuton Olrich's avatar
Avuton Olrich committed
163 164
static char *base64Dup(char *s)
{
165 166
	int i;
	int len = strlen(s);
167
	char *ret = xcalloc(BASE64_LENGTH(len) + 1, 1);
Avuton Olrich's avatar
Avuton Olrich committed
168
	unsigned char *p = (unsigned char *)ret;
169 170

	char tbl[64] = {
Avuton Olrich's avatar
Avuton Olrich committed
171 172 173 174 175 176 177 178
		'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
		'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
		'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
		'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
		'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
		'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
		'w', 'x', 'y', 'z', '0', '1', '2', '3',
		'4', '5', '6', '7', '8', '9', '+', '/'
179 180 181
	};

	/* Transform the 3x8 bits to 4x6 bits, as required by base64.  */
Avuton Olrich's avatar
Avuton Olrich committed
182
	for (i = 0; i < len; i += 3) {
183
		*p++ = tbl[s[0] >> 2];
Avuton Olrich's avatar
Avuton Olrich committed
184 185 186 187
		*p++ = tbl[((s[0] & 3) << 4) + (s[1] >> 4)];
		*p++ = tbl[((s[1] & 0xf) << 2) + (s[2] >> 6)];
		*p++ = tbl[s[2] & 0x3f];
		s += 3;
188 189
	}
	/* Pad the result if necessary...  */
Avuton Olrich's avatar
Avuton Olrich committed
190 191 192 193 194 195
	if (i == len + 1)
		*(p - 1) = '=';
	else if (i == len + 2)
		*(p - 1) = *(p - 2) = '=';
	/* ...and zero-terminate it.  */
	*p = '\0';
196 197 198 199

	return ret;
}

Avuton Olrich's avatar
Avuton Olrich committed
200 201 202
static char *authString(char *header, char *user, char *password)
{
	char *ret = NULL;
203
	int templen;
Avuton Olrich's avatar
Avuton Olrich committed
204 205
	char *temp;
	char *temp64;
206

Avuton Olrich's avatar
Avuton Olrich committed
207 208
	if (!user || !password)
		return NULL;
209 210

	templen = strlen(user) + strlen(password) + 2;
211
	temp = xmalloc(templen);
Warren Dukes's avatar
Warren Dukes committed
212 213 214
	strcpy(temp, user);
	strcat(temp, ":");
	strcat(temp, password);
215 216 217
	temp64 = base64Dup(temp);
	free(temp);

218
	ret = xmalloc(strlen(temp64) + strlen(header) + 3);
219
	strcpy(ret, header);
220 221 222 223 224 225 226
	strcat(ret, temp64);
	strcat(ret, "\r\n");
	free(temp64);

	return ret;
}

227 228 229 230 231 232
#define PROXY_AUTH_HEADER	"Proxy-Authorization: Basic "
#define HTTP_AUTH_HEADER	"Authorization: Basic "

#define proxyAuthString(x, y)	authString(PROXY_AUTH_HEADER, x, y)
#define httpAuthString(x, y)	authString(HTTP_AUTH_HEADER, x, y)

Avuton Olrich's avatar
Avuton Olrich committed
233 234
static InputStreamHTTPData *newInputStreamHTTPData(void)
{
235
	InputStreamHTTPData *ret = xmalloc(sizeof(InputStreamHTTPData));
Warren Dukes's avatar
Warren Dukes committed
236

Avuton Olrich's avatar
Avuton Olrich committed
237
	if (proxyHost) {
238
		ret->proxyAuth = proxyAuthString(proxyUser, proxyPassword);
Avuton Olrich's avatar
Avuton Olrich committed
239 240
	} else
		ret->proxyAuth = NULL;
241

242
	ret->httpAuth = NULL;
243
	ret->host = NULL;
Avuton Olrich's avatar
Avuton Olrich committed
244
	ret->path = NULL;
245
	ret->port = NULL;
Avuton Olrich's avatar
Avuton Olrich committed
246 247 248
	ret->connState = HTTP_CONN_STATE_CLOSED;
	ret->timesRedirected = 0;
	ret->icyMetaint = 0;
249
	ret->prebuffer = 0;
Warren Dukes's avatar
Warren Dukes committed
250
	ret->icyOffset = 0;
251
	ret->buffer = xmalloc(bufferSize);
Warren Dukes's avatar
Warren Dukes committed
252

Avuton Olrich's avatar
Avuton Olrich committed
253
	return ret;
Warren Dukes's avatar
Warren Dukes committed
254 255
}

Avuton Olrich's avatar
Avuton Olrich committed
256 257 258 259 260 261 262 263 264 265 266 267
static void freeInputStreamHTTPData(InputStreamHTTPData * data)
{
	if (data->host)
		free(data->host);
	if (data->path)
		free(data->path);
	if (data->port)
		free(data->port);
	if (data->proxyAuth)
		free(data->proxyAuth);
	if (data->httpAuth)
		free(data->httpAuth);
Warren Dukes's avatar
Warren Dukes committed
268

269 270
	free(data->buffer);

Avuton Olrich's avatar
Avuton Olrich committed
271
	free(data);
Warren Dukes's avatar
Warren Dukes committed
272 273
}

Avuton Olrich's avatar
Avuton Olrich committed
274 275 276 277 278 279 280
static int parseUrl(InputStreamHTTPData * data, char *url)
{
	char *temp;
	char *colon;
	char *slash;
	char *at;
	int len;
Warren Dukes's avatar
Warren Dukes committed
281

Avuton Olrich's avatar
Avuton Olrich committed
282 283
	if (strncmp("http://", url, strlen("http://")) != 0)
		return -1;
Warren Dukes's avatar
Warren Dukes committed
284

Avuton Olrich's avatar
Avuton Olrich committed
285
	temp = url + strlen("http://");
Warren Dukes's avatar
Warren Dukes committed
286

Avuton Olrich's avatar
Avuton Olrich committed
287
	colon = strchr(temp, ':');
288 289
	at = strchr(temp, '@');

Avuton Olrich's avatar
Avuton Olrich committed
290
	if (data->httpAuth) {
291 292 293 294
		free(data->httpAuth);
		data->httpAuth = NULL;
	}

Avuton Olrich's avatar
Avuton Olrich committed
295 296 297
	if (at) {
		char *user;
		char *passwd;
298

Avuton Olrich's avatar
Avuton Olrich committed
299
		if (colon && colon < at) {
300
			user = xmalloc(colon - temp + 1);
Eric Wong's avatar
Eric Wong committed
301
			memcpy(user, temp, colon - temp);
Avuton Olrich's avatar
Avuton Olrich committed
302 303
			user[colon - temp] = '\0';

304
			passwd = xmalloc(at - colon);
Eric Wong's avatar
Eric Wong committed
305
			memcpy(passwd, colon + 1, at - colon - 1);
Avuton Olrich's avatar
Avuton Olrich committed
306 307
			passwd[at - colon - 1] = '\0';
		} else {
308
			user = xmalloc(at - temp + 1);
Eric Wong's avatar
Eric Wong committed
309
			memcpy(user, temp, at - temp);
Avuton Olrich's avatar
Avuton Olrich committed
310
			user[at - temp] = '\0';
311

312
			passwd = xstrdup("");
313 314 315 316 317 318 319
		}

		data->httpAuth = httpAuthString(user, passwd);

		free(user);
		free(passwd);

Avuton Olrich's avatar
Avuton Olrich committed
320 321
		temp = at + 1;
		colon = strchr(temp, ':');
322 323
	}

Avuton Olrich's avatar
Avuton Olrich committed
324
	slash = strchr(temp, '/');
Warren Dukes's avatar
Warren Dukes committed
325

Avuton Olrich's avatar
Avuton Olrich committed
326 327
	if (slash && colon && slash <= colon)
		return -1;
Warren Dukes's avatar
Warren Dukes committed
328

Avuton Olrich's avatar
Avuton Olrich committed
329 330 331 332 333 334 335
	/* fetch the host portion */
	if (colon)
		len = colon - temp + 1;
	else if (slash)
		len = slash - temp + 1;
	else
		len = strlen(temp) + 1;
Warren Dukes's avatar
Warren Dukes committed
336

Avuton Olrich's avatar
Avuton Olrich committed
337 338
	if (len <= 1)
		return -1;
Warren Dukes's avatar
Warren Dukes committed
339

340
	data->host = xmalloc(len);
Eric Wong's avatar
Eric Wong committed
341
	memcpy(data->host, temp, len - 1);
Avuton Olrich's avatar
Avuton Olrich committed
342 343 344 345 346 347
	data->host[len - 1] = '\0';
	/* fetch the port */
	if (colon && (!slash || slash != colon + 1)) {
		len = strlen(colon) - 1;
		if (slash)
			len -= strlen(slash);
348
		data->port = xmalloc(len + 1);
Eric Wong's avatar
Eric Wong committed
349
		memcpy(data->port, colon + 1, len);
Avuton Olrich's avatar
Avuton Olrich committed
350
		data->port[len] = '\0';
351
		DEBUG(__FILE__ ": Port: %s\n", data->port);
Avuton Olrich's avatar
Avuton Olrich committed
352
	} else {
353
		data->port = xstrdup("80");
354
	}
Warren Dukes's avatar
Warren Dukes committed
355

Avuton Olrich's avatar
Avuton Olrich committed
356 357
	/* fetch the path */
	if (proxyHost)
358
		data->path = xstrdup(url);
Avuton Olrich's avatar
Avuton Olrich committed
359
	else
360
		data->path = xstrdup(slash ? slash : "/");
Avuton Olrich's avatar
Avuton Olrich committed
361 362

	return 0;
Warren Dukes's avatar
Warren Dukes committed
363 364
}

Avuton Olrich's avatar
Avuton Olrich committed
365 366
static int initHTTPConnection(InputStream * inStream)
{
367 368 369 370 371 372
	char *connHost;
	char *connPort;
	struct addrinfo *ans = NULL;
	struct addrinfo *ap = NULL;
	struct addrinfo hints;
	int error, flags;
Avuton Olrich's avatar
Avuton Olrich committed
373
	InputStreamHTTPData *data = (InputStreamHTTPData *) inStream->data;
374 375 376
	/**
	 * Setup hints
	 */
Avuton Olrich's avatar
Avuton Olrich committed
377 378 379 380 381 382 383 384 385 386
	hints.ai_flags = 0;
	hints.ai_family = PF_UNSPEC;
	hints.ai_socktype = SOCK_STREAM;
	hints.ai_protocol = IPPROTO_TCP;
	hints.ai_addrlen = 0;
	hints.ai_addr = NULL;
	hints.ai_canonname = NULL;
	hints.ai_next = NULL;

	if (proxyHost) {
387 388
		connHost = proxyHost;
		connPort = proxyPort;
Avuton Olrich's avatar
Avuton Olrich committed
389
	} else {
390 391 392 393
		connHost = data->host;
		connPort = data->port;
	}

394
	error = getaddrinfo(connHost, connPort, &hints, &ans);
Avuton Olrich's avatar
Avuton Olrich committed
395 396 397
	if (error) {
		DEBUG(__FILE__ ": Error getting address info: %s\n",
		      gai_strerror(error));
398 399
		return -1;
	}
Avuton Olrich's avatar
Avuton Olrich committed
400

401
	/* loop through possible addresses */
Avuton Olrich's avatar
Avuton Olrich committed
402 403 404 405 406
	for (ap = ans; ap != NULL; ap = ap->ai_next) {
		if ((data->sock = socket(ap->ai_family, ap->ai_socktype,
					 ap->ai_protocol)) < 0) {
			DEBUG(__FILE__ ": unable to connect: %s\n",
			      strerror(errno));
407 408 409
			freeaddrinfo(ans);
			return -1;
		}
Avuton Olrich's avatar
Avuton Olrich committed
410

411 412
		flags = fcntl(data->sock, F_GETFL, 0);
		fcntl(data->sock, F_SETFL, flags | O_NONBLOCK);
Avuton Olrich's avatar
Avuton Olrich committed
413 414 415

		if (connect(data->sock, ap->ai_addr, ap->ai_addrlen) >= 0
		    || errno == EINPROGRESS) {
416 417 418
			data->connState = HTTP_CONN_STATE_INIT;
			data->buflen = 0;
			freeaddrinfo(ans);
Avuton Olrich's avatar
Avuton Olrich committed
419
			return 0;	/* success */
420
		}
Avuton Olrich's avatar
Avuton Olrich committed
421

422
		/* failed, get the next one */
Avuton Olrich's avatar
Avuton Olrich committed
423

424
		DEBUG(__FILE__ ": unable to connect: %s\n", strerror(errno));
Avuton Olrich's avatar
Avuton Olrich committed
425 426
		close(data->sock);
	}
Warren Dukes's avatar
Warren Dukes committed
427

428
	freeaddrinfo(ans);
Avuton Olrich's avatar
Avuton Olrich committed
429
	return -1;	/* failed */
Warren Dukes's avatar
Warren Dukes committed
430 431
}

Avuton Olrich's avatar
Avuton Olrich committed
432 433 434 435 436 437 438 439 440
static int finishHTTPInit(InputStream * inStream)
{
	InputStreamHTTPData *data = (InputStreamHTTPData *) inStream->data;
	struct timeval tv;
	fd_set writeSet;
	fd_set errorSet;
	int error;
	socklen_t error_len = sizeof(int);
	int ret;
441 442
	int length;
	char request[2048];
Avuton Olrich's avatar
Avuton Olrich committed
443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458

	tv.tv_sec = 0;
	tv.tv_usec = 0;

	FD_ZERO(&writeSet);
	FD_ZERO(&errorSet);
	FD_SET(data->sock, &writeSet);
	FD_SET(data->sock, &errorSet);

	ret = select(data->sock + 1, NULL, &writeSet, &errorSet, &tv);

	if (ret == 0 || (ret < 0 && errno == EINTR))
		return 0;

	if (ret < 0) {
		DEBUG(__FILE__ ": problem select'ing: %s\n", strerror(errno));
459
		goto close_err;
Avuton Olrich's avatar
Avuton Olrich committed
460 461 462
	}

	getsockopt(data->sock, SOL_SOCKET, SO_ERROR, &error, &error_len);
463 464
	if (error)
		goto close_err;
Avuton Olrich's avatar
Avuton Olrich committed
465

466
	/* deal with ICY metadata later, for now its fucking up stuff! */
467 468 469 470 471 472
	length = snprintf(request, sizeof(request),
	                 "GET %s HTTP/1.1\r\n" "Host: %s\r\n"
			 /*"Connection: close\r\n" */
			 "User-Agent: %s/%s\r\n"
			 "Range: bytes=%ld-\r\n"
			 "%s"	/* authorization */
473
			 "Icy-Metadata:1\r\n"
474 475 476 477 478 479 480 481 482 483 484 485
			 "\r\n",
			 data->path, data->host,
			 PACKAGE_NAME, PACKAGE_VERSION,
			 inStream->offset,
			 data->proxyAuth ? data->proxyAuth :
			  (data->httpAuth ? data->httpAuth : ""));

	if (length >= sizeof(request))
		goto close_err;
	ret = write(data->sock, request, length);
	if (ret != length)
		goto close_err;
Avuton Olrich's avatar
Avuton Olrich committed
486 487 488

	data->connState = HTTP_CONN_STATE_HELLO;
	return 0;
489 490 491 492 493

close_err:
	close(data->sock);
	data->connState = HTTP_CONN_STATE_CLOSED;
	return -1;
Warren Dukes's avatar
Warren Dukes committed
494 495
}

Avuton Olrich's avatar
Avuton Olrich committed
496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585
static int getHTTPHello(InputStream * inStream)
{
	InputStreamHTTPData *data = (InputStreamHTTPData *) inStream->data;
	fd_set readSet;
	struct timeval tv;
	int ret;
	char *needle;
	char *cur = data->buffer;
	int rc;
	long readed;

	FD_ZERO(&readSet);
	FD_SET(data->sock, &readSet);

	tv.tv_sec = 0;
	tv.tv_usec = 0;

	ret = select(data->sock + 1, &readSet, NULL, NULL, &tv);

	if (ret == 0 || (ret < 0 && errno == EINTR))
		return 0;

	if (ret < 0) {
		data->connState = HTTP_CONN_STATE_CLOSED;
		close(data->sock);
		data->buflen = 0;
		return -1;
	}

	if (data->buflen >= bufferSize - 1) {
		data->connState = HTTP_CONN_STATE_CLOSED;
		close(data->sock);
		return -1;
	}

	readed = recv(data->sock, data->buffer + data->buflen,
		      bufferSize - 1 - data->buflen, 0);

	if (readed < 0 && (errno == EAGAIN || errno == EINTR))
		return 0;

	if (readed <= 0) {
		data->connState = HTTP_CONN_STATE_CLOSED;
		close(data->sock);
		data->buflen = 0;
		return -1;
	}

	data->buffer[data->buflen + readed] = '\0';
	data->buflen += readed;

	needle = strstr(data->buffer, "\r\n\r\n");

	if (!needle)
		return 0;

	if (0 == strncmp(cur, "HTTP/1.0 ", 9)) {
		inStream->seekable = 0;
		rc = atoi(cur + 9);
	} else if (0 == strncmp(cur, "HTTP/1.1 ", 9)) {
		inStream->seekable = 1;
		rc = atoi(cur + 9);
	} else if (0 == strncmp(cur, "ICY 200 OK", 10)) {
		inStream->seekable = 0;
		rc = 200;
	} else if (0 == strncmp(cur, "ICY 400 Server Full", 19))
		rc = 400;
	else if (0 == strncmp(cur, "ICY 404", 7))
		rc = 404;
	else {
		close(data->sock);
		data->connState = HTTP_CONN_STATE_CLOSED;
		return -1;
	}

	switch (rc) {
	case 200:
	case 206:
		break;
	case 301:
	case 302:
		cur = strstr(cur, "Location: ");
		if (cur) {
			char *url;
			int curlen = 0;
			cur += strlen("Location: ");
			while (*(cur + curlen) != '\0'
			       && *(cur + curlen) != '\r') {
				curlen++;
			}
586
			url = xmalloc(curlen + 1);
Avuton Olrich's avatar
Avuton Olrich committed
587 588 589 590 591 592 593 594 595 596 597 598 599 600 601
			memcpy(url, cur, curlen);
			url[curlen] = '\0';
			ret = parseUrl(data, url);
			free(url);
			if (ret == 0 && data->timesRedirected <
			    HTTP_REDIRECT_MAX) {
				data->timesRedirected++;
				close(data->sock);
				data->connState = HTTP_CONN_STATE_REOPEN;
				data->buflen = 0;
				return 0;
			}
		}
	case 400:
	case 401:
602
	case 403:
Avuton Olrich's avatar
Avuton Olrich committed
603 604 605 606 607 608 609 610 611 612
	case 404:
	default:
		close(data->sock);
		data->connState = HTTP_CONN_STATE_CLOSED;
		data->buflen = 0;
		return -1;
	}

	cur = strstr(data->buffer, "\r\n");
	while (cur && cur != needle) {
613
		if (0 == strncasecmp(cur, "\r\nContent-Length: ", 18)) {
Avuton Olrich's avatar
Avuton Olrich committed
614 615
			if (!inStream->size)
				inStream->size = atol(cur + 18);
616
		} else if (0 == strncasecmp(cur, "\r\nicy-metaint:", 14)) {
Avuton Olrich's avatar
Avuton Olrich committed
617
			data->icyMetaint = atoi(cur + 14);
618 619
		} else if (0 == strncasecmp(cur, "\r\nicy-name:", 11) ||
			   0 == strncasecmp(cur, "\r\nice-name:", 11)) {
Avuton Olrich's avatar
Avuton Olrich committed
620 621 622 623 624 625 626 627 628
			int incr = 11;
			char *temp = strstr(cur + incr, "\r\n");
			if (!temp)
				break;
			*temp = '\0';
			if (inStream->metaName)
				free(inStream->metaName);
			while (*(incr + cur) == ' ')
				incr++;
629
			inStream->metaName = xstrdup(cur + incr);
Avuton Olrich's avatar
Avuton Olrich committed
630 631 632
			*temp = '\r';
			DEBUG("inputStream_http: metaName: %s\n",
			      inStream->metaName);
633
		} else if (0 == strncasecmp(cur, "\r\nx-audiocast-name:", 19)) {
Avuton Olrich's avatar
Avuton Olrich committed
634 635 636 637 638 639 640 641 642
			int incr = 19;
			char *temp = strstr(cur + incr, "\r\n");
			if (!temp)
				break;
			*temp = '\0';
			if (inStream->metaName)
				free(inStream->metaName);
			while (*(incr + cur) == ' ')
				incr++;
643
			inStream->metaName = xstrdup(cur + incr);
Avuton Olrich's avatar
Avuton Olrich committed
644 645 646
			*temp = '\r';
			DEBUG("inputStream_http: metaName: %s\n",
			      inStream->metaName);
647
		} else if (0 == strncasecmp(cur, "\r\nContent-Type:", 15)) {
Avuton Olrich's avatar
Avuton Olrich committed
648 649 650 651 652 653 654 655 656
			int incr = 15;
			char *temp = strstr(cur + incr, "\r\n");
			if (!temp)
				break;
			*temp = '\0';
			if (inStream->mime)
				free(inStream->mime);
			while (*(incr + cur) == ' ')
				incr++;
657
			inStream->mime = xstrdup(cur + incr);
Avuton Olrich's avatar
Avuton Olrich committed
658 659 660 661 662 663 664 665 666
			*temp = '\r';
		}

		cur = strstr(cur + 2, "\r\n");
	}

	if (inStream->size <= 0)
		inStream->seekable = 0;

Avuton Olrich's avatar
Avuton Olrich committed
667
	needle += 4;	/* 4 == strlen("\r\n\r\n") */
Avuton Olrich's avatar
Avuton Olrich committed
668 669 670 671
	data->buflen -= (needle - data->buffer);
	memmove(data->buffer, needle, data->buflen);

	data->connState = HTTP_CONN_STATE_OPEN;
Warren Dukes's avatar
Warren Dukes committed
672

Warren Dukes's avatar
Warren Dukes committed
673
	data->prebuffer = 1;
674

Avuton Olrich's avatar
Avuton Olrich committed
675
	return 0;
Warren Dukes's avatar
Warren Dukes committed
676 677
}

Avuton Olrich's avatar
Avuton Olrich committed
678 679 680
int inputStream_httpOpen(InputStream * inStream, char *url)
{
	InputStreamHTTPData *data = newInputStreamHTTPData();
Warren Dukes's avatar
Warren Dukes committed
681

Avuton Olrich's avatar
Avuton Olrich committed
682
	inStream->data = data;
Warren Dukes's avatar
Warren Dukes committed
683

Avuton Olrich's avatar
Avuton Olrich committed
684 685 686 687
	if (parseUrl(data, url) < 0) {
		freeInputStreamHTTPData(data);
		return -1;
	}
Warren Dukes's avatar
Warren Dukes committed
688

Avuton Olrich's avatar
Avuton Olrich committed
689 690 691 692
	if (initHTTPConnection(inStream) < 0) {
		freeInputStreamHTTPData(data);
		return -1;
	}
Warren Dukes's avatar
Warren Dukes committed
693

Avuton Olrich's avatar
Avuton Olrich committed
694 695 696 697 698
	inStream->seekFunc = inputStream_httpSeek;
	inStream->closeFunc = inputStream_httpClose;
	inStream->readFunc = inputStream_httpRead;
	inStream->atEOFFunc = inputStream_httpAtEOF;
	inStream->bufferFunc = inputStream_httpBuffer;
Warren Dukes's avatar
Warren Dukes committed
699 700 701 702

	return 0;
}

Avuton Olrich's avatar
Avuton Olrich committed
703 704
int inputStream_httpSeek(InputStream * inStream, long offset, int whence)
{
705 706
	InputStreamHTTPData *data;

707 708
	if (!inStream->seekable)
		return -1;
709

710 711 712 713 714 715 716 717 718 719 720 721
	switch (whence) {
	case SEEK_SET:
		inStream->offset = offset;
		break;
	case SEEK_CUR:
		inStream->offset += offset;
		break;
	case SEEK_END:
		inStream->offset = inStream->size + offset;
		break;
	default:
		return -1;
722
	}
Avuton Olrich's avatar
Avuton Olrich committed
723

724
	data = (InputStreamHTTPData *)inStream->data;
725 726 727 728 729 730 731
	close(data->sock);
	data->connState = HTTP_CONN_STATE_REOPEN;
	data->buflen = 0;

	inputStream_httpBuffer(inStream);

	return 0;
Warren Dukes's avatar
Warren Dukes committed
732 733
}

Avuton Olrich's avatar
Avuton Olrich committed
734
static void parseIcyMetadata(InputStream * inStream, char *metadata, int size)
Warren Dukes's avatar
Warren Dukes committed
735
{
Avuton Olrich's avatar
Avuton Olrich committed
736 737
	char *r;
	char *s;
738
	char *temp = xmalloc(size + 1);
Warren Dukes's avatar
Warren Dukes committed
739 740 741
	memcpy(temp, metadata, size);
	temp[size] = '\0';
	s = strtok_r(temp, ";", &r);
Avuton Olrich's avatar
Avuton Olrich committed
742 743
	while (s) {
		if (0 == strncmp(s, "StreamTitle=", 12)) {
744
			int cur = 12;
Avuton Olrich's avatar
Avuton Olrich committed
745 746 747 748 749 750
			if (inStream->metaTitle)
				free(inStream->metaTitle);
			if (*(s + cur) == '\'')
				cur++;
			if (s[strlen(s) - 1] == '\'') {
				s[strlen(s) - 1] = '\0';
751
			}
752
			inStream->metaTitle = xstrdup(s + cur);
Avuton Olrich's avatar
Avuton Olrich committed
753 754
			DEBUG("inputStream_http: metaTitle: %s\n",
			      inStream->metaTitle);
Warren Dukes's avatar
Warren Dukes committed
755 756 757 758 759 760
		}
		s = strtok_r(NULL, ";", &r);
	}
	free(temp);
}

Avuton Olrich's avatar
Avuton Olrich committed
761 762
size_t inputStream_httpRead(InputStream * inStream, void *ptr, size_t size,
			    size_t nmemb)
Warren Dukes's avatar
Warren Dukes committed
763
{
Avuton Olrich's avatar
Avuton Olrich committed
764 765 766
	InputStreamHTTPData *data = (InputStreamHTTPData *) inStream->data;
	long tosend = 0;
	long inlen = size * nmemb;
Warren Dukes's avatar
Warren Dukes committed
767
	long maxToSend = data->buflen;
768

Avuton Olrich's avatar
Avuton Olrich committed
769
	inputStream_httpBuffer(inStream);
770

Avuton Olrich's avatar
Avuton Olrich committed
771 772 773 774
	switch (data->connState) {
	case HTTP_CONN_STATE_OPEN:
		if (data->prebuffer || data->buflen < data->icyMetaint)
			return 0;
775 776

		break;
Avuton Olrich's avatar
Avuton Olrich committed
777 778 779 780 781 782 783 784 785
	case HTTP_CONN_STATE_CLOSED:
		if (data->buflen)
			break;
	default:
		return 0;
	}

	if (data->icyMetaint > 0) {
		if (data->icyOffset >= data->icyMetaint) {
Warren Dukes's avatar
Warren Dukes committed
786 787
			int metalen = *(data->buffer);
			metalen <<= 4;
Avuton Olrich's avatar
Avuton Olrich committed
788 789 790
			if (metalen < 0)
				metalen = 0;
			if (metalen + 1 > data->buflen) {
Warren Dukes's avatar
Warren Dukes committed
791
				/* damn that's some fucking big metadata! */
Avuton Olrich's avatar
Avuton Olrich committed
792 793 794 795
				if (bufferSize < metalen + 1) {
					data->connState =
					    HTTP_CONN_STATE_CLOSED;
					close(data->sock);
Warren Dukes's avatar
Warren Dukes committed
796 797 798 799
					data->buflen = 0;
				}
				return 0;
			}
Avuton Olrich's avatar
Avuton Olrich committed
800 801 802
			if (metalen > 0) {
				parseIcyMetadata(inStream, data->buffer + 1,
						 metalen);
Warren Dukes's avatar
Warren Dukes committed
803
			}
Avuton Olrich's avatar
Avuton Olrich committed
804 805 806
			data->buflen -= metalen + 1;
			memmove(data->buffer, data->buffer + metalen + 1,
				data->buflen);
Warren Dukes's avatar
Warren Dukes committed
807 808
			data->icyOffset = 0;
		}
Avuton Olrich's avatar
Avuton Olrich committed
809
		maxToSend = data->icyMetaint - data->icyOffset;
Warren Dukes's avatar
Warren Dukes committed
810 811
		maxToSend = maxToSend > data->buflen ? data->buflen : maxToSend;
	}
812

Avuton Olrich's avatar
Avuton Olrich committed
813 814 815
	if (data->buflen > 0) {
		tosend = inlen > maxToSend ? maxToSend : inlen;
		tosend = (tosend / size) * size;
816

Avuton Olrich's avatar
Avuton Olrich committed
817 818 819 820 821 822 823
		memcpy(ptr, data->buffer, tosend);
		data->buflen -= tosend;
		data->icyOffset += tosend;
		memmove(data->buffer, data->buffer + tosend, data->buflen);

		inStream->offset += tosend;
	}
824

Avuton Olrich's avatar
Avuton Olrich committed
825
	return tosend / size;
826 827
}

Avuton Olrich's avatar
Avuton Olrich committed
828 829 830
int inputStream_httpClose(InputStream * inStream)
{
	InputStreamHTTPData *data = (InputStreamHTTPData *) inStream->data;
831

Avuton Olrich's avatar
Avuton Olrich committed
832 833 834 835 836 837
	switch (data->connState) {
	case HTTP_CONN_STATE_CLOSED:
		break;
	default:
		close(data->sock);
	}
838

Avuton Olrich's avatar
Avuton Olrich committed
839
	freeInputStreamHTTPData(data);
840

Avuton Olrich's avatar
Avuton Olrich committed
841
	return 0;
842 843
}

Avuton Olrich's avatar
Avuton Olrich committed
844 845 846 847 848 849 850 851 852 853
int inputStream_httpAtEOF(InputStream * inStream)
{
	InputStreamHTTPData *data = (InputStreamHTTPData *) inStream->data;
	switch (data->connState) {
	case HTTP_CONN_STATE_CLOSED:
		if (data->buflen == 0)
			return 1;
	default:
		return 0;
	}
854 855
}

Avuton Olrich's avatar
Avuton Olrich committed
856 857 858 859
int inputStream_httpBuffer(InputStream * inStream)
{
	InputStreamHTTPData *data = (InputStreamHTTPData *) inStream->data;
	ssize_t readed = 0;
Warren Dukes's avatar
Warren Dukes committed
860

Avuton Olrich's avatar
Avuton Olrich committed
861 862 863 864
	if (data->connState == HTTP_CONN_STATE_REOPEN) {
		if (initHTTPConnection(inStream) < 0)
			return -1;
	}
Warren Dukes's avatar
Warren Dukes committed
865

Avuton Olrich's avatar
Avuton Olrich committed
866 867 868 869
	if (data->connState == HTTP_CONN_STATE_INIT) {
		if (finishHTTPInit(inStream) < 0)
			return -1;
	}
Warren Dukes's avatar
Warren Dukes committed
870

Avuton Olrich's avatar
Avuton Olrich committed
871 872 873 874
	if (data->connState == HTTP_CONN_STATE_HELLO) {
		if (getHTTPHello(inStream) < 0)
			return -1;
	}
Warren Dukes's avatar
Warren Dukes committed
875

Avuton Olrich's avatar
Avuton Olrich committed
876 877 878 879 880 881 882
	switch (data->connState) {
	case HTTP_CONN_STATE_OPEN:
	case HTTP_CONN_STATE_CLOSED:
		break;
	default:
		return -1;
	}
Warren Dukes's avatar
Warren Dukes committed
883

Avuton Olrich's avatar
Avuton Olrich committed
884
	if (data->buflen == 0 || data->buflen < data->icyMetaint) {
Warren Dukes's avatar
Warren Dukes committed
885
		data->prebuffer = 1;
Avuton Olrich's avatar
Avuton Olrich committed
886 887 888 889 890 891 892 893 894 895 896 897 898 899 900
	} else if (data->buflen > prebufferSize)
		data->prebuffer = 0;

	if (data->connState == HTTP_CONN_STATE_OPEN &&
	    data->buflen < bufferSize - 1) {
		readed = read(data->sock, data->buffer + data->buflen,
			      (size_t) (bufferSize - 1 - data->buflen));

		if (readed < 0 && (errno == EAGAIN || errno == EINTR)) {
			readed = 0;
		} else if (readed <= 0) {
			close(data->sock);
			data->connState = HTTP_CONN_STATE_CLOSED;
			readed = 0;
		}
901
		data->buflen += readed;
Avuton Olrich's avatar
Avuton Olrich committed
902
	}
Warren Dukes's avatar
Warren Dukes committed
903

Avuton Olrich's avatar
Avuton Olrich committed
904 905
	if (data->buflen > prebufferSize)
		data->prebuffer = 0;
906

Avuton Olrich's avatar
Avuton Olrich committed
907
	return (readed ? 1 : 0);
Warren Dukes's avatar
Warren Dukes committed
908
}