build system: Separate export and setting variable
[cmus.git] / http.c
blobe8f1b320a3ff54cbac98bdf33a11d391d6b5bb6c
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 != EAGAIN && errno != EINPROGRESS)
189 goto close_exit;
191 FD_ZERO(&wfds);
192 FD_SET(sock, &wfds);
193 while (1) {
194 int rc;
196 rc = select(sock + 1, NULL, &wfds, NULL, &tv);
197 if (rc == -1) {
198 if (errno != EINTR)
199 goto close_exit;
200 /* signalled */
201 continue;
203 if (rc == 1) {
204 /* socket ready */
205 break;
207 if (tv.tv_sec == 0 && tv.tv_usec == 0) {
208 errno = ETIMEDOUT;
209 goto close_exit;
214 /* restore old flags */
215 if (fcntl(sock, F_SETFL, flags) == -1)
216 goto close_exit;
217 return sock;
218 close_exit:
219 save = errno;
220 close(sock);
221 errno = save;
222 return -1;
225 static int http_write(int fd, const char *buf, int count, int timeout_ms)
227 struct timeval tv;
228 int pos = 0;
230 tv.tv_sec = timeout_ms / 1000;
231 tv.tv_usec = (timeout_ms % 1000) * 1000;
232 while (1) {
233 fd_set wfds;
234 int rc;
236 d_print("timeout=%ld s %ld us\n", tv.tv_sec, tv.tv_usec);
238 FD_ZERO(&wfds);
239 FD_SET(fd, &wfds);
240 rc = select(fd + 1, NULL, &wfds, NULL, &tv);
241 if (rc == -1) {
242 if (errno != EINTR)
243 return -1;
244 /* signalled */
245 continue;
247 if (rc == 1) {
248 rc = write(fd, buf + pos, count - pos);
249 if (rc == -1) {
250 if (errno == EINTR || errno == EAGAIN)
251 continue;
252 return -1;
254 pos += rc;
255 if (pos == count)
256 return 0;
257 } else if (tv.tv_sec == 0 && tv.tv_usec == 0) {
258 errno = ETIMEDOUT;
259 return -1;
264 static int read_timeout(int fd, int timeout_ms)
266 struct timeval tv;
268 tv.tv_sec = timeout_ms / 1000;
269 tv.tv_usec = (timeout_ms % 1000) * 1000;
270 while (1) {
271 fd_set rfds;
272 int rc;
274 FD_ZERO(&rfds);
275 FD_SET(fd, &rfds);
276 rc = select(fd + 1, &rfds, NULL, NULL, &tv);
277 if (rc == -1) {
278 if (errno != EINTR)
279 return -1;
280 /* signalled */
281 continue;
283 if (rc == 1)
284 return 0;
285 if (tv.tv_sec == 0 && tv.tv_usec == 0) {
286 errno = ETIMEDOUT;
287 return -1;
292 /* reads response, ignores fscking carriage returns */
293 static int http_read_response(int fd, char **bufp, int *sizep, int *posp, int timeout_ms)
295 if (read_timeout(fd, timeout_ms))
296 return -1;
297 while (1) {
298 char *buf = *bufp;
299 int pos = *posp;
300 int rc;
301 char ch;
303 rc = read(fd, &ch, 1);
304 if (rc == -1) {
305 return -1;
307 if (rc == 0) {
308 return -2;
310 if (ch == '\r')
311 continue;
312 if (ch == '\n' && pos > 0 && buf[pos - 1] == '\n') {
313 buf_write_ch(bufp, sizep, posp, 0);
314 return 0;
316 buf_write_ch(bufp, sizep, posp, ch);
320 static int http_parse_response(const char *str, int *codep, char **reasonp, struct http_header **hp)
322 /* str is 0 terminated buffer of lines
323 * every line ends with '\n'
324 * no carriage returns
325 * no empty lines
327 const char *end;
328 char *reason;
329 int code, i, count;
330 struct http_header *h;
332 *codep = -1;
333 *reasonp = NULL;
334 *hp = NULL;
336 if (strncmp(str, "HTTP/", 5) == 0) {
337 str += 5;
338 while (*str != ' ') {
339 if (*str == '\n') {
340 return -2;
342 str++;
344 } else if (strncmp(str, "ICY", 3) == 0) {
345 str += 3;
346 } else {
347 return -2;
349 while (*str == ' ')
350 str++;
352 code = 0;
353 for (i = 0; i < 3; i++) {
354 if (*str < '0' || *str > '9') {
355 return -2;
357 code *= 10;
358 code += *str - '0';
359 str++;
361 while (*str == ' ')
362 str++;
364 end = strchr(str, '\n');
365 reason = xstrndup(str, end - str);
366 str = end + 1;
368 /* headers */
369 count = 4;
370 h = xnew(struct http_header, count);
371 i = 0;
372 while (*str) {
373 const char *ptr;
375 if (i == count - 1) {
376 count *= 2;
377 h = xrenew(struct http_header, h, count);
380 end = strchr(str, '\n');
381 ptr = strchr(str, ':');
382 if (ptr == NULL || ptr > end) {
383 int j;
385 for (j = 0; j < i; j++) {
386 free(h[j].key);
387 free(h[j].val);
389 free(h);
390 free(reason);
391 *reasonp = NULL;
392 return -2;
394 h[i].key = xstrndup(str, ptr - str);
395 ptr++;
396 while (*ptr == ' ')
397 ptr++;
398 h[i].val = xstrndup(ptr, end - ptr);
399 i++;
400 str = end + 1;
402 h[i].key = NULL;
403 h[i].val = NULL;
404 *codep = code;
405 *reasonp = reason;
406 *hp = h;
407 return 0;
410 int http_get(int fd, const char *path, struct http_header *headers,
411 int *codep, char **reasonp, struct http_header **ret_headersp,
412 int timeout_ms)
414 char *buf = NULL;
415 int size = 0;
416 int pos = 0;
417 int i, rc, save;
419 buf_write(&buf, &size, &pos, "GET ");
420 buf_write(&buf, &size, &pos, path);
421 buf_write(&buf, &size, &pos, " HTTP/1.0\r\n");
422 for (i = 0; headers[i].key; i++) {
423 buf_write(&buf, &size, &pos, headers[i].key);
424 buf_write(&buf, &size, &pos, ": ");
425 buf_write(&buf, &size, &pos, headers[i].val);
426 buf_write(&buf, &size, &pos, "\r\n");
428 buf_write(&buf, &size, &pos, "\r\n");
430 rc = http_write(fd, buf, pos, timeout_ms);
431 if (rc)
432 goto out;
434 pos = 0;
435 rc = http_read_response(fd, &buf, &size, &pos, timeout_ms);
436 if (rc)
437 goto out;
439 rc = http_parse_response(buf, codep, reasonp, ret_headersp);
440 out:
441 save = errno;
442 free(buf);
443 errno = save;
444 return rc;
447 int http_read_body(int fd, char **bodyp, int timeout_ms)
449 char *body = NULL;
450 int size = 0;
451 int pos = 0;
453 *bodyp = NULL;
454 if (read_timeout(fd, timeout_ms))
455 return -1;
456 while (1) {
457 int rc;
459 buf_ensure_space(&body, &size, &pos, 256);
460 rc = read_all(fd, body + pos, size - pos);
461 if (rc == -1) {
462 free(body);
463 return -1;
465 if (rc == 0) {
466 buf_ensure_space(&body, &size, &pos, 1);
467 body[pos] = 0;
468 *bodyp = body;
469 return 0;
471 pos += rc;
475 const char *http_headers_get_value(const struct http_header *headers, const char *key)
477 int i;
479 for (i = 0; headers[i].key; i++) {
480 if (strcasecmp(headers[i].key, key) == 0)
481 return headers[i].val;
483 return NULL;
486 void http_headers_free(struct http_header *headers)
488 int i;
490 for (i = 0; headers[i].key; i++) {
491 free(headers[i].key);
492 free(headers[i].val);
494 free(headers);
497 char *base64_encode(const char *str)
499 static const char t[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
500 int str_len, buf_len, i, s, d;
501 char *buf;
502 unsigned char b0, b1, b2;
504 str_len = strlen(str);
505 buf_len = (str_len + 2) / 3 * 4 + 1;
506 buf = xnew(char, buf_len);
507 s = 0;
508 d = 0;
509 for (i = 0; i < str_len / 3; i++) {
510 b0 = str[s++];
511 b1 = str[s++];
512 b2 = str[s++];
514 /* 6 ms bits of b0 */
515 buf[d++] = t[b0 >> 2];
517 /* 2 ls bits of b0 . 4 ms bits of b1 */
518 buf[d++] = t[((b0 << 4) | (b1 >> 4)) & 0x3f];
520 /* 4 ls bits of b1 . 2 ms bits of b2 */
521 buf[d++] = t[((b1 << 2) | (b2 >> 6)) & 0x3f];
523 /* 6 ls bits of b2 */
524 buf[d++] = t[b2 & 0x3f];
526 switch (str_len % 3) {
527 case 2:
528 b0 = str[s++];
529 b1 = str[s++];
531 /* 6 ms bits of b0 */
532 buf[d++] = t[b0 >> 2];
534 /* 2 ls bits of b0 . 4 ms bits of b1 */
535 buf[d++] = t[((b0 << 4) | (b1 >> 4)) & 0x3f];
537 /* 4 ls bits of b1 */
538 buf[d++] = t[(b1 << 2) & 0x3f];
540 buf[d++] = '=';
541 break;
542 case 1:
543 b0 = str[s++];
545 /* 6 ms bits of b0 */
546 buf[d++] = t[b0 >> 2];
548 /* 2 ls bits of b0 */
549 buf[d++] = t[(b0 << 4) & 0x3f];
551 buf[d++] = '=';
552 buf[d++] = '=';
553 break;
554 case 0:
555 break;
557 buf[d++] = 0;
558 return buf;