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
27 #include <sys/types.h>
29 #include <sys/socket.h>
30 #include <netinet/in.h>
32 #include <arpa/inet.h>
36 static void buf_ensure_space(char **bufp
, int *sizep
, int *posp
, int len
)
41 if (size
- pos
< len
) {
44 while (size
- pos
< len
)
46 *bufp
= xrenew(char, *bufp
, 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
);
60 static void buf_write_ch(char **bufp
, int *sizep
, int *posp
, char ch
)
62 buf_ensure_space(bufp
, sizep
, 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
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 */
85 if (strncmp(uri
, "http://", 7))
91 slash
= strchr(str
, '/');
93 u
->path
= xstrdup(slash
);
95 u
->path
= xstrdup("/");
99 at
= strchr(str
, '@');
103 colon
= strchr(str
, ':');
104 if (colon
== NULL
|| colon
> at
) {
106 u
->user
= xstrndup(str
, at
- str
);
109 u
->user
= xstrndup(str
, colon
- str
);
110 u
->pass
= xstrndup(colon
+ 1, at
- (colon
+ 1));
115 colon
= strchr(host_start
, ':');
121 u
->host
= xstrndup(host_start
, colon
- host_start
);
126 while (*colon
>= '0' && *colon
<= '9') {
128 port
+= *colon
- '0';
133 if (colon
== start
|| (*colon
!= 0 && *colon
!= '/')) {
140 u
->host
= xstrndup(host_start
, slash
- host_start
);
142 u
->host
= xstrdup(host_start
);
148 void http_free_uri(struct http_uri
*u
)
156 int http_open(struct http_get
*hg
, int timeout_ms
)
158 struct hostent
*hostent
;
161 struct sockaddr_in in
;
166 hostent
= gethostbyname(hg
->uri
.host
);
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);
177 flags
= fcntl(hg
->fd
, F_GETFL
);
178 if (fcntl(hg
->fd
, F_SETFL
, O_NONBLOCK
) == -1)
181 tv
.tv_sec
= timeout_ms
/ 1000;
182 tv
.tv_usec
= (timeout_ms
% 1000) * 1000;
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)
189 if (errno
== EISCONN
)
191 if (errno
!= EAGAIN
&& errno
!= EINPROGRESS
)
195 FD_SET(hg
->fd
, &wfds
);
199 rc
= select(hg
->fd
+ 1, NULL
, &wfds
, NULL
, &tv
);
210 if (tv
.tv_sec
== 0 && tv
.tv_usec
== 0) {
217 /* restore old flags */
218 if (fcntl(hg
->fd
, F_SETFL
, flags
) == -1)
228 static int http_write(int fd
, const char *buf
, int count
, int timeout_ms
)
233 tv
.tv_sec
= timeout_ms
/ 1000;
234 tv
.tv_usec
= (timeout_ms
% 1000) * 1000;
239 d_print("timeout=%ld s %ld us\n", tv
.tv_sec
, tv
.tv_usec
);
243 rc
= select(fd
+ 1, NULL
, &wfds
, NULL
, &tv
);
251 rc
= write(fd
, buf
+ pos
, count
- pos
);
253 if (errno
== EINTR
|| errno
== EAGAIN
)
260 } else if (tv
.tv_sec
== 0 && tv
.tv_usec
== 0) {
267 static int read_timeout(int fd
, int timeout_ms
)
271 tv
.tv_sec
= timeout_ms
/ 1000;
272 tv
.tv_usec
= (timeout_ms
% 1000) * 1000;
279 rc
= select(fd
+ 1, &rfds
, NULL
, NULL
, &tv
);
288 if (tv
.tv_sec
== 0 && tv
.tv_usec
== 0) {
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
))
306 rc
= read(fd
, &ch
, 1);
315 if (ch
== '\n' && pos
> 0 && buf
[pos
- 1] == '\n') {
316 buf_write_ch(bufp
, sizep
, posp
, 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
339 if (strncmp(str
, "HTTP/", 5) == 0) {
341 while (*str
!= ' ') {
347 } else if (strncmp(str
, "ICY", 3) == 0) {
356 for (i
= 0; i
< 3; i
++) {
357 if (*str
< '0' || *str
> '9') {
367 end
= strchr(str
, '\n');
368 reason
= xstrndup(str
, end
- str
);
373 h
= xnew(struct keyval
, count
);
378 if (i
== count
- 1) {
380 h
= xrenew(struct keyval
, h
, count
);
383 end
= strchr(str
, '\n');
384 ptr
= strchr(str
, ':');
385 if (ptr
== NULL
|| ptr
> end
) {
388 for (j
= 0; j
< i
; j
++) {
397 h
[i
].key
= xstrndup(str
, ptr
- str
);
401 h
[i
].val
= xstrndup(ptr
, end
- ptr
);
413 int http_get(struct http_get
*hg
, struct keyval
*headers
, int timeout_ms
)
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
);
436 rc
= http_read_response(hg
->fd
, &buf
, &size
, &pos
, timeout_ms
);
440 rc
= http_parse_response(buf
, &hg
->code
, &hg
->reason
, &hg
->headers
);
448 int http_read_body(int fd
, char **bodyp
, int timeout_ms
)
455 if (read_timeout(fd
, timeout_ms
))
460 buf_ensure_space(&body
, &size
, &pos
, 256);
461 rc
= read_all(fd
, body
+ pos
, size
- pos
);
467 buf_ensure_space(&body
, &size
, &pos
, 1);
476 void http_get_free(struct http_get
*hg
)
478 http_free_uri(&hg
->uri
);
479 keyvals_free(hg
->headers
);
483 char *base64_encode(const char *str
)
485 static const char t
[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
486 int str_len
, buf_len
, i
, s
, d
;
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
);
495 for (i
= 0; i
< str_len
/ 3; i
++) {
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) {
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];
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];