build system: Mark clean and distclean .NOTPARALLEL
[cmus.git] / http.c
blobe6176b08ce70d97a0bed3c804126240c5aa4e8e8
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"
25 #include <unistd.h>
26 #include <fcntl.h>
27 #include <sys/types.h>
28 #include <sys/time.h>
29 #include <sys/socket.h>
30 #include <netinet/in.h>
31 #include <netdb.h>
32 #include <arpa/inet.h>
33 #include <string.h>
34 #include <errno.h>
36 static void buf_ensure_space(char **bufp, int *sizep, int *posp, int len)
38 int size = *sizep;
39 int pos = *posp;
41 if (size - pos < len) {
42 if (size == 0)
43 size = 128;
44 while (size - pos < len)
45 size *= 2;
46 *bufp = xrenew(char, *bufp, size);
47 *sizep = size;
51 static void buf_write(char **bufp, int *sizep, int *posp, const char *str)
53 int len = strlen(str);
55 buf_ensure_space(bufp, sizep, posp, len);
56 memcpy(*bufp + *posp, str, len);
57 *posp += len;
60 static void buf_write_ch(char **bufp, int *sizep, int *posp, char ch)
62 buf_ensure_space(bufp, sizep, posp, 1);
63 (*bufp)[*posp] = ch;
64 *posp += 1;
68 * @uri is http://[user[:pass]@]host[:port][/path][?query]
70 * uri(7): If the URL supplies a user name but no password, and the remote
71 * server requests a password, the program interpreting the URL should request
72 * one from the user.
74 int http_parse_uri(const char *uri, struct http_uri *u)
76 const char *str, *colon, *at, *slash, *host_start;
78 /* initialize all fields */
79 u->user = NULL;
80 u->pass = NULL;
81 u->host = NULL;
82 u->path = NULL;
83 u->port = 80;
85 if (strncmp(uri, "http://", 7))
86 return -1;
87 str = uri + 7;
88 host_start = str;
90 /* [/path] */
91 slash = strchr(str, '/');
92 if (slash) {
93 u->path = xstrdup(slash);
94 } else {
95 u->path = xstrdup("/");
98 /* [user[:pass]@] */
99 at = strchr(str, '@');
100 if (at) {
101 /* user[:pass]@ */
102 host_start = at + 1;
103 colon = strchr(str, ':');
104 if (colon == NULL || colon > at) {
105 /* user */
106 u->user = xstrndup(str, at - str);
107 } else {
108 /* user:pass */
109 u->user = xstrndup(str, colon - str);
110 u->pass = xstrndup(colon + 1, at - (colon + 1));
114 /* host[:port] */
115 colon = strchr(host_start, ':');
116 if (colon) {
117 /* host:port */
118 const char *start;
119 int port;
121 u->host = xstrndup(host_start, colon - host_start);
122 colon++;
123 start = colon;
125 port = 0;
126 while (*colon >= '0' && *colon <= '9') {
127 port *= 10;
128 port += *colon - '0';
129 colon++;
131 u->port = port;
133 if (colon == start || (*colon != 0 && *colon != '/')) {
134 http_free_uri(u);
135 return -1;
137 } else {
138 /* host */
139 if (slash) {
140 u->host = xstrndup(host_start, slash - host_start);
141 } else {
142 u->host = xstrdup(host_start);
145 return 0;
148 void http_free_uri(struct http_uri *u)
150 free(u->user);
151 free(u->pass);
152 free(u->host);
153 free(u->path);
156 int http_open(const char *hostname, unsigned int port, int timeout_ms)
158 struct hostent *hostent;
159 struct sockaddr_in addr;
160 struct timeval tv;
161 int sock, save, flags;
163 hostent = gethostbyname(hostname);
164 if (hostent == NULL)
165 return -1;
166 if (hostent->h_length > sizeof(addr.sin_addr))
167 hostent->h_length = sizeof(addr.sin_addr);
168 addr.sin_family = AF_INET;
169 addr.sin_port = htons(port);
170 memcpy(&addr.sin_addr, hostent->h_addr_list[0], hostent->h_length);
172 sock = socket(PF_INET, SOCK_STREAM, 0);
173 if (sock == -1)
174 return -1;
176 flags = fcntl(sock, F_GETFL);
177 if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1)
178 goto close_exit;
180 tv.tv_sec = timeout_ms / 1000;
181 tv.tv_usec = (timeout_ms % 1000) * 1000;
182 while (1) {
183 fd_set wfds;
185 d_print("connecting. timeout=%ld s %ld us\n", tv.tv_sec, tv.tv_usec);
186 if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) == 0)
187 break;
188 if (errno == EISCONN)
189 break;
190 if (errno != EAGAIN && errno != EINPROGRESS)
191 goto close_exit;
193 FD_ZERO(&wfds);
194 FD_SET(sock, &wfds);
195 while (1) {
196 int rc;
198 rc = select(sock + 1, NULL, &wfds, NULL, &tv);
199 if (rc == -1) {
200 if (errno != EINTR)
201 goto close_exit;
202 /* signalled */
203 continue;
205 if (rc == 1) {
206 /* socket ready */
207 break;
209 if (tv.tv_sec == 0 && tv.tv_usec == 0) {
210 errno = ETIMEDOUT;
211 goto close_exit;
216 /* restore old flags */
217 if (fcntl(sock, F_SETFL, flags) == -1)
218 goto close_exit;
219 return sock;
220 close_exit:
221 save = errno;
222 close(sock);
223 errno = save;
224 return -1;
227 static int http_write(int fd, const char *buf, int count, int timeout_ms)
229 struct timeval tv;
230 int pos = 0;
232 tv.tv_sec = timeout_ms / 1000;
233 tv.tv_usec = (timeout_ms % 1000) * 1000;
234 while (1) {
235 fd_set wfds;
236 int rc;
238 d_print("timeout=%ld s %ld us\n", tv.tv_sec, tv.tv_usec);
240 FD_ZERO(&wfds);
241 FD_SET(fd, &wfds);
242 rc = select(fd + 1, NULL, &wfds, NULL, &tv);
243 if (rc == -1) {
244 if (errno != EINTR)
245 return -1;
246 /* signalled */
247 continue;
249 if (rc == 1) {
250 rc = write(fd, buf + pos, count - pos);
251 if (rc == -1) {
252 if (errno == EINTR || errno == EAGAIN)
253 continue;
254 return -1;
256 pos += rc;
257 if (pos == count)
258 return 0;
259 } else if (tv.tv_sec == 0 && tv.tv_usec == 0) {
260 errno = ETIMEDOUT;
261 return -1;
266 static int read_timeout(int fd, int timeout_ms)
268 struct timeval tv;
270 tv.tv_sec = timeout_ms / 1000;
271 tv.tv_usec = (timeout_ms % 1000) * 1000;
272 while (1) {
273 fd_set rfds;
274 int rc;
276 FD_ZERO(&rfds);
277 FD_SET(fd, &rfds);
278 rc = select(fd + 1, &rfds, NULL, NULL, &tv);
279 if (rc == -1) {
280 if (errno != EINTR)
281 return -1;
282 /* signalled */
283 continue;
285 if (rc == 1)
286 return 0;
287 if (tv.tv_sec == 0 && tv.tv_usec == 0) {
288 errno = ETIMEDOUT;
289 return -1;
294 /* reads response, ignores fscking carriage returns */
295 static int http_read_response(int fd, char **bufp, int *sizep, int *posp, int timeout_ms)
297 if (read_timeout(fd, timeout_ms))
298 return -1;
299 while (1) {
300 char *buf = *bufp;
301 int pos = *posp;
302 int rc;
303 char ch;
305 rc = read(fd, &ch, 1);
306 if (rc == -1) {
307 return -1;
309 if (rc == 0) {
310 return -2;
312 if (ch == '\r')
313 continue;
314 if (ch == '\n' && pos > 0 && buf[pos - 1] == '\n') {
315 buf_write_ch(bufp, sizep, posp, 0);
316 return 0;
318 buf_write_ch(bufp, sizep, posp, ch);
322 static int http_parse_response(const char *str, int *codep, char **reasonp, struct http_header **hp)
324 /* str is 0 terminated buffer of lines
325 * every line ends with '\n'
326 * no carriage returns
327 * no empty lines
329 const char *end;
330 char *reason;
331 int code, i, count;
332 struct http_header *h;
334 *codep = -1;
335 *reasonp = NULL;
336 *hp = NULL;
338 if (strncmp(str, "HTTP/", 5) == 0) {
339 str += 5;
340 while (*str != ' ') {
341 if (*str == '\n') {
342 return -2;
344 str++;
346 } else if (strncmp(str, "ICY", 3) == 0) {
347 str += 3;
348 } else {
349 return -2;
351 while (*str == ' ')
352 str++;
354 code = 0;
355 for (i = 0; i < 3; i++) {
356 if (*str < '0' || *str > '9') {
357 return -2;
359 code *= 10;
360 code += *str - '0';
361 str++;
363 while (*str == ' ')
364 str++;
366 end = strchr(str, '\n');
367 reason = xstrndup(str, end - str);
368 str = end + 1;
370 /* headers */
371 count = 4;
372 h = xnew(struct http_header, count);
373 i = 0;
374 while (*str) {
375 const char *ptr;
377 if (i == count - 1) {
378 count *= 2;
379 h = xrenew(struct http_header, h, count);
382 end = strchr(str, '\n');
383 ptr = strchr(str, ':');
384 if (ptr == NULL || ptr > end) {
385 int j;
387 for (j = 0; j < i; j++) {
388 free(h[j].key);
389 free(h[j].val);
391 free(h);
392 free(reason);
393 *reasonp = NULL;
394 return -2;
396 h[i].key = xstrndup(str, ptr - str);
397 ptr++;
398 while (*ptr == ' ')
399 ptr++;
400 h[i].val = xstrndup(ptr, end - ptr);
401 i++;
402 str = end + 1;
404 h[i].key = NULL;
405 h[i].val = NULL;
406 *codep = code;
407 *reasonp = reason;
408 *hp = h;
409 return 0;
412 int http_get(int fd, const char *path, struct http_header *headers,
413 int *codep, char **reasonp, struct http_header **ret_headersp,
414 int timeout_ms)
416 char *buf = NULL;
417 int size = 0;
418 int pos = 0;
419 int i, rc, save;
421 buf_write(&buf, &size, &pos, "GET ");
422 buf_write(&buf, &size, &pos, path);
423 buf_write(&buf, &size, &pos, " HTTP/1.0\r\n");
424 for (i = 0; headers[i].key; i++) {
425 buf_write(&buf, &size, &pos, headers[i].key);
426 buf_write(&buf, &size, &pos, ": ");
427 buf_write(&buf, &size, &pos, headers[i].val);
428 buf_write(&buf, &size, &pos, "\r\n");
430 buf_write(&buf, &size, &pos, "\r\n");
432 rc = http_write(fd, buf, pos, timeout_ms);
433 if (rc)
434 goto out;
436 pos = 0;
437 rc = http_read_response(fd, &buf, &size, &pos, timeout_ms);
438 if (rc)
439 goto out;
441 rc = http_parse_response(buf, codep, reasonp, ret_headersp);
442 out:
443 save = errno;
444 free(buf);
445 errno = save;
446 return rc;
449 int http_read_body(int fd, char **bodyp, int timeout_ms)
451 char *body = NULL;
452 int size = 0;
453 int pos = 0;
455 *bodyp = NULL;
456 if (read_timeout(fd, timeout_ms))
457 return -1;
458 while (1) {
459 int rc;
461 buf_ensure_space(&body, &size, &pos, 256);
462 rc = read_all(fd, body + pos, size - pos);
463 if (rc == -1) {
464 free(body);
465 return -1;
467 if (rc == 0) {
468 buf_ensure_space(&body, &size, &pos, 1);
469 body[pos] = 0;
470 *bodyp = body;
471 return 0;
473 pos += rc;
477 const char *http_headers_get_value(const struct http_header *headers, const char *key)
479 int i;
481 for (i = 0; headers[i].key; i++) {
482 if (strcasecmp(headers[i].key, key) == 0)
483 return headers[i].val;
485 return NULL;
488 void http_headers_free(struct http_header *headers)
490 int i;
492 for (i = 0; headers[i].key; i++) {
493 free(headers[i].key);
494 free(headers[i].val);
496 free(headers);
499 char *base64_encode(const char *str)
501 static const char t[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
502 int str_len, buf_len, i, s, d;
503 char *buf;
504 unsigned char b0, b1, b2;
506 str_len = strlen(str);
507 buf_len = (str_len + 2) / 3 * 4 + 1;
508 buf = xnew(char, buf_len);
509 s = 0;
510 d = 0;
511 for (i = 0; i < str_len / 3; i++) {
512 b0 = str[s++];
513 b1 = str[s++];
514 b2 = str[s++];
516 /* 6 ms bits of b0 */
517 buf[d++] = t[b0 >> 2];
519 /* 2 ls bits of b0 . 4 ms bits of b1 */
520 buf[d++] = t[((b0 << 4) | (b1 >> 4)) & 0x3f];
522 /* 4 ls bits of b1 . 2 ms bits of b2 */
523 buf[d++] = t[((b1 << 2) | (b2 >> 6)) & 0x3f];
525 /* 6 ls bits of b2 */
526 buf[d++] = t[b2 & 0x3f];
528 switch (str_len % 3) {
529 case 2:
530 b0 = str[s++];
531 b1 = str[s++];
533 /* 6 ms bits of b0 */
534 buf[d++] = t[b0 >> 2];
536 /* 2 ls bits of b0 . 4 ms bits of b1 */
537 buf[d++] = t[((b0 << 4) | (b1 >> 4)) & 0x3f];
539 /* 4 ls bits of b1 */
540 buf[d++] = t[(b1 << 2) & 0x3f];
542 buf[d++] = '=';
543 break;
544 case 1:
545 b0 = str[s++];
547 /* 6 ms bits of b0 */
548 buf[d++] = t[b0 >> 2];
550 /* 2 ls bits of b0 */
551 buf[d++] = t[(b0 << 4) & 0x3f];
553 buf[d++] = '=';
554 buf[d++] = '=';
555 break;
556 case 0:
557 break;
559 buf[d++] = 0;
560 return buf;