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
== EISCONN
)
190 if (errno
!= EAGAIN
&& errno
!= EINPROGRESS
)
198 rc
= select(sock
+ 1, NULL
, &wfds
, NULL
, &tv
);
209 if (tv
.tv_sec
== 0 && tv
.tv_usec
== 0) {
216 /* restore old flags */
217 if (fcntl(sock
, F_SETFL
, flags
) == -1)
227 static int http_write(int fd
, const char *buf
, int count
, int timeout_ms
)
232 tv
.tv_sec
= timeout_ms
/ 1000;
233 tv
.tv_usec
= (timeout_ms
% 1000) * 1000;
238 d_print("timeout=%ld s %ld us\n", tv
.tv_sec
, tv
.tv_usec
);
242 rc
= select(fd
+ 1, NULL
, &wfds
, NULL
, &tv
);
250 rc
= write(fd
, buf
+ pos
, count
- pos
);
252 if (errno
== EINTR
|| errno
== EAGAIN
)
259 } else if (tv
.tv_sec
== 0 && tv
.tv_usec
== 0) {
266 static int read_timeout(int fd
, int timeout_ms
)
270 tv
.tv_sec
= timeout_ms
/ 1000;
271 tv
.tv_usec
= (timeout_ms
% 1000) * 1000;
278 rc
= select(fd
+ 1, &rfds
, NULL
, NULL
, &tv
);
287 if (tv
.tv_sec
== 0 && tv
.tv_usec
== 0) {
294 /* reads response, ignores fscking carriage returns */
295 static int http_read_response(int fd
, char **bufp
, int *sizep
, int *posp
, int timeout_ms
)
297 if (read_timeout(fd
, timeout_ms
))
305 rc
= read(fd
, &ch
, 1);
314 if (ch
== '\n' && pos
> 0 && buf
[pos
- 1] == '\n') {
315 buf_write_ch(bufp
, sizep
, posp
, 0);
318 buf_write_ch(bufp
, sizep
, posp
, ch
);
322 static int http_parse_response(const char *str
, int *codep
, char **reasonp
, struct http_header
**hp
)
324 /* str is 0 terminated buffer of lines
325 * every line ends with '\n'
326 * no carriage returns
332 struct http_header
*h
;
338 if (strncmp(str
, "HTTP/", 5) == 0) {
340 while (*str
!= ' ') {
346 } else if (strncmp(str
, "ICY", 3) == 0) {
355 for (i
= 0; i
< 3; i
++) {
356 if (*str
< '0' || *str
> '9') {
366 end
= strchr(str
, '\n');
367 reason
= xstrndup(str
, end
- str
);
372 h
= xnew(struct http_header
, count
);
377 if (i
== count
- 1) {
379 h
= xrenew(struct http_header
, h
, count
);
382 end
= strchr(str
, '\n');
383 ptr
= strchr(str
, ':');
384 if (ptr
== NULL
|| ptr
> end
) {
387 for (j
= 0; j
< i
; j
++) {
396 h
[i
].key
= xstrndup(str
, ptr
- str
);
400 h
[i
].val
= xstrndup(ptr
, end
- ptr
);
412 int http_get(int fd
, const char *path
, struct http_header
*headers
,
413 int *codep
, char **reasonp
, struct http_header
**ret_headersp
,
421 buf_write(&buf
, &size
, &pos
, "GET ");
422 buf_write(&buf
, &size
, &pos
, path
);
423 buf_write(&buf
, &size
, &pos
, " HTTP/1.0\r\n");
424 for (i
= 0; headers
[i
].key
; i
++) {
425 buf_write(&buf
, &size
, &pos
, headers
[i
].key
);
426 buf_write(&buf
, &size
, &pos
, ": ");
427 buf_write(&buf
, &size
, &pos
, headers
[i
].val
);
428 buf_write(&buf
, &size
, &pos
, "\r\n");
430 buf_write(&buf
, &size
, &pos
, "\r\n");
432 rc
= http_write(fd
, buf
, pos
, timeout_ms
);
437 rc
= http_read_response(fd
, &buf
, &size
, &pos
, timeout_ms
);
441 rc
= http_parse_response(buf
, codep
, reasonp
, ret_headersp
);
449 int http_read_body(int fd
, char **bodyp
, int timeout_ms
)
456 if (read_timeout(fd
, timeout_ms
))
461 buf_ensure_space(&body
, &size
, &pos
, 256);
462 rc
= read_all(fd
, body
+ pos
, size
- pos
);
468 buf_ensure_space(&body
, &size
, &pos
, 1);
477 const char *http_headers_get_value(const struct http_header
*headers
, const char *key
)
481 for (i
= 0; headers
[i
].key
; i
++) {
482 if (strcasecmp(headers
[i
].key
, key
) == 0)
483 return headers
[i
].val
;
488 void http_headers_free(struct http_header
*headers
)
492 for (i
= 0; headers
[i
].key
; i
++) {
493 free(headers
[i
].key
);
494 free(headers
[i
].val
);
499 char *base64_encode(const char *str
)
501 static const char t
[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
502 int str_len
, buf_len
, i
, s
, d
;
504 unsigned char b0
, b1
, b2
;
506 str_len
= strlen(str
);
507 buf_len
= (str_len
+ 2) / 3 * 4 + 1;
508 buf
= xnew(char, buf_len
);
511 for (i
= 0; i
< str_len
/ 3; i
++) {
516 /* 6 ms bits of b0 */
517 buf
[d
++] = t
[b0
>> 2];
519 /* 2 ls bits of b0 . 4 ms bits of b1 */
520 buf
[d
++] = t
[((b0
<< 4) | (b1
>> 4)) & 0x3f];
522 /* 4 ls bits of b1 . 2 ms bits of b2 */
523 buf
[d
++] = t
[((b1
<< 2) | (b2
>> 6)) & 0x3f];
525 /* 6 ls bits of b2 */
526 buf
[d
++] = t
[b2
& 0x3f];
528 switch (str_len
% 3) {
533 /* 6 ms bits of b0 */
534 buf
[d
++] = t
[b0
>> 2];
536 /* 2 ls bits of b0 . 4 ms bits of b1 */
537 buf
[d
++] = t
[((b0
<< 4) | (b1
>> 4)) & 0x3f];
539 /* 4 ls bits of b1 */
540 buf
[d
++] = t
[(b1
<< 2) & 0x3f];
547 /* 6 ms bits of b0 */
548 buf
[d
++] = t
[b0
>> 2];
550 /* 2 ls bits of b0 */
551 buf
[d
++] = t
[(b0
<< 4) & 0x3f];