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(const char *hostname
, unsigned int port
, int timeout_ms
)
158 struct hostent
*hostent
;
159 struct sockaddr_in addr
;
161 int sock
, save
, flags
;
163 hostent
= gethostbyname(hostname
);
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);
176 flags
= fcntl(sock
, F_GETFL
);
177 if (fcntl(sock
, F_SETFL
, O_NONBLOCK
) == -1)
180 tv
.tv_sec
= timeout_ms
/ 1000;
181 tv
.tv_usec
= (timeout_ms
% 1000) * 1000;
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)
188 if (errno
!= EAGAIN
&& errno
!= EINPROGRESS
)
196 rc
= select(sock
+ 1, NULL
, &wfds
, NULL
, &tv
);
207 if (tv
.tv_sec
== 0 && tv
.tv_usec
== 0) {
214 /* restore old flags */
215 if (fcntl(sock
, F_SETFL
, flags
) == -1)
225 static int http_write(int fd
, const char *buf
, int count
, int timeout_ms
)
230 tv
.tv_sec
= timeout_ms
/ 1000;
231 tv
.tv_usec
= (timeout_ms
% 1000) * 1000;
236 d_print("timeout=%ld s %ld us\n", tv
.tv_sec
, tv
.tv_usec
);
240 rc
= select(fd
+ 1, NULL
, &wfds
, NULL
, &tv
);
248 rc
= write(fd
, buf
+ pos
, count
- pos
);
250 if (errno
== EINTR
|| errno
== EAGAIN
)
257 } else if (tv
.tv_sec
== 0 && tv
.tv_usec
== 0) {
264 static int read_timeout(int fd
, int timeout_ms
)
268 tv
.tv_sec
= timeout_ms
/ 1000;
269 tv
.tv_usec
= (timeout_ms
% 1000) * 1000;
276 rc
= select(fd
+ 1, &rfds
, NULL
, NULL
, &tv
);
285 if (tv
.tv_sec
== 0 && tv
.tv_usec
== 0) {
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
))
303 rc
= read(fd
, &ch
, 1);
312 if (ch
== '\n' && pos
> 0 && buf
[pos
- 1] == '\n') {
313 buf_write_ch(bufp
, sizep
, posp
, 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
330 struct http_header
*h
;
336 if (strncmp(str
, "HTTP/", 5) == 0) {
338 while (*str
!= ' ') {
344 } else if (strncmp(str
, "ICY", 3) == 0) {
353 for (i
= 0; i
< 3; i
++) {
354 if (*str
< '0' || *str
> '9') {
364 end
= strchr(str
, '\n');
365 reason
= xstrndup(str
, end
- str
);
370 h
= xnew(struct http_header
, count
);
375 if (i
== count
- 1) {
377 h
= xrenew(struct http_header
, h
, count
);
380 end
= strchr(str
, '\n');
381 ptr
= strchr(str
, ':');
382 if (ptr
== NULL
|| ptr
> end
) {
385 for (j
= 0; j
< i
; j
++) {
394 h
[i
].key
= xstrndup(str
, ptr
- str
);
398 h
[i
].val
= xstrndup(ptr
, end
- ptr
);
410 int http_get(int fd
, const char *path
, struct http_header
*headers
,
411 int *codep
, char **reasonp
, struct http_header
**ret_headersp
,
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
);
435 rc
= http_read_response(fd
, &buf
, &size
, &pos
, timeout_ms
);
439 rc
= http_parse_response(buf
, codep
, reasonp
, ret_headersp
);
447 int http_read_body(int fd
, char **bodyp
, int timeout_ms
)
454 if (read_timeout(fd
, timeout_ms
))
459 buf_ensure_space(&body
, &size
, &pos
, 256);
460 rc
= read_all(fd
, body
+ pos
, size
- pos
);
466 buf_ensure_space(&body
, &size
, &pos
, 1);
475 const char *http_headers_get_value(const struct http_header
*headers
, const char *key
)
479 for (i
= 0; headers
[i
].key
; i
++) {
480 if (strcasecmp(headers
[i
].key
, key
) == 0)
481 return headers
[i
].val
;
486 void http_headers_free(struct http_header
*headers
)
490 for (i
= 0; headers
[i
].key
; i
++) {
491 free(headers
[i
].key
);
492 free(headers
[i
].val
);
497 char *base64_encode(const char *str
)
499 static const char t
[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
500 int str_len
, buf_len
, i
, s
, d
;
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
);
509 for (i
= 0; i
< str_len
/ 3; i
++) {
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) {
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];
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];