Small clean up to http_open()
[cmus.git] / http.c
blobeadd1c0ef8192bc50cb1afdf59f7f5e50d2160e1
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(struct http_get *hg, int timeout_ms)
158 struct hostent *hostent;
159 union {
160 struct sockaddr sa;
161 struct sockaddr_in in;
162 } addr;
163 struct timeval tv;
164 int save, flags;
166 hostent = gethostbyname(hg->uri.host);
167 if (hostent == NULL)
168 return -1;
169 addr.in.sin_family = AF_INET;
170 addr.in.sin_port = htons(hg->uri.port);
171 memcpy(&addr.in.sin_addr, hostent->h_addr_list[0], hostent->h_length);
173 hg->fd = socket(PF_INET, SOCK_STREAM, 0);
174 if (hg->fd == -1)
175 return -1;
177 flags = fcntl(hg->fd, F_GETFL);
178 if (fcntl(hg->fd, F_SETFL, O_NONBLOCK) == -1)
179 goto close_exit;
181 tv.tv_sec = timeout_ms / 1000;
182 tv.tv_usec = (timeout_ms % 1000) * 1000;
183 while (1) {
184 fd_set wfds;
186 d_print("connecting. timeout=%ld s %ld us\n", tv.tv_sec, tv.tv_usec);
187 if (connect(hg->fd, &addr.sa, sizeof(addr.in)) == 0)
188 break;
189 if (errno == EISCONN)
190 break;
191 if (errno != EAGAIN && errno != EINPROGRESS)
192 goto close_exit;
194 FD_ZERO(&wfds);
195 FD_SET(hg->fd, &wfds);
196 while (1) {
197 int rc;
199 rc = select(hg->fd + 1, NULL, &wfds, NULL, &tv);
200 if (rc == -1) {
201 if (errno != EINTR)
202 goto close_exit;
203 /* signalled */
204 continue;
206 if (rc == 1) {
207 /* socket ready */
208 break;
210 if (tv.tv_sec == 0 && tv.tv_usec == 0) {
211 errno = ETIMEDOUT;
212 goto close_exit;
217 /* restore old flags */
218 if (fcntl(hg->fd, F_SETFL, flags) == -1)
219 goto close_exit;
220 return 0;
221 close_exit:
222 save = errno;
223 close(hg->fd);
224 errno = save;
225 return -1;
228 static int http_write(int fd, const char *buf, int count, int timeout_ms)
230 struct timeval tv;
231 int pos = 0;
233 tv.tv_sec = timeout_ms / 1000;
234 tv.tv_usec = (timeout_ms % 1000) * 1000;
235 while (1) {
236 fd_set wfds;
237 int rc;
239 d_print("timeout=%ld s %ld us\n", tv.tv_sec, tv.tv_usec);
241 FD_ZERO(&wfds);
242 FD_SET(fd, &wfds);
243 rc = select(fd + 1, NULL, &wfds, NULL, &tv);
244 if (rc == -1) {
245 if (errno != EINTR)
246 return -1;
247 /* signalled */
248 continue;
250 if (rc == 1) {
251 rc = write(fd, buf + pos, count - pos);
252 if (rc == -1) {
253 if (errno == EINTR || errno == EAGAIN)
254 continue;
255 return -1;
257 pos += rc;
258 if (pos == count)
259 return 0;
260 } else if (tv.tv_sec == 0 && tv.tv_usec == 0) {
261 errno = ETIMEDOUT;
262 return -1;
267 static int read_timeout(int fd, int timeout_ms)
269 struct timeval tv;
271 tv.tv_sec = timeout_ms / 1000;
272 tv.tv_usec = (timeout_ms % 1000) * 1000;
273 while (1) {
274 fd_set rfds;
275 int rc;
277 FD_ZERO(&rfds);
278 FD_SET(fd, &rfds);
279 rc = select(fd + 1, &rfds, NULL, NULL, &tv);
280 if (rc == -1) {
281 if (errno != EINTR)
282 return -1;
283 /* signalled */
284 continue;
286 if (rc == 1)
287 return 0;
288 if (tv.tv_sec == 0 && tv.tv_usec == 0) {
289 errno = ETIMEDOUT;
290 return -1;
295 /* reads response, ignores fscking carriage returns */
296 static int http_read_response(int fd, char **bufp, int *sizep, int *posp, int timeout_ms)
298 if (read_timeout(fd, timeout_ms))
299 return -1;
300 while (1) {
301 char *buf = *bufp;
302 int pos = *posp;
303 int rc;
304 char ch;
306 rc = read(fd, &ch, 1);
307 if (rc == -1) {
308 return -1;
310 if (rc == 0) {
311 return -2;
313 if (ch == '\r')
314 continue;
315 if (ch == '\n' && pos > 0 && buf[pos - 1] == '\n') {
316 buf_write_ch(bufp, sizep, posp, 0);
317 return 0;
319 buf_write_ch(bufp, sizep, posp, ch);
323 static int http_parse_response(const char *str, int *codep, char **reasonp, struct keyval **hp)
325 /* str is 0 terminated buffer of lines
326 * every line ends with '\n'
327 * no carriage returns
328 * no empty lines
330 const char *end;
331 char *reason;
332 int code, i, count;
333 struct keyval *h;
335 *codep = -1;
336 *reasonp = NULL;
337 *hp = NULL;
339 if (strncmp(str, "HTTP/", 5) == 0) {
340 str += 5;
341 while (*str != ' ') {
342 if (*str == '\n') {
343 return -2;
345 str++;
347 } else if (strncmp(str, "ICY", 3) == 0) {
348 str += 3;
349 } else {
350 return -2;
352 while (*str == ' ')
353 str++;
355 code = 0;
356 for (i = 0; i < 3; i++) {
357 if (*str < '0' || *str > '9') {
358 return -2;
360 code *= 10;
361 code += *str - '0';
362 str++;
364 while (*str == ' ')
365 str++;
367 end = strchr(str, '\n');
368 reason = xstrndup(str, end - str);
369 str = end + 1;
371 /* headers */
372 count = 4;
373 h = xnew(struct keyval, count);
374 i = 0;
375 while (*str) {
376 const char *ptr;
378 if (i == count - 1) {
379 count *= 2;
380 h = xrenew(struct keyval, h, count);
383 end = strchr(str, '\n');
384 ptr = strchr(str, ':');
385 if (ptr == NULL || ptr > end) {
386 int j;
388 for (j = 0; j < i; j++) {
389 free(h[j].key);
390 free(h[j].val);
392 free(h);
393 free(reason);
394 *reasonp = NULL;
395 return -2;
397 h[i].key = xstrndup(str, ptr - str);
398 ptr++;
399 while (*ptr == ' ')
400 ptr++;
401 h[i].val = xstrndup(ptr, end - ptr);
402 i++;
403 str = end + 1;
405 h[i].key = NULL;
406 h[i].val = NULL;
407 *codep = code;
408 *reasonp = reason;
409 *hp = h;
410 return 0;
413 int http_get(struct http_get *hg, struct keyval *headers, int timeout_ms)
415 char *buf = NULL;
416 int size = 0;
417 int pos = 0;
418 int i, rc, save;
420 buf_write(&buf, &size, &pos, "GET ");
421 buf_write(&buf, &size, &pos, hg->uri.path);
422 buf_write(&buf, &size, &pos, " HTTP/1.0\r\n");
423 for (i = 0; headers[i].key; i++) {
424 buf_write(&buf, &size, &pos, headers[i].key);
425 buf_write(&buf, &size, &pos, ": ");
426 buf_write(&buf, &size, &pos, headers[i].val);
427 buf_write(&buf, &size, &pos, "\r\n");
429 buf_write(&buf, &size, &pos, "\r\n");
431 rc = http_write(hg->fd, buf, pos, timeout_ms);
432 if (rc)
433 goto out;
435 pos = 0;
436 rc = http_read_response(hg->fd, &buf, &size, &pos, timeout_ms);
437 if (rc)
438 goto out;
440 rc = http_parse_response(buf, &hg->code, &hg->reason, &hg->headers);
441 out:
442 save = errno;
443 free(buf);
444 errno = save;
445 return rc;
448 int http_read_body(int fd, char **bodyp, int timeout_ms)
450 char *body = NULL;
451 int size = 0;
452 int pos = 0;
454 *bodyp = NULL;
455 if (read_timeout(fd, timeout_ms))
456 return -1;
457 while (1) {
458 int rc;
460 buf_ensure_space(&body, &size, &pos, 256);
461 rc = read_all(fd, body + pos, size - pos);
462 if (rc == -1) {
463 free(body);
464 return -1;
466 if (rc == 0) {
467 buf_ensure_space(&body, &size, &pos, 1);
468 body[pos] = 0;
469 *bodyp = body;
470 return 0;
472 pos += rc;
476 void http_get_free(struct http_get *hg)
478 http_free_uri(&hg->uri);
479 keyvals_free(hg->headers);
480 free(hg->reason);
483 char *base64_encode(const char *str)
485 static const char t[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
486 int str_len, buf_len, i, s, d;
487 char *buf;
488 unsigned char b0, b1, b2;
490 str_len = strlen(str);
491 buf_len = (str_len + 2) / 3 * 4 + 1;
492 buf = xnew(char, buf_len);
493 s = 0;
494 d = 0;
495 for (i = 0; i < str_len / 3; i++) {
496 b0 = str[s++];
497 b1 = str[s++];
498 b2 = str[s++];
500 /* 6 ms bits of b0 */
501 buf[d++] = t[b0 >> 2];
503 /* 2 ls bits of b0 . 4 ms bits of b1 */
504 buf[d++] = t[((b0 << 4) | (b1 >> 4)) & 0x3f];
506 /* 4 ls bits of b1 . 2 ms bits of b2 */
507 buf[d++] = t[((b1 << 2) | (b2 >> 6)) & 0x3f];
509 /* 6 ls bits of b2 */
510 buf[d++] = t[b2 & 0x3f];
512 switch (str_len % 3) {
513 case 2:
514 b0 = str[s++];
515 b1 = str[s++];
517 /* 6 ms bits of b0 */
518 buf[d++] = t[b0 >> 2];
520 /* 2 ls bits of b0 . 4 ms bits of b1 */
521 buf[d++] = t[((b0 << 4) | (b1 >> 4)) & 0x3f];
523 /* 4 ls bits of b1 */
524 buf[d++] = t[(b1 << 2) & 0x3f];
526 buf[d++] = '=';
527 break;
528 case 1:
529 b0 = str[s++];
531 /* 6 ms bits of b0 */
532 buf[d++] = t[b0 >> 2];
534 /* 2 ls bits of b0 */
535 buf[d++] = t[(b0 << 4) & 0x3f];
537 buf[d++] = '=';
538 buf[d++] = '=';
539 break;
540 case 0:
541 break;
543 buf[d++] = 0;
544 return buf;