connwrap - initialize gnutls session in cw_connect
[centerim.git] / libgadu / http.c
blob7379bf4dd3ad089ab70f85235193259538917922
1 /* $Id: http.c,v 1.3 2005/01/23 13:22:37 konst Exp $ */
3 /*
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>
21 #include <sys/wait.h>
22 #include <sys/socket.h>
23 #include <netinet/in.h>
24 #include <arpa/inet.h>
26 #include "libgadu-config.h"
28 #include <ctype.h>
29 #include <errno.h>
30 #include <netdb.h>
31 #ifdef __GG_LIBGADU_HAVE_PTHREAD
32 # include <pthread.h>
33 #endif
34 #include <stdarg.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <unistd.h>
40 #include "compat.h"
41 #include "libgadu.h"
43 #define gg_http_error(x) \
44 close(h->fd); \
45 h->fd = -1; \
46 h->state = GG_STATE_ERROR; \
47 h->error = x; \
48 return 0;
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)
67 struct gg_http *h;
69 if (!hostname || !port || !method || !path || !header) {
70 gg_debug(GG_DEBUG_MISC, "// gg_http_connect() invalid arguments\n");
71 errno = EINVAL;
72 return NULL;
75 if (!(h = malloc(sizeof(*h))))
76 return NULL;
77 memset(h, 0, sizeof(*h));
79 h->async = async;
80 h->port = port;
81 h->fd = -1;
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 :
89 "", header);
90 hostname = gg_proxy_host;
91 h->port = port = gg_proxy_port;
93 if (auth)
94 free(auth);
95 } else {
96 h->query = gg_saprintf("%s %s HTTP/1.0\r\n%s",
97 method, path, header);
100 if (!h->query) {
101 gg_debug(GG_DEBUG_MISC, "// gg_http_connect() not enough memory for query\n");
102 free(h);
103 errno = ENOMEM;
104 return NULL;
107 gg_debug(GG_DEBUG_MISC, "=> -----BEGIN-HTTP-QUERY-----\n%s\n=> -----END-HTTP-QUERY-----\n", h->query);
109 if (async) {
110 struct hostent *he;
111 struct in_addr a;
113 if (!(he = gg_gethostbyname(hostname))) {
114 gg_http_free(h);
115 errno = ENOENT;
116 return NULL;
117 } else {
118 memcpy((char*) &a, he->h_addr, sizeof(a));
119 free(he);
122 #ifndef __GG_LIBGADU_HAVE_PTHREAD
123 if (gg_resolve(&h->fd, &h->pid, hostname)) {
124 #else
125 if (gg_resolve_pthread(&h->fd, &h->resolver, hostname)) {
126 #endif
127 gg_debug(GG_DEBUG_MISC, "// gg_http_connect() resolver failed\n");
128 gg_http_free(h);
129 errno = ENOENT;
130 return NULL;
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;
147 } else {
148 struct hostent *he;
149 struct in_addr a;
151 if (!(he = gg_gethostbyname(hostname))) {
152 gg_debug(GG_DEBUG_MISC, "// gg_http_connect() host not found\n");
153 gg_http_free(h);
154 errno = ENOENT;
155 return NULL;
156 } else {
157 memcpy((char*) &a, he->h_addr, sizeof(a));
158 free(he);
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));
163 gg_http_free(h);
164 return NULL;
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)
171 break;
174 if (h->state != GG_STATE_PARSING) {
175 gg_debug(GG_DEBUG_MISC, "// gg_http_connect() some strange error\n");
176 gg_http_free(h);
177 return NULL;
181 h->callback = gg_http_watch_fd;
182 h->destroy = gg_http_free;
184 return h;
188 * gg_http_watch_fd()
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);
203 if (!h) {
204 gg_debug(GG_DEBUG_MISC, "// gg_http_watch_fd() invalid arguments\n");
205 errno = EINVAL;
206 return -1;
209 if (h->state == GG_STATE_RESOLVING) {
210 struct in_addr a;
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);
219 close(h->fd);
220 h->fd = -1;
222 #ifndef __GG_LIBGADU_HAVE_PTHREAD
223 waitpid(h->pid, NULL, 0);
224 #else
225 if (h->resolver) {
226 pthread_cancel(*((pthread_t *) h->resolver));
227 free(h->resolver);
228 h->resolver = NULL;
230 #endif
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;
243 return 0;
246 if (h->state == GG_STATE_CONNECTING) {
247 int res = 0;
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));
252 close(h->fd);
253 h->fd = -1;
254 h->state = GG_STATE_ERROR;
255 h->error = GG_ERROR_CONNECTING;
256 if (res)
257 errno = res;
258 return 0;
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) {
267 int res;
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;
281 } else {
282 gg_debug(GG_DEBUG_MISC, "=> http, request sent (len=%d)\n", strlen(h->query));
283 free(h->query);
284 h->query = NULL;
286 h->state = GG_STATE_READING_HEADER;
287 h->check = GG_CHECK_READ;
288 h->timeout = GG_DEFAULT_TIMEOUT;
291 return 0;
294 if (h->state == GG_STATE_READING_HEADER) {
295 char buf[1024], *tmp;
296 int res;
298 if ((res = read(h->fd, buf, sizeof(buf))) == -1) {
299 gg_debug(GG_DEBUG_MISC, "=> http, reading header failed (errno=%d)\n", errno);
300 if (h->header) {
301 free(h->header);
302 h->header = NULL;
304 gg_http_error(GG_ERROR_READING);
307 if (!res) {
308 gg_debug(GG_DEBUG_MISC, "=> http, connection reset by peer\n");
309 if (h->header) {
310 free(h->header);
311 h->header = NULL;
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");
320 free(h->header);
321 h->header = NULL;
322 gg_http_error(GG_ERROR_READING);
325 h->header = tmp;
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;
336 unsigned int left;
337 char *line;
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");
348 free(h->header);
349 h->header = NULL;
350 gg_http_error(GG_ERROR_CONNECTING);
353 h->body_size = 0;
354 line = h->header;
355 *tmp = 0;
357 gg_debug(GG_DEBUG_MISC, "=> -----BEGIN-HTTP-HEADER-----\n%s\n=> -----END-HTTP-HEADER-----\n", h->header);
359 while (line) {
360 if (!strncasecmp(line, "Content-length: ", 16)) {
361 h->body_size = atoi(line + 16);
363 line = strchr(line, '\n');
364 if (line)
365 line++;
368 if (h->body_size <= 0) {
369 gg_debug(GG_DEBUG_MISC, "=> http, content-length not found\n");
370 h->body_size = left;
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);
375 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);
382 free(h->header);
383 h->header = NULL;
384 gg_http_error(GG_ERROR_READING);
387 if (left) {
388 memcpy(h->body, tmp + sep_len, left);
389 h->body_done = left;
392 h->body[left] = 0;
394 h->state = GG_STATE_READING_DATA;
395 h->check = GG_CHECK_READ;
396 h->timeout = GG_DEFAULT_TIMEOUT;
399 return 0;
402 if (h->state == GG_STATE_READING_DATA) {
403 char buf[1024];
404 int res;
406 if ((res = read(h->fd, buf, sizeof(buf))) == -1) {
407 gg_debug(GG_DEBUG_MISC, "=> http, reading body failed (errno=%d)\n", errno);
408 if (h->body) {
409 free(h->body);
410 h->body = NULL;
412 gg_http_error(GG_ERROR_READING);
415 if (!res) {
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;
419 close(h->fd);
420 h->fd = -1;
421 } else {
422 gg_debug(GG_DEBUG_MISC, "=> http, connection closed while reading (have %d, need %d)\n", h->body_done, h->body_size);
423 if (h->body) {
424 free(h->body);
425 h->body = NULL;
427 gg_http_error(GG_ERROR_READING);
430 return 0;
433 gg_debug(GG_DEBUG_MISC, "=> http, read %d bytes of body\n", res);
435 if (h->body_done + res > h->body_size) {
436 char *tmp;
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);
442 free(h->body);
443 h->body = NULL;
444 gg_http_error(GG_ERROR_READING);
447 h->body = tmp;
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);
453 h->body_done += res;
455 gg_debug(GG_DEBUG_MISC, "=> body_done=%d, body_size=%d\n", h->body_done, h->body_size);
457 return 0;
460 if (h->fd != -1)
461 close(h->fd);
463 h->fd = -1;
464 h->state = GG_STATE_ERROR;
465 h->error = 0;
467 return -1;
470 #undef gg_http_error
473 * gg_http_stop()
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)
481 if (!h)
482 return;
484 if (h->state == GG_STATE_ERROR || h->state == GG_STATE_DONE)
485 return;
487 if (h->fd != -1)
488 close(h->fd);
489 h->fd = -1;
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)
499 if (!h)
500 return;
502 if (h->body) {
503 free(h->body);
504 h->body = NULL;
507 if (h->query) {
508 free(h->query);
509 h->query = NULL;
512 if (h->header) {
513 free(h->header);
514 h->header = NULL;
519 * gg_http_free()
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)
527 if (!h)
528 return;
530 gg_http_stop(h);
531 gg_http_free_fields(h);
532 free(h);
536 * Local variables:
537 * c-indentation-style: k&r
538 * c-basic-offset: 8
539 * indent-tabs-mode: notnil
540 * End:
542 * vim: shiftwidth=8: