Initial revision 6759
[qball-mpd.git] / src / inputStream_http.c
blobf7bcbce0fbdc762a83979373548add4f670f6f15
1 /* the Music Player Daemon (MPD)
2 * Copyright (C) 2003-2007 by Warren Dukes (warren.dukes@gmail.com)
3 * This project's homepage is: http://www.musicpd.org
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 #include "inputStream_http.h"
21 #include "utils.h"
22 #include "log.h"
23 #include "conf.h"
25 #include <stdio.h>
26 #include <sys/time.h>
27 #include <sys/types.h>
28 #include <sys/socket.h>
29 #include <netdb.h>
30 #include <sys/param.h>
31 #include <netinet/in.h>
32 #include <arpa/inet.h>
33 #include <unistd.h>
34 #include <errno.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <fcntl.h>
39 #define HTTP_CONN_STATE_CLOSED 0
40 #define HTTP_CONN_STATE_INIT 1
41 #define HTTP_CONN_STATE_HELLO 2
42 #define HTTP_CONN_STATE_OPEN 3
43 #define HTTP_CONN_STATE_REOPEN 4
45 #define HTTP_BUFFER_SIZE_DEFAULT 131072
46 #define HTTP_PREBUFFER_SIZE_DEFAULT (HTTP_BUFFER_SIZE_DEFAULT >> 2)
48 #define HTTP_REDIRECT_MAX 10
50 static char *proxyHost;
51 static char *proxyPort;
52 static char *proxyUser;
53 static char *proxyPassword;
54 static int bufferSize = HTTP_BUFFER_SIZE_DEFAULT;
55 static int prebufferSize = HTTP_PREBUFFER_SIZE_DEFAULT;
57 typedef struct _InputStreemHTTPData {
58 char *host;
59 char *path;
60 char *port;
61 int sock;
62 int connState;
63 char *buffer;
64 size_t buflen;
65 int timesRedirected;
66 int icyMetaint;
67 int prebuffer;
68 int icyOffset;
69 char *proxyAuth;
70 char *httpAuth;
71 } InputStreamHTTPData;
73 void inputStream_initHttp(void)
75 ConfigParam *param = getConfigParam(CONF_HTTP_PROXY_HOST);
76 char *test;
78 if (param) {
79 proxyHost = param->value;
81 param = getConfigParam(CONF_HTTP_PROXY_PORT);
83 if (!param) {
84 FATAL("%s specified but not %s", CONF_HTTP_PROXY_HOST,
85 CONF_HTTP_PROXY_PORT);
87 proxyPort = param->value;
89 param = getConfigParam(CONF_HTTP_PROXY_USER);
91 if (param) {
92 proxyUser = param->value;
94 param = getConfigParam(CONF_HTTP_PROXY_PASSWORD);
96 if (!param) {
97 FATAL("%s specified but not %s\n",
98 CONF_HTTP_PROXY_USER,
99 CONF_HTTP_PROXY_PASSWORD);
102 proxyPassword = param->value;
103 } else {
104 param = getConfigParam(CONF_HTTP_PROXY_PASSWORD);
106 if (param) {
107 FATAL("%s specified but not %s\n",
108 CONF_HTTP_PROXY_PASSWORD, CONF_HTTP_PROXY_USER);
111 } else if ((param = getConfigParam(CONF_HTTP_PROXY_PORT))) {
112 FATAL("%s specified but not %s, line %i\n",
113 CONF_HTTP_PROXY_PORT, CONF_HTTP_PROXY_HOST, param->line);
114 } else if ((param = getConfigParam(CONF_HTTP_PROXY_USER))) {
115 FATAL("%s specified but not %s, line %i\n",
116 CONF_HTTP_PROXY_USER, CONF_HTTP_PROXY_HOST, param->line);
117 } else if ((param = getConfigParam(CONF_HTTP_PROXY_PASSWORD))) {
118 FATAL("%s specified but not %s, line %i\n",
119 CONF_HTTP_PROXY_PASSWORD, CONF_HTTP_PROXY_HOST,
120 param->line);
123 param = getConfigParam(CONF_HTTP_BUFFER_SIZE);
125 if (param) {
126 bufferSize = strtol(param->value, &test, 10);
128 if (bufferSize <= 0 || *test != '\0') {
129 FATAL("\"%s\" specified for %s at line %i is not a "
130 "positive integer\n",
131 param->value, CONF_HTTP_BUFFER_SIZE, param->line);
134 bufferSize *= 1024;
136 if (prebufferSize > bufferSize)
137 prebufferSize = bufferSize;
140 param = getConfigParam(CONF_HTTP_PREBUFFER_SIZE);
142 if (param) {
143 prebufferSize = strtol(param->value, &test, 10);
145 if (prebufferSize <= 0 || *test != '\0') {
146 FATAL("\"%s\" specified for %s at line %i is not a "
147 "positive integer\n",
148 param->value, CONF_HTTP_PREBUFFER_SIZE,
149 param->line);
152 prebufferSize *= 1024;
155 if (prebufferSize > bufferSize)
156 prebufferSize = bufferSize;
159 /* base64 code taken from xmms */
161 #define BASE64_LENGTH(len) (4 * (((len) + 2) / 3))
163 static char *base64Dup(char *s)
165 int i;
166 int len = strlen(s);
167 char *ret = xcalloc(BASE64_LENGTH(len) + 1, 1);
168 unsigned char *p = (unsigned char *)ret;
170 char tbl[64] = {
171 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
172 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
173 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
174 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
175 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
176 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
177 'w', 'x', 'y', 'z', '0', '1', '2', '3',
178 '4', '5', '6', '7', '8', '9', '+', '/'
181 /* Transform the 3x8 bits to 4x6 bits, as required by base64. */
182 for (i = 0; i < len; i += 3) {
183 *p++ = tbl[s[0] >> 2];
184 *p++ = tbl[((s[0] & 3) << 4) + (s[1] >> 4)];
185 *p++ = tbl[((s[1] & 0xf) << 2) + (s[2] >> 6)];
186 *p++ = tbl[s[2] & 0x3f];
187 s += 3;
189 /* Pad the result if necessary... */
190 if (i == len + 1)
191 *(p - 1) = '=';
192 else if (i == len + 2)
193 *(p - 1) = *(p - 2) = '=';
194 /* ...and zero-terminate it. */
195 *p = '\0';
197 return ret;
200 static char *authString(char *header, char *user, char *password)
202 char *ret = NULL;
203 int templen;
204 char *temp;
205 char *temp64;
207 if (!user || !password)
208 return NULL;
210 templen = strlen(user) + strlen(password) + 2;
211 temp = xmalloc(templen);
212 strcpy(temp, user);
213 strcat(temp, ":");
214 strcat(temp, password);
215 temp64 = base64Dup(temp);
216 free(temp);
218 ret = xmalloc(strlen(temp64) + strlen(header) + 3);
219 strcpy(ret, header);
220 strcat(ret, temp64);
221 strcat(ret, "\r\n");
222 free(temp64);
224 return ret;
227 #define PROXY_AUTH_HEADER "Proxy-Authorization: Basic "
228 #define HTTP_AUTH_HEADER "Authorization: Basic "
230 #define proxyAuthString(x, y) authString(PROXY_AUTH_HEADER, x, y)
231 #define httpAuthString(x, y) authString(HTTP_AUTH_HEADER, x, y)
233 static InputStreamHTTPData *newInputStreamHTTPData(void)
235 InputStreamHTTPData *ret = xmalloc(sizeof(InputStreamHTTPData));
237 if (proxyHost) {
238 ret->proxyAuth = proxyAuthString(proxyUser, proxyPassword);
239 } else
240 ret->proxyAuth = NULL;
242 ret->httpAuth = NULL;
243 ret->host = NULL;
244 ret->path = NULL;
245 ret->port = NULL;
246 ret->connState = HTTP_CONN_STATE_CLOSED;
247 ret->timesRedirected = 0;
248 ret->icyMetaint = 0;
249 ret->prebuffer = 0;
250 ret->icyOffset = 0;
251 ret->buffer = xmalloc(bufferSize);
253 return ret;
256 static void freeInputStreamHTTPData(InputStreamHTTPData * data)
258 if (data->host)
259 free(data->host);
260 if (data->path)
261 free(data->path);
262 if (data->port)
263 free(data->port);
264 if (data->proxyAuth)
265 free(data->proxyAuth);
266 if (data->httpAuth)
267 free(data->httpAuth);
269 free(data->buffer);
271 free(data);
274 static int parseUrl(InputStreamHTTPData * data, char *url)
276 char *temp;
277 char *colon;
278 char *slash;
279 char *at;
280 int len;
282 if (strncmp("http://", url, strlen("http://")) != 0)
283 return -1;
285 temp = url + strlen("http://");
287 colon = strchr(temp, ':');
288 at = strchr(temp, '@');
290 if (data->httpAuth) {
291 free(data->httpAuth);
292 data->httpAuth = NULL;
295 if (at) {
296 char *user;
297 char *passwd;
299 if (colon && colon < at) {
300 user = xmalloc(colon - temp + 1);
301 memcpy(user, temp, colon - temp);
302 user[colon - temp] = '\0';
304 passwd = xmalloc(at - colon);
305 memcpy(passwd, colon + 1, at - colon - 1);
306 passwd[at - colon - 1] = '\0';
307 } else {
308 user = xmalloc(at - temp + 1);
309 memcpy(user, temp, at - temp);
310 user[at - temp] = '\0';
312 passwd = xstrdup("");
315 data->httpAuth = httpAuthString(user, passwd);
317 free(user);
318 free(passwd);
320 temp = at + 1;
321 colon = strchr(temp, ':');
324 slash = strchr(temp, '/');
326 if (slash && colon && slash <= colon)
327 return -1;
329 /* fetch the host portion */
330 if (colon)
331 len = colon - temp + 1;
332 else if (slash)
333 len = slash - temp + 1;
334 else
335 len = strlen(temp) + 1;
337 if (len <= 1)
338 return -1;
340 data->host = xmalloc(len);
341 memcpy(data->host, temp, len - 1);
342 data->host[len - 1] = '\0';
343 /* fetch the port */
344 if (colon && (!slash || slash != colon + 1)) {
345 len = strlen(colon) - 1;
346 if (slash)
347 len -= strlen(slash);
348 data->port = xmalloc(len + 1);
349 memcpy(data->port, colon + 1, len);
350 data->port[len] = '\0';
351 DEBUG(__FILE__ ": Port: %s\n", data->port);
352 } else {
353 data->port = xstrdup("80");
356 /* fetch the path */
357 if (proxyHost)
358 data->path = xstrdup(url);
359 else
360 data->path = xstrdup(slash ? slash : "/");
362 return 0;
365 static int initHTTPConnection(InputStream * inStream)
367 char *connHost;
368 char *connPort;
369 struct addrinfo *ans = NULL;
370 struct addrinfo *ap = NULL;
371 struct addrinfo hints;
372 int error, flags;
373 InputStreamHTTPData *data = (InputStreamHTTPData *) inStream->data;
375 * Setup hints
377 hints.ai_flags = 0;
378 hints.ai_family = PF_UNSPEC;
379 hints.ai_socktype = SOCK_STREAM;
380 hints.ai_protocol = IPPROTO_TCP;
381 hints.ai_addrlen = 0;
382 hints.ai_addr = NULL;
383 hints.ai_canonname = NULL;
384 hints.ai_next = NULL;
386 if (proxyHost) {
387 connHost = proxyHost;
388 connPort = proxyPort;
389 } else {
390 connHost = data->host;
391 connPort = data->port;
394 error = getaddrinfo(connHost, connPort, &hints, &ans);
395 if (error) {
396 DEBUG(__FILE__ ": Error getting address info: %s\n",
397 gai_strerror(error));
398 return -1;
401 /* loop through possible addresses */
402 for (ap = ans; ap != NULL; ap = ap->ai_next) {
403 if ((data->sock = socket(ap->ai_family, ap->ai_socktype,
404 ap->ai_protocol)) < 0) {
405 DEBUG(__FILE__ ": unable to connect: %s\n",
406 strerror(errno));
407 freeaddrinfo(ans);
408 return -1;
411 flags = fcntl(data->sock, F_GETFL, 0);
412 fcntl(data->sock, F_SETFL, flags | O_NONBLOCK);
414 if (connect(data->sock, ap->ai_addr, ap->ai_addrlen) >= 0
415 || errno == EINPROGRESS) {
416 data->connState = HTTP_CONN_STATE_INIT;
417 data->buflen = 0;
418 freeaddrinfo(ans);
419 return 0; /* success */
422 /* failed, get the next one */
424 DEBUG(__FILE__ ": unable to connect: %s\n", strerror(errno));
425 close(data->sock);
428 freeaddrinfo(ans);
429 return -1; /* failed */
432 static int finishHTTPInit(InputStream * inStream)
434 InputStreamHTTPData *data = (InputStreamHTTPData *) inStream->data;
435 struct timeval tv;
436 fd_set writeSet;
437 fd_set errorSet;
438 int error;
439 socklen_t error_len = sizeof(int);
440 int ret;
441 int length;
442 char request[2048];
444 tv.tv_sec = 0;
445 tv.tv_usec = 0;
447 FD_ZERO(&writeSet);
448 FD_ZERO(&errorSet);
449 FD_SET(data->sock, &writeSet);
450 FD_SET(data->sock, &errorSet);
452 ret = select(data->sock + 1, NULL, &writeSet, &errorSet, &tv);
454 if (ret == 0 || (ret < 0 && errno == EINTR))
455 return 0;
457 if (ret < 0) {
458 DEBUG(__FILE__ ": problem select'ing: %s\n", strerror(errno));
459 goto close_err;
462 getsockopt(data->sock, SOL_SOCKET, SO_ERROR, &error, &error_len);
463 if (error)
464 goto close_err;
466 /* deal with ICY metadata later, for now its fucking up stuff! */
467 length = snprintf(request, sizeof(request),
468 "GET %s HTTP/1.1\r\n"
469 "Host: %s\r\n"
470 /* "Connection: close\r\n" */
471 "User-Agent: " PACKAGE_NAME "/" PACKAGE_VERSION "\r\n"
472 "Range: bytes=%ld-\r\n"
473 "%s" /* authorization */
474 "Icy-Metadata:1\r\n"
475 "\r\n",
476 data->path,
477 data->host,
478 inStream->offset,
479 data->proxyAuth ? data->proxyAuth :
480 (data->httpAuth ? data->httpAuth : ""));
482 if (length >= sizeof(request))
483 goto close_err;
484 ret = write(data->sock, request, length);
485 if (ret != length)
486 goto close_err;
488 data->connState = HTTP_CONN_STATE_HELLO;
489 return 0;
491 close_err:
492 close(data->sock);
493 data->connState = HTTP_CONN_STATE_CLOSED;
494 return -1;
497 static int getHTTPHello(InputStream * inStream)
499 InputStreamHTTPData *data = (InputStreamHTTPData *) inStream->data;
500 fd_set readSet;
501 struct timeval tv;
502 int ret;
503 char *needle;
504 char *cur = data->buffer;
505 int rc;
506 long readed;
508 FD_ZERO(&readSet);
509 FD_SET(data->sock, &readSet);
511 tv.tv_sec = 0;
512 tv.tv_usec = 0;
514 ret = select(data->sock + 1, &readSet, NULL, NULL, &tv);
516 if (ret == 0 || (ret < 0 && errno == EINTR))
517 return 0;
519 if (ret < 0) {
520 data->connState = HTTP_CONN_STATE_CLOSED;
521 close(data->sock);
522 data->buflen = 0;
523 return -1;
526 if (data->buflen >= bufferSize - 1) {
527 data->connState = HTTP_CONN_STATE_CLOSED;
528 close(data->sock);
529 return -1;
532 readed = recv(data->sock, data->buffer + data->buflen,
533 bufferSize - 1 - data->buflen, 0);
535 if (readed < 0 && (errno == EAGAIN || errno == EINTR))
536 return 0;
538 if (readed <= 0) {
539 data->connState = HTTP_CONN_STATE_CLOSED;
540 close(data->sock);
541 data->buflen = 0;
542 return -1;
545 data->buffer[data->buflen + readed] = '\0';
546 data->buflen += readed;
548 needle = strstr(data->buffer, "\r\n\r\n");
550 if (!needle)
551 return 0;
553 if (0 == strncmp(cur, "HTTP/1.0 ", 9)) {
554 inStream->seekable = 0;
555 rc = atoi(cur + 9);
556 } else if (0 == strncmp(cur, "HTTP/1.1 ", 9)) {
557 inStream->seekable = 1;
558 rc = atoi(cur + 9);
559 } else if (0 == strncmp(cur, "ICY 200 OK", 10)) {
560 inStream->seekable = 0;
561 rc = 200;
562 } else if (0 == strncmp(cur, "ICY 400 Server Full", 19))
563 rc = 400;
564 else if (0 == strncmp(cur, "ICY 404", 7))
565 rc = 404;
566 else {
567 close(data->sock);
568 data->connState = HTTP_CONN_STATE_CLOSED;
569 return -1;
572 switch (rc) {
573 case 200:
574 case 206:
575 break;
576 case 301:
577 case 302:
578 cur = strstr(cur, "Location: ");
579 if (cur) {
580 char *url;
581 int curlen = 0;
582 cur += strlen("Location: ");
583 while (*(cur + curlen) != '\0'
584 && *(cur + curlen) != '\r') {
585 curlen++;
587 url = xmalloc(curlen + 1);
588 memcpy(url, cur, curlen);
589 url[curlen] = '\0';
590 ret = parseUrl(data, url);
591 free(url);
592 if (ret == 0 && data->timesRedirected <
593 HTTP_REDIRECT_MAX) {
594 data->timesRedirected++;
595 close(data->sock);
596 data->connState = HTTP_CONN_STATE_REOPEN;
597 data->buflen = 0;
598 return 0;
601 case 400:
602 case 401:
603 case 403:
604 case 404:
605 default:
606 close(data->sock);
607 data->connState = HTTP_CONN_STATE_CLOSED;
608 data->buflen = 0;
609 return -1;
612 cur = strstr(data->buffer, "\r\n");
613 while (cur && cur != needle) {
614 if (0 == strncasecmp(cur, "\r\nContent-Length: ", 18)) {
615 if (!inStream->size)
616 inStream->size = atol(cur + 18);
617 } else if (0 == strncasecmp(cur, "\r\nicy-metaint:", 14)) {
618 data->icyMetaint = atoi(cur + 14);
619 } else if (0 == strncasecmp(cur, "\r\nicy-name:", 11) ||
620 0 == strncasecmp(cur, "\r\nice-name:", 11)) {
621 int incr = 11;
622 char *temp = strstr(cur + incr, "\r\n");
623 if (!temp)
624 break;
625 *temp = '\0';
626 if (inStream->metaName)
627 free(inStream->metaName);
628 while (*(incr + cur) == ' ')
629 incr++;
630 inStream->metaName = xstrdup(cur + incr);
631 *temp = '\r';
632 DEBUG("inputStream_http: metaName: %s\n",
633 inStream->metaName);
634 } else if (0 == strncasecmp(cur, "\r\nx-audiocast-name:", 19)) {
635 int incr = 19;
636 char *temp = strstr(cur + incr, "\r\n");
637 if (!temp)
638 break;
639 *temp = '\0';
640 if (inStream->metaName)
641 free(inStream->metaName);
642 while (*(incr + cur) == ' ')
643 incr++;
644 inStream->metaName = xstrdup(cur + incr);
645 *temp = '\r';
646 DEBUG("inputStream_http: metaName: %s\n",
647 inStream->metaName);
648 } else if (0 == strncasecmp(cur, "\r\nContent-Type:", 15)) {
649 int incr = 15;
650 char *temp = strstr(cur + incr, "\r\n");
651 if (!temp)
652 break;
653 *temp = '\0';
654 if (inStream->mime)
655 free(inStream->mime);
656 while (*(incr + cur) == ' ')
657 incr++;
658 inStream->mime = xstrdup(cur + incr);
659 *temp = '\r';
662 cur = strstr(cur + 2, "\r\n");
665 if (inStream->size <= 0)
666 inStream->seekable = 0;
668 needle += 4; /* 4 == strlen("\r\n\r\n") */
669 data->buflen -= (needle - data->buffer);
670 memmove(data->buffer, needle, data->buflen);
672 data->connState = HTTP_CONN_STATE_OPEN;
674 data->prebuffer = 1;
676 return 0;
679 int inputStream_httpOpen(InputStream * inStream, char *url)
681 InputStreamHTTPData *data = newInputStreamHTTPData();
683 inStream->data = data;
685 if (parseUrl(data, url) < 0) {
686 freeInputStreamHTTPData(data);
687 return -1;
690 if (initHTTPConnection(inStream) < 0) {
691 freeInputStreamHTTPData(data);
692 return -1;
695 inStream->seekFunc = inputStream_httpSeek;
696 inStream->closeFunc = inputStream_httpClose;
697 inStream->readFunc = inputStream_httpRead;
698 inStream->atEOFFunc = inputStream_httpAtEOF;
699 inStream->bufferFunc = inputStream_httpBuffer;
701 while (!inputStream_httpAtEOF(inStream)) {
702 if (inputStream_httpBuffer(inStream) >= 0) {
703 return 0;
705 /* sleep so we don't consume 100% of the cpu */
706 my_usleep(1000);
709 freeInputStreamHTTPData(data);
710 return -1;
713 int inputStream_httpSeek(InputStream * inStream, long offset, int whence)
715 InputStreamHTTPData *data;
717 if (!inStream->seekable)
718 return -1;
720 switch (whence) {
721 case SEEK_SET:
722 inStream->offset = offset;
723 break;
724 case SEEK_CUR:
725 inStream->offset += offset;
726 break;
727 case SEEK_END:
728 inStream->offset = inStream->size + offset;
729 break;
730 default:
731 return -1;
733 data = (InputStreamHTTPData *)inStream->data;
734 close(data->sock);
735 data->connState = HTTP_CONN_STATE_REOPEN;
736 data->buflen = 0;
738 while (!inputStream_httpAtEOF(inStream)) {
739 if (inputStream_httpBuffer(inStream) >= 0) {
740 return 0;
742 /* sleep so we don't consume 100% of the cpu */
743 my_usleep(1000);
746 return -1;
749 static void parseIcyMetadata(InputStream * inStream, char *metadata, int size)
751 char *r;
752 char *s;
753 char *temp = xmalloc(size + 1);
754 memcpy(temp, metadata, size);
755 temp[size] = '\0';
756 s = strtok_r(temp, ";", &r);
757 while (s) {
758 if (0 == strncmp(s, "StreamTitle=", 12)) {
759 int cur = 12;
760 if (inStream->metaTitle)
761 free(inStream->metaTitle);
762 if (*(s + cur) == '\'')
763 cur++;
764 if (s[strlen(s) - 1] == '\'') {
765 s[strlen(s) - 1] = '\0';
767 inStream->metaTitle = xstrdup(s + cur);
768 DEBUG("inputStream_http: metaTitle: %s\n",
769 inStream->metaTitle);
771 s = strtok_r(NULL, ";", &r);
773 free(temp);
777 * This stuff can only handle max bufsize sized blocks in the good case.
778 * Good case means the buffer is full.
780 static size_t inputStream_httpRead_(InputStream * inStream, void *ptr,
781 size_t size, size_t nmemb)
783 InputStreamHTTPData *data = (InputStreamHTTPData *) inStream->data;
784 long tosend = 0;
785 long inlen = size * nmemb;
786 long maxToSend = data->buflen;
788 inputStream_httpBuffer(inStream);
790 switch (data->connState) {
791 case HTTP_CONN_STATE_OPEN:
792 if (data->prebuffer || data->buflen < data->icyMetaint)
793 return 0;
795 break;
796 case HTTP_CONN_STATE_CLOSED:
797 if (data->buflen)
798 break;
799 default:
800 return 0;
803 if (data->icyMetaint > 0) {
804 if (data->icyOffset >= data->icyMetaint) {
805 int metalen = *(data->buffer);
806 metalen <<= 4;
807 if (metalen < 0)
808 metalen = 0;
809 if (metalen + 1 > data->buflen) {
810 /* damn that's some fucking big metadata! */
811 if (bufferSize < metalen + 1) {
812 data->connState =
813 HTTP_CONN_STATE_CLOSED;
814 close(data->sock);
815 data->buflen = 0;
817 return 0;
819 if (metalen > 0) {
820 parseIcyMetadata(inStream, data->buffer + 1,
821 metalen);
823 data->buflen -= metalen + 1;
824 memmove(data->buffer, data->buffer + metalen + 1,
825 data->buflen);
826 data->icyOffset = 0;
828 maxToSend = data->icyMetaint - data->icyOffset;
829 maxToSend = maxToSend > data->buflen ? data->buflen : maxToSend;
832 if (data->buflen > 0) {
833 tosend = inlen > maxToSend ? maxToSend : inlen;
834 tosend = (tosend / size) * size;
836 memcpy(ptr, data->buffer, tosend);
837 data->buflen -= tosend;
838 data->icyOffset += tosend;
839 memmove(data->buffer, data->buffer + tosend, data->buflen);
841 inStream->offset += tosend;
844 return tosend / size;
847 /* wrapper for the previous function */
848 size_t inputStream_httpRead(InputStream *inStream, void *ptr, size_t size,
849 size_t nmemb)
851 size_t req = nmemb * size;
852 size_t got = 0;
854 while (req) {
855 size_t r = inputStream_httpRead_(inStream, ptr + got, 1, req);
856 got += r;
857 req -= r;
858 if (inputStream_httpAtEOF(inStream))
859 break;
862 return got / size;
865 int inputStream_httpClose(InputStream * inStream)
867 InputStreamHTTPData *data = (InputStreamHTTPData *) inStream->data;
869 switch (data->connState) {
870 case HTTP_CONN_STATE_CLOSED:
871 break;
872 default:
873 close(data->sock);
876 freeInputStreamHTTPData(data);
878 return 0;
881 int inputStream_httpAtEOF(InputStream * inStream)
883 InputStreamHTTPData *data = (InputStreamHTTPData *) inStream->data;
884 switch (data->connState) {
885 case HTTP_CONN_STATE_CLOSED:
886 if (data->buflen == 0)
887 return 1;
888 default:
889 return 0;
893 int inputStream_httpBuffer(InputStream * inStream)
895 InputStreamHTTPData *data = (InputStreamHTTPData *) inStream->data;
896 ssize_t readed = 0;
898 if (data->connState == HTTP_CONN_STATE_REOPEN) {
899 if (initHTTPConnection(inStream) < 0)
900 return -1;
903 if (data->connState == HTTP_CONN_STATE_INIT) {
904 if (finishHTTPInit(inStream) < 0)
905 return -1;
908 if (data->connState == HTTP_CONN_STATE_HELLO) {
909 if (getHTTPHello(inStream) < 0)
910 return -1;
913 switch (data->connState) {
914 case HTTP_CONN_STATE_OPEN:
915 case HTTP_CONN_STATE_CLOSED:
916 break;
917 default:
918 return -1;
921 if (data->buflen == 0 || data->buflen < data->icyMetaint) {
922 data->prebuffer = 1;
923 } else if (data->buflen > prebufferSize)
924 data->prebuffer = 0;
926 if (data->connState == HTTP_CONN_STATE_OPEN &&
927 data->buflen < bufferSize - 1) {
928 readed = read(data->sock, data->buffer + data->buflen,
929 (size_t) (bufferSize - 1 - data->buflen));
931 if (readed < 0 && (errno == EAGAIN || errno == EINTR)) {
932 readed = 0;
933 } else if (readed <= 0) {
934 close(data->sock);
935 data->connState = HTTP_CONN_STATE_CLOSED;
936 readed = 0;
938 data->buflen += readed;
941 if (data->buflen > prebufferSize)
942 data->prebuffer = 0;
944 return (readed ? 1 : 0);