1 /* $Id: http.c,v 1.3 2005/01/23 13:22:37 konst Exp $ */
4 * (C) Copyright 2001-2002 Wojtek Kaniewski <wojtekka@irc.pl>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License Version
8 * 2.1 as published by the Free Software Foundation.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 #include <sys/types.h>
22 #include <sys/socket.h>
23 #include <netinet/in.h>
24 #include <arpa/inet.h>
26 #include "libgadu-config.h"
31 #ifdef __GG_LIBGADU_HAVE_PTHREAD
43 #define gg_http_error(x) \
46 h->state = GG_STATE_ERROR; \
51 * gg_http_connect() // funkcja pomocnicza
53 * rozpoczyna po³±czenie po http.
55 * - hostname - adres serwera
56 * - port - port serwera
57 * - async - asynchroniczne po³±czenie
58 * - method - metoda http (GET, POST, cokolwiek)
59 * - path - ¶cie¿ka do zasobu (musi byæ poprzedzona ,,/'')
60 * - header - nag³ówek zapytania plus ewentualne dane dla POST
62 * zaalokowana struct gg_http, któr± po¼niej nale¿y
63 * zwolniæ funkcj± gg_http_free(), albo NULL je¶li wyst±pi³ b³±d.
65 struct gg_http
*gg_http_connect(const char *hostname
, int port
, int async
, const char *method
, const char *path
, const char *header
)
69 if (!hostname
|| !port
|| !method
|| !path
|| !header
) {
70 gg_debug(GG_DEBUG_MISC
, "// gg_http_connect() invalid arguments\n");
75 if (!(h
= malloc(sizeof(*h
))))
77 memset(h
, 0, sizeof(*h
));
82 h
->type
= GG_SESSION_HTTP
;
84 if (gg_proxy_enabled
) {
85 char *auth
= gg_proxy_auth();
87 h
->query
= gg_saprintf("%s http://%s:%d%s HTTP/1.0\r\n%s%s",
88 method
, hostname
, port
, path
, (auth
) ? auth
:
90 hostname
= gg_proxy_host
;
91 h
->port
= port
= gg_proxy_port
;
96 h
->query
= gg_saprintf("%s %s HTTP/1.0\r\n%s",
97 method
, path
, header
);
101 gg_debug(GG_DEBUG_MISC
, "// gg_http_connect() not enough memory for query\n");
107 gg_debug(GG_DEBUG_MISC
, "=> -----BEGIN-HTTP-QUERY-----\n%s\n=> -----END-HTTP-QUERY-----\n", h
->query
);
113 if (!(he
= gg_gethostbyname(hostname
))) {
118 memcpy((char*) &a
, he
->h_addr
, sizeof(a
));
122 #ifndef __GG_LIBGADU_HAVE_PTHREAD
123 if (gg_resolve(&h->fd, &h->pid, hostname)) {
125 if (gg_resolve_pthread(&h->fd, &h->resolver, hostname)) {
127 gg_debug(GG_DEBUG_MISC, "// gg_http_connect() resolver failed\n");
133 gg_debug(GG_DEBUG_MISC, "// gg_http_connect() resolver = %p\n", h->resolver);
134 h->state = GG_STATE_RESOLVING;
135 h->check = GG_CHECK_READ;
136 h->timeout = GG_DEFAULT_TIMEOUT;
138 if ((h
->fd
= gg_connect(&a
, h
->port
, h
->async
)) == -1) {
139 gg_debug(GG_DEBUG_MISC
, "=> http, connection failed (errno=%d, %s)\n", errno
, strerror(errno
));
140 gg_http_error(GG_ERROR_CONNECTING
);
143 h
->state
= GG_STATE_CONNECTING
;
144 h
->check
= GG_CHECK_WRITE
;
145 h
->timeout
= GG_DEFAULT_TIMEOUT
;
151 if (!(he
= gg_gethostbyname(hostname
))) {
152 gg_debug(GG_DEBUG_MISC
, "// gg_http_connect() host not found\n");
157 memcpy((char*) &a
, he
->h_addr
, sizeof(a
));
161 if (!(h
->fd
= gg_connect(&a
, port
, 0)) == -1) {
162 gg_debug(GG_DEBUG_MISC
, "// gg_http_connect() connection failed (errno=%d, %s)\n", errno
, strerror(errno
));
167 h
->state
= GG_STATE_CONNECTING
;
169 while (h
->state
!= GG_STATE_ERROR
&& h
->state
!= GG_STATE_PARSING
) {
170 if (gg_http_watch_fd(h
) == -1)
174 if (h
->state
!= GG_STATE_PARSING
) {
175 gg_debug(GG_DEBUG_MISC
, "// gg_http_connect() some strange error\n");
181 h
->callback
= gg_http_watch_fd
;
182 h
->destroy
= gg_http_free
;
190 * przy asynchronicznej obs³udze HTTP funkcjê t± nale¿y wywo³aæ, je¶li
191 * zmieni³o siê co¶ na obserwowanym deskryptorze.
193 * - h - struktura opisuj±ca po³±czenie
195 * je¶li wszystko posz³o dobrze to 0, inaczej -1. po³±czenie bêdzie
196 * zakoñczone, je¶li h->state == GG_STATE_PARSING. je¶li wyst±pi jaki¶
197 * b³±d, to bêdzie tam GG_STATE_ERROR i odpowiedni kod b³êdu w h->error.
199 int gg_http_watch_fd(struct gg_http
*h
)
201 gg_debug(GG_DEBUG_FUNCTION
, "** gg_http_watch_fd(%p);\n", h
);
204 gg_debug(GG_DEBUG_MISC
, "// gg_http_watch_fd() invalid arguments\n");
209 if (h
->state
== GG_STATE_RESOLVING
) {
212 gg_debug(GG_DEBUG_MISC
, "=> http, resolving done\n");
214 if (read(h
->fd
, &a
, sizeof(a
)) < (signed)sizeof(a
) || a
.s_addr
== INADDR_NONE
) {
215 gg_debug(GG_DEBUG_MISC
, "=> http, resolver thread failed\n");
216 gg_http_error(GG_ERROR_RESOLVING
);
222 #ifndef __GG_LIBGADU_HAVE_PTHREAD
223 waitpid(h
->pid
, NULL
, 0);
226 pthread_cancel(*((pthread_t
*) h
->resolver
));
232 gg_debug(GG_DEBUG_MISC
, "=> http, connecting to %s:%d\n", inet_ntoa(a
), h
->port
);
234 if ((h
->fd
= gg_connect(&a
, h
->port
, h
->async
)) == -1) {
235 gg_debug(GG_DEBUG_MISC
, "=> http, connection failed (errno=%d, %s)\n", errno
, strerror(errno
));
236 gg_http_error(GG_ERROR_CONNECTING
);
239 h
->state
= GG_STATE_CONNECTING
;
240 h
->check
= GG_CHECK_WRITE
;
241 h
->timeout
= GG_DEFAULT_TIMEOUT
;
246 if (h
->state
== GG_STATE_CONNECTING
) {
248 unsigned int res_size
= sizeof(res
);
250 if (h
->async
&& (getsockopt(h
->fd
, SOL_SOCKET
, SO_ERROR
, &res
, &res_size
) || res
)) {
251 gg_debug(GG_DEBUG_MISC
, "=> http, async connection failed (errno=%d, %s)\n", (res
) ? res
: errno
, strerror((res
) ? res
: errno
));
254 h
->state
= GG_STATE_ERROR
;
255 h
->error
= GG_ERROR_CONNECTING
;
261 gg_debug(GG_DEBUG_MISC
, "=> http, connected, sending request\n");
263 h
->state
= GG_STATE_SENDING_QUERY
;
266 if (h
->state
== GG_STATE_SENDING_QUERY
) {
269 if ((res
= write(h
->fd
, h
->query
, strlen(h
->query
))) < 1) {
270 gg_debug(GG_DEBUG_MISC
, "=> http, write() failed (len=%d, res=%d, errno=%d)\n", strlen(h
->query
), res
, errno
);
271 gg_http_error(GG_ERROR_WRITING
);
274 if (res
< strlen(h
->query
)) {
275 gg_debug(GG_DEBUG_MISC
, "=> http, partial header sent (led=%d, sent=%d)\n", strlen(h
->query
), res
);
277 memmove(h
->query
, h
->query
+ res
, strlen(h
->query
) - res
+ 1);
278 h
->state
= GG_STATE_SENDING_QUERY
;
279 h
->check
= GG_CHECK_WRITE
;
280 h
->timeout
= GG_DEFAULT_TIMEOUT
;
282 gg_debug(GG_DEBUG_MISC
, "=> http, request sent (len=%d)\n", strlen(h
->query
));
286 h
->state
= GG_STATE_READING_HEADER
;
287 h
->check
= GG_CHECK_READ
;
288 h
->timeout
= GG_DEFAULT_TIMEOUT
;
294 if (h
->state
== GG_STATE_READING_HEADER
) {
295 char buf
[1024], *tmp
;
298 if ((res
= read(h
->fd
, buf
, sizeof(buf
))) == -1) {
299 gg_debug(GG_DEBUG_MISC
, "=> http, reading header failed (errno=%d)\n", errno
);
304 gg_http_error(GG_ERROR_READING
);
308 gg_debug(GG_DEBUG_MISC
, "=> http, connection reset by peer\n");
313 gg_http_error(GG_ERROR_READING
);
316 gg_debug(GG_DEBUG_MISC
, "=> http, read %d bytes of header\n", res
);
318 if (!(tmp
= realloc(h
->header
, h
->header_size
+ res
+ 1))) {
319 gg_debug(GG_DEBUG_MISC
, "=> http, not enough memory for header\n");
322 gg_http_error(GG_ERROR_READING
);
327 memcpy(h
->header
+ h
->header_size
, buf
, res
);
328 h
->header_size
+= res
;
330 gg_debug(GG_DEBUG_MISC
, "=> http, header_buf=%p, header_size=%d\n", h
->header
, h
->header_size
);
332 h
->header
[h
->header_size
] = 0;
334 if ((tmp
= strstr(h
->header
, "\r\n\r\n")) || (tmp
= strstr(h
->header
, "\n\n"))) {
335 int sep_len
= (*tmp
== '\r') ? 4 : 2;
339 left
= h
->header_size
- ((long)(tmp
) - (long)(h
->header
) + sep_len
);
341 gg_debug(GG_DEBUG_MISC
, "=> http, got all header (%d bytes, %d left)\n", h
->header_size
- left
, left
);
343 /* HTTP/1.1 200 OK */
344 if (strlen(h
->header
) < 16 || strncmp(h
->header
+ 9, "200", 3)) {
345 gg_debug(GG_DEBUG_MISC
, "=> -----BEGIN-HTTP-HEADER-----\n%s\n=> -----END-HTTP-HEADER-----\n", h
->header
);
347 gg_debug(GG_DEBUG_MISC
, "=> http, didn't get 200 OK -- no results\n");
350 gg_http_error(GG_ERROR_CONNECTING
);
357 gg_debug(GG_DEBUG_MISC
, "=> -----BEGIN-HTTP-HEADER-----\n%s\n=> -----END-HTTP-HEADER-----\n", h
->header
);
360 if (!strncasecmp(line
, "Content-length: ", 16)) {
361 h
->body_size
= atoi(line
+ 16);
363 line
= strchr(line
, '\n');
368 if (h
->body_size
<= 0) {
369 gg_debug(GG_DEBUG_MISC
, "=> http, content-length not found\n");
373 if (left
> h
->body_size
) {
374 gg_debug(GG_DEBUG_MISC
, "=> http, oversized reply (%d bytes needed, %d bytes left)\n", h
->body_size
, left
);
378 gg_debug(GG_DEBUG_MISC
, "=> http, body_size=%d\n", h
->body_size
);
380 if (!(h
->body
= malloc(h
->body_size
+ 1))) {
381 gg_debug(GG_DEBUG_MISC
, "=> http, not enough memory (%d bytes for body_buf)\n", h
->body_size
+ 1);
384 gg_http_error(GG_ERROR_READING
);
388 memcpy(h
->body
, tmp
+ sep_len
, left
);
394 h
->state
= GG_STATE_READING_DATA
;
395 h
->check
= GG_CHECK_READ
;
396 h
->timeout
= GG_DEFAULT_TIMEOUT
;
402 if (h
->state
== GG_STATE_READING_DATA
) {
406 if ((res
= read(h
->fd
, buf
, sizeof(buf
))) == -1) {
407 gg_debug(GG_DEBUG_MISC
, "=> http, reading body failed (errno=%d)\n", errno
);
412 gg_http_error(GG_ERROR_READING
);
416 if (h
->body_done
>= h
->body_size
) {
417 gg_debug(GG_DEBUG_MISC
, "=> http, we're done, closing socket\n");
418 h
->state
= GG_STATE_PARSING
;
422 gg_debug(GG_DEBUG_MISC
, "=> http, connection closed while reading (have %d, need %d)\n", h
->body_done
, h
->body_size
);
427 gg_http_error(GG_ERROR_READING
);
433 gg_debug(GG_DEBUG_MISC
, "=> http, read %d bytes of body\n", res
);
435 if (h
->body_done
+ res
> h
->body_size
) {
438 gg_debug(GG_DEBUG_MISC
, "=> http, too much data (%d bytes, %d needed), enlarging buffer\n", h
->body_done
+ res
, h
->body_size
);
440 if (!(tmp
= realloc(h
->body
, h
->body_done
+ res
+ 1))) {
441 gg_debug(GG_DEBUG_MISC
, "=> http, not enough memory for data (%d needed)\n", h
->body_done
+ res
+ 1);
444 gg_http_error(GG_ERROR_READING
);
448 h
->body_size
= h
->body_done
+ res
;
451 h
->body
[h
->body_done
+ res
] = 0;
452 memcpy(h
->body
+ h
->body_done
, buf
, res
);
455 gg_debug(GG_DEBUG_MISC
, "=> body_done=%d, body_size=%d\n", h
->body_done
, h
->body_size
);
464 h
->state
= GG_STATE_ERROR
;
475 * je¶li po³±czenie jest w trakcie, przerywa je. nie zwalnia h->data.
477 * - h - struktura opisuj±ca po³±czenie
479 void gg_http_stop(struct gg_http
*h
)
484 if (h
->state
== GG_STATE_ERROR
|| h
->state
== GG_STATE_DONE
)
493 * gg_http_free_fields() // funkcja wewnêtrzna
495 * zwalnia pola struct gg_http, ale nie zwalnia samej struktury.
497 void gg_http_free_fields(struct gg_http
*h
)
521 * próbuje zamkn±æ po³±czenie i zwalnia pamiêæ po nim.
523 * - h - struktura, któr± nale¿y zlikwidowaæ
525 void gg_http_free(struct gg_http
*h
)
531 gg_http_free_fields(h
);
537 * c-indentation-style: k&r
539 * indent-tabs-mode: notnil