2 * Copyright 2004-2005 Timo Hirvonen
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
28 #include <sys/types.h>
30 #include <sys/socket.h>
31 #include <netinet/in.h>
33 #include <arpa/inet.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
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 */
55 if (strncmp(uri
, "http://", 7))
61 slash
= strchr(str
, '/');
63 u
->path
= xstrdup(slash
);
65 u
->path
= xstrdup("/");
69 at
= strchr(str
, '@');
73 colon
= strchr(str
, ':');
74 if (colon
== NULL
|| colon
> at
) {
76 u
->user
= xstrndup(str
, at
- str
);
79 u
->user
= xstrndup(str
, colon
- str
);
80 u
->pass
= xstrndup(colon
+ 1, at
- (colon
+ 1));
85 colon
= strchr(host_start
, ':');
91 u
->host
= xstrndup(host_start
, colon
- host_start
);
96 while (*colon
>= '0' && *colon
<= '9') {
103 if (colon
== start
|| (*colon
!= 0 && *colon
!= '/')) {
110 u
->host
= xstrndup(host_start
, slash
- host_start
);
112 u
->host
= xstrdup(host_start
);
118 void http_free_uri(struct http_uri
*u
)
126 int http_open(struct http_get
*hg
, int timeout_ms
)
128 struct hostent
*hostent
;
131 struct sockaddr_in in
;
136 hostent
= gethostbyname(hg
->uri
.host
);
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);
147 flags
= fcntl(hg
->fd
, F_GETFL
);
148 if (fcntl(hg
->fd
, F_SETFL
, O_NONBLOCK
) == -1)
151 tv
.tv_sec
= timeout_ms
/ 1000;
152 tv
.tv_usec
= (timeout_ms
% 1000) * 1000;
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)
159 if (errno
== EISCONN
)
161 if (errno
!= EAGAIN
&& errno
!= EINPROGRESS
)
165 FD_SET(hg
->fd
, &wfds
);
169 rc
= select(hg
->fd
+ 1, NULL
, &wfds
, NULL
, &tv
);
180 if (tv
.tv_sec
== 0 && tv
.tv_usec
== 0) {
187 /* restore old flags */
188 if (fcntl(hg
->fd
, F_SETFL
, flags
) == -1)
198 static int http_write(int fd
, const char *buf
, int count
, int timeout_ms
)
203 tv
.tv_sec
= timeout_ms
/ 1000;
204 tv
.tv_usec
= (timeout_ms
% 1000) * 1000;
209 d_print("timeout=%ld s %ld us\n", tv
.tv_sec
, tv
.tv_usec
);
213 rc
= select(fd
+ 1, NULL
, &wfds
, NULL
, &tv
);
221 rc
= write(fd
, buf
+ pos
, count
- pos
);
223 if (errno
== EINTR
|| errno
== EAGAIN
)
230 } else if (tv
.tv_sec
== 0 && tv
.tv_usec
== 0) {
237 static int read_timeout(int fd
, int timeout_ms
)
241 tv
.tv_sec
= timeout_ms
/ 1000;
242 tv
.tv_usec
= (timeout_ms
% 1000) * 1000;
249 rc
= select(fd
+ 1, &rfds
, NULL
, NULL
, &tv
);
258 if (tv
.tv_sec
== 0 && tv
.tv_usec
== 0) {
265 /* reads response, ignores fscking carriage returns */
266 static int http_read_response(int fd
, struct gbuf
*buf
, int timeout_ms
)
270 if (read_timeout(fd
, timeout_ms
))
276 rc
= read(fd
, &ch
, 1);
285 if (ch
== '\n' && prev
== '\n')
287 gbuf_add_ch(buf
, 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
302 if (strncmp(str
, "HTTP/", 5) == 0) {
304 while (*str
!= ' ') {
310 } else if (strncmp(str
, "ICY", 3) == 0) {
319 while (*str
>= '0' && *str
<= '9') {
321 hg
->code
+= *str
- '0';
329 end
= strchr(str
, '\n');
330 hg
->reason
= xstrndup(str
, end
- str
);
337 end
= strchr(str
, '\n');
338 ptr
= strchr(str
, ':');
339 if (ptr
== NULL
|| ptr
> end
) {
342 keyvals_terminate(&h
);
343 keyvals_free(h
.keyvals
);
351 keyvals_add(&h
, str
, xstrndup(ptr
, end
- ptr
));
354 keyvals_terminate(&h
);
355 hg
->headers
= h
.keyvals
;
359 int http_get(struct http_get
*hg
, struct keyval
*headers
, int timeout_ms
)
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
);
380 rc
= http_read_response(hg
->fd
, &buf
, timeout_ms
);
384 rc
= http_parse_response(buf
.buffer
, hg
);
392 char *http_read_body(int fd
, size_t *size
, int timeout_ms
)
396 if (read_timeout(fd
, timeout_ms
))
402 gbuf_grow(&buf
, count
);
403 rc
= read_all(fd
, buf
.buffer
+ buf
.len
, count
);
411 return gbuf_steal(&buf
);
416 void http_get_free(struct http_get
*hg
)
418 http_free_uri(&hg
->uri
);
420 keyvals_free(hg
->headers
);
424 char *base64_encode(const char *str
)
426 static const char t
[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
427 int str_len
, buf_len
, i
, s
, d
;
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
);
436 for (i
= 0; i
< str_len
/ 3; i
++) {
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) {
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];
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];