Handle streams separately in tree_add_track()
[cmus.git] / http.c
blob134954f6c9d6979112a0c3c15208b26a9f56061a
1 /*
2 * Copyright 2004-2005 Timo Hirvonen
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * 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
17 * 02111-1307, USA.
20 #include "http.h"
21 #include "file.h"
22 #include "debug.h"
23 #include "xmalloc.h"
24 #include "gbuf.h"
26 #include <unistd.h>
27 #include <fcntl.h>
28 #include <sys/types.h>
29 #include <sys/time.h>
30 #include <sys/socket.h>
31 #include <netinet/in.h>
32 #include <netdb.h>
33 #include <arpa/inet.h>
34 #include <string.h>
35 #include <errno.h>
38 * @uri is http://[user[:pass]@]host[:port][/path][?query]
40 * uri(7): If the URL supplies a user name but no password, and the remote
41 * server requests a password, the program interpreting the URL should request
42 * one from the user.
44 int http_parse_uri(const char *uri, struct http_uri *u)
46 const char *str, *colon, *at, *slash, *host_start;
48 /* initialize all fields */
49 u->user = NULL;
50 u->pass = NULL;
51 u->host = NULL;
52 u->path = NULL;
53 u->port = 80;
55 if (strncmp(uri, "http://", 7))
56 return -1;
57 str = uri + 7;
58 host_start = str;
60 /* [/path] */
61 slash = strchr(str, '/');
62 if (slash) {
63 u->path = xstrdup(slash);
64 } else {
65 u->path = xstrdup("/");
68 /* [user[:pass]@] */
69 at = strchr(str, '@');
70 if (at) {
71 /* user[:pass]@ */
72 host_start = at + 1;
73 colon = strchr(str, ':');
74 if (colon == NULL || colon > at) {
75 /* user */
76 u->user = xstrndup(str, at - str);
77 } else {
78 /* user:pass */
79 u->user = xstrndup(str, colon - str);
80 u->pass = xstrndup(colon + 1, at - (colon + 1));
84 /* host[:port] */
85 colon = strchr(host_start, ':');
86 if (colon) {
87 /* host:port */
88 const char *start;
89 int port;
91 u->host = xstrndup(host_start, colon - host_start);
92 colon++;
93 start = colon;
95 port = 0;
96 while (*colon >= '0' && *colon <= '9') {
97 port *= 10;
98 port += *colon - '0';
99 colon++;
101 u->port = port;
103 if (colon == start || (*colon != 0 && *colon != '/')) {
104 http_free_uri(u);
105 return -1;
107 } else {
108 /* host */
109 if (slash) {
110 u->host = xstrndup(host_start, slash - host_start);
111 } else {
112 u->host = xstrdup(host_start);
115 return 0;
118 void http_free_uri(struct http_uri *u)
120 free(u->user);
121 free(u->pass);
122 free(u->host);
123 free(u->path);
126 int http_open(struct http_get *hg, int timeout_ms)
128 struct hostent *hostent;
129 union {
130 struct sockaddr sa;
131 struct sockaddr_in in;
132 } addr;
133 struct timeval tv;
134 int save, flags;
136 hostent = gethostbyname(hg->uri.host);
137 if (hostent == NULL)
138 return -1;
139 addr.in.sin_family = AF_INET;
140 addr.in.sin_port = htons(hg->uri.port);
141 memcpy(&addr.in.sin_addr, hostent->h_addr_list[0], hostent->h_length);
143 hg->fd = socket(PF_INET, SOCK_STREAM, 0);
144 if (hg->fd == -1)
145 return -1;
147 flags = fcntl(hg->fd, F_GETFL);
148 if (fcntl(hg->fd, F_SETFL, O_NONBLOCK) == -1)
149 goto close_exit;
151 tv.tv_sec = timeout_ms / 1000;
152 tv.tv_usec = (timeout_ms % 1000) * 1000;
153 while (1) {
154 fd_set wfds;
156 d_print("connecting. timeout=%ld s %ld us\n", tv.tv_sec, tv.tv_usec);
157 if (connect(hg->fd, &addr.sa, sizeof(addr.in)) == 0)
158 break;
159 if (errno == EISCONN)
160 break;
161 if (errno != EAGAIN && errno != EINPROGRESS)
162 goto close_exit;
164 FD_ZERO(&wfds);
165 FD_SET(hg->fd, &wfds);
166 while (1) {
167 int rc;
169 rc = select(hg->fd + 1, NULL, &wfds, NULL, &tv);
170 if (rc == -1) {
171 if (errno != EINTR)
172 goto close_exit;
173 /* signalled */
174 continue;
176 if (rc == 1) {
177 /* socket ready */
178 break;
180 if (tv.tv_sec == 0 && tv.tv_usec == 0) {
181 errno = ETIMEDOUT;
182 goto close_exit;
187 /* restore old flags */
188 if (fcntl(hg->fd, F_SETFL, flags) == -1)
189 goto close_exit;
190 return 0;
191 close_exit:
192 save = errno;
193 close(hg->fd);
194 errno = save;
195 return -1;
198 static int http_write(int fd, const char *buf, int count, int timeout_ms)
200 struct timeval tv;
201 int pos = 0;
203 tv.tv_sec = timeout_ms / 1000;
204 tv.tv_usec = (timeout_ms % 1000) * 1000;
205 while (1) {
206 fd_set wfds;
207 int rc;
209 d_print("timeout=%ld s %ld us\n", tv.tv_sec, tv.tv_usec);
211 FD_ZERO(&wfds);
212 FD_SET(fd, &wfds);
213 rc = select(fd + 1, NULL, &wfds, NULL, &tv);
214 if (rc == -1) {
215 if (errno != EINTR)
216 return -1;
217 /* signalled */
218 continue;
220 if (rc == 1) {
221 rc = write(fd, buf + pos, count - pos);
222 if (rc == -1) {
223 if (errno == EINTR || errno == EAGAIN)
224 continue;
225 return -1;
227 pos += rc;
228 if (pos == count)
229 return 0;
230 } else if (tv.tv_sec == 0 && tv.tv_usec == 0) {
231 errno = ETIMEDOUT;
232 return -1;
237 static int read_timeout(int fd, int timeout_ms)
239 struct timeval tv;
241 tv.tv_sec = timeout_ms / 1000;
242 tv.tv_usec = (timeout_ms % 1000) * 1000;
243 while (1) {
244 fd_set rfds;
245 int rc;
247 FD_ZERO(&rfds);
248 FD_SET(fd, &rfds);
249 rc = select(fd + 1, &rfds, NULL, NULL, &tv);
250 if (rc == -1) {
251 if (errno != EINTR)
252 return -1;
253 /* signalled */
254 continue;
256 if (rc == 1)
257 return 0;
258 if (tv.tv_sec == 0 && tv.tv_usec == 0) {
259 errno = ETIMEDOUT;
260 return -1;
265 /* reads response, ignores fscking carriage returns */
266 static int http_read_response(int fd, struct gbuf *buf, int timeout_ms)
268 char prev = 0;
270 if (read_timeout(fd, timeout_ms))
271 return -1;
272 while (1) {
273 int rc;
274 char ch;
276 rc = read(fd, &ch, 1);
277 if (rc == -1) {
278 return -1;
280 if (rc == 0) {
281 return -2;
283 if (ch == '\r')
284 continue;
285 if (ch == '\n' && prev == '\n')
286 return 0;
287 gbuf_add_ch(buf, ch);
288 prev = ch;
292 static int http_parse_response(char *str, struct http_get *hg)
294 /* str is 0 terminated buffer of lines
295 * every line ends with '\n'
296 * no carriage returns
297 * no empty lines
299 GROWING_KEYVALS(h);
300 char *end;
302 if (strncmp(str, "HTTP/", 5) == 0) {
303 str += 5;
304 while (*str != ' ') {
305 if (*str == '\n') {
306 return -2;
308 str++;
310 } else if (strncmp(str, "ICY", 3) == 0) {
311 str += 3;
312 } else {
313 return -2;
315 while (*str == ' ')
316 str++;
318 hg->code = 0;
319 while (*str >= '0' && *str <= '9') {
320 hg->code *= 10;
321 hg->code += *str - '0';
322 str++;
324 if (!hg->code)
325 return -2;
326 while (*str == ' ')
327 str++;
329 end = strchr(str, '\n');
330 hg->reason = xstrndup(str, end - str);
331 str = end + 1;
333 /* headers */
334 while (*str) {
335 char *ptr;
337 end = strchr(str, '\n');
338 ptr = strchr(str, ':');
339 if (ptr == NULL || ptr > end) {
340 free(hg->reason);
341 hg->reason = NULL;
342 keyvals_terminate(&h);
343 keyvals_free(h.keyvals);
344 return -2;
347 *ptr++ = 0;
348 while (*ptr == ' ')
349 ptr++;
351 keyvals_add(&h, str, xstrndup(ptr, end - ptr));
352 str = end + 1;
354 keyvals_terminate(&h);
355 hg->headers = h.keyvals;
356 return 0;
359 int http_get(struct http_get *hg, struct keyval *headers, int timeout_ms)
361 GBUF(buf);
362 int i, rc, save;
364 gbuf_add_str(&buf, "GET ");
365 gbuf_add_str(&buf, hg->uri.path);
366 gbuf_add_str(&buf, " HTTP/1.0\r\n");
367 for (i = 0; headers[i].key; i++) {
368 gbuf_add_str(&buf, headers[i].key);
369 gbuf_add_str(&buf, ": ");
370 gbuf_add_str(&buf, headers[i].val);
371 gbuf_add_str(&buf, "\r\n");
373 gbuf_add_str(&buf, "\r\n");
375 rc = http_write(hg->fd, buf.buffer, buf.len, timeout_ms);
376 if (rc)
377 goto out;
379 gbuf_clear(&buf);
380 rc = http_read_response(hg->fd, &buf, timeout_ms);
381 if (rc)
382 goto out;
384 rc = http_parse_response(buf.buffer, hg);
385 out:
386 save = errno;
387 gbuf_free(&buf);
388 errno = save;
389 return rc;
392 char *http_read_body(int fd, size_t *size, int timeout_ms)
394 GBUF(buf);
396 if (read_timeout(fd, timeout_ms))
397 return NULL;
398 while (1) {
399 int count = 1023;
400 int rc;
402 gbuf_grow(&buf, count);
403 rc = read_all(fd, buf.buffer + buf.len, count);
404 if (rc == -1) {
405 gbuf_free(&buf);
406 return NULL;
408 buf.len += rc;
409 if (rc == 0) {
410 *size = buf.len;
411 return gbuf_steal(&buf);
416 void http_get_free(struct http_get *hg)
418 http_free_uri(&hg->uri);
419 if (hg->headers)
420 keyvals_free(hg->headers);
421 free(hg->reason);
424 char *base64_encode(const char *str)
426 static const char t[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
427 int str_len, buf_len, i, s, d;
428 char *buf;
429 unsigned char b0, b1, b2;
431 str_len = strlen(str);
432 buf_len = (str_len + 2) / 3 * 4 + 1;
433 buf = xnew(char, buf_len);
434 s = 0;
435 d = 0;
436 for (i = 0; i < str_len / 3; i++) {
437 b0 = str[s++];
438 b1 = str[s++];
439 b2 = str[s++];
441 /* 6 ms bits of b0 */
442 buf[d++] = t[b0 >> 2];
444 /* 2 ls bits of b0 . 4 ms bits of b1 */
445 buf[d++] = t[((b0 << 4) | (b1 >> 4)) & 0x3f];
447 /* 4 ls bits of b1 . 2 ms bits of b2 */
448 buf[d++] = t[((b1 << 2) | (b2 >> 6)) & 0x3f];
450 /* 6 ls bits of b2 */
451 buf[d++] = t[b2 & 0x3f];
453 switch (str_len % 3) {
454 case 2:
455 b0 = str[s++];
456 b1 = str[s++];
458 /* 6 ms bits of b0 */
459 buf[d++] = t[b0 >> 2];
461 /* 2 ls bits of b0 . 4 ms bits of b1 */
462 buf[d++] = t[((b0 << 4) | (b1 >> 4)) & 0x3f];
464 /* 4 ls bits of b1 */
465 buf[d++] = t[(b1 << 2) & 0x3f];
467 buf[d++] = '=';
468 break;
469 case 1:
470 b0 = str[s++];
472 /* 6 ms bits of b0 */
473 buf[d++] = t[b0 >> 2];
475 /* 2 ls bits of b0 */
476 buf[d++] = t[(b0 << 4) & 0x3f];
478 buf[d++] = '=';
479 buf[d++] = '=';
480 break;
481 case 0:
482 break;
484 buf[d++] = 0;
485 return buf;