src/protocols/gg/lib/http.c

changeset 11360
b77fd5a8959a
child 11546
acb5676e57bb
equal deleted inserted replaced
11359:9630f62029f2 11360:b77fd5a8959a
1 /* $Id: http.c 13582 2005-08-28 22:46:01Z boler $ */
2
3 /*
4 * (C) Copyright 2001-2002 Wojtek Kaniewski <wojtekka@irc.pl>
5 *
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.
9 *
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.
14 *
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
18 * USA.
19 */
20
21 #include <sys/types.h>
22 #include <sys/wait.h>
23 #include <sys/socket.h>
24 #include <netinet/in.h>
25 #include <arpa/inet.h>
26
27 #include "libgadu-config.h"
28
29 #include <ctype.h>
30 #include <errno.h>
31 #include <netdb.h>
32 #ifdef __GG_LIBGADU_HAVE_PTHREAD
33 # include <pthread.h>
34 #endif
35 #include <stdarg.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <unistd.h>
40
41 #include "compat.h"
42 #include "libgadu.h"
43
44 /*
45 * gg_http_connect() // funkcja pomocnicza
46 *
47 * rozpoczyna połączenie po http.
48 *
49 * - hostname - adres serwera
50 * - port - port serwera
51 * - async - asynchroniczne połączenie
52 * - method - metoda http (GET, POST, cokolwiek)
53 * - path - ścieżka do zasobu (musi być poprzedzona ,,/'')
54 * - header - nagłówek zapytania plus ewentualne dane dla POST
55 *
56 * zaalokowana struct gg_http, którą poźniej należy
57 * zwolnić funkcją gg_http_free(), albo NULL jeśli wystąpił błąd.
58 */
59 struct gg_http *gg_http_connect(const char *hostname, int port, int async, const char *method, const char *path, const char *header)
60 {
61 struct gg_http *h;
62
63 if (!hostname || !port || !method || !path || !header) {
64 gg_debug(GG_DEBUG_MISC, "// gg_http_connect() invalid arguments\n");
65 errno = EFAULT;
66 return NULL;
67 }
68
69 if (!(h = malloc(sizeof(*h))))
70 return NULL;
71 memset(h, 0, sizeof(*h));
72
73 h->async = async;
74 h->port = port;
75 h->fd = -1;
76 h->type = GG_SESSION_HTTP;
77
78 if (gg_proxy_enabled) {
79 char *auth = gg_proxy_auth();
80
81 h->query = gg_saprintf("%s http://%s:%d%s HTTP/1.0\r\n%s%s",
82 method, hostname, port, path, (auth) ? auth :
83 "", header);
84 hostname = gg_proxy_host;
85 h->port = port = gg_proxy_port;
86
87 if (auth)
88 free(auth);
89 } else {
90 h->query = gg_saprintf("%s %s HTTP/1.0\r\n%s",
91 method, path, header);
92 }
93
94 if (!h->query) {
95 gg_debug(GG_DEBUG_MISC, "// gg_http_connect() not enough memory for query\n");
96 free(h);
97 errno = ENOMEM;
98 return NULL;
99 }
100
101 gg_debug(GG_DEBUG_MISC, "=> -----BEGIN-HTTP-QUERY-----\n%s\n=> -----END-HTTP-QUERY-----\n", h->query);
102
103 if (async) {
104 #ifndef __GG_LIBGADU_HAVE_PTHREAD
105 if (gg_resolve(&h->fd, &h->pid, hostname)) {
106 #else
107 if (gg_resolve_pthread(&h->fd, &h->resolver, hostname)) {
108 #endif
109 gg_debug(GG_DEBUG_MISC, "// gg_http_connect() resolver failed\n");
110 gg_http_free(h);
111 errno = ENOENT;
112 return NULL;
113 }
114
115 gg_debug(GG_DEBUG_MISC, "// gg_http_connect() resolver = %p\n", h->resolver);
116
117 h->state = GG_STATE_RESOLVING;
118 h->check = GG_CHECK_READ;
119 h->timeout = GG_DEFAULT_TIMEOUT;
120 } else {
121 struct in_addr *hn, a;
122
123 if (!(hn = gg_gethostbyname(hostname))) {
124 gg_debug(GG_DEBUG_MISC, "// gg_http_connect() host not found\n");
125 gg_http_free(h);
126 errno = ENOENT;
127 return NULL;
128 } else {
129 a.s_addr = hn->s_addr;
130 free(hn);
131 }
132
133 if (!(h->fd = gg_connect(&a, port, 0)) == -1) {
134 gg_debug(GG_DEBUG_MISC, "// gg_http_connect() connection failed (errno=%d, %s)\n", errno, strerror(errno));
135 gg_http_free(h);
136 return NULL;
137 }
138
139 h->state = GG_STATE_CONNECTING;
140
141 while (h->state != GG_STATE_ERROR && h->state != GG_STATE_PARSING) {
142 if (gg_http_watch_fd(h) == -1)
143 break;
144 }
145
146 if (h->state != GG_STATE_PARSING) {
147 gg_debug(GG_DEBUG_MISC, "// gg_http_connect() some strange error\n");
148 gg_http_free(h);
149 return NULL;
150 }
151 }
152
153 h->callback = gg_http_watch_fd;
154 h->destroy = gg_http_free;
155
156 return h;
157 }
158
159 #define gg_http_error(x) \
160 close(h->fd); \
161 h->fd = -1; \
162 h->state = GG_STATE_ERROR; \
163 h->error = x; \
164 return 0;
165
166 /*
167 * gg_http_watch_fd()
168 *
169 * przy asynchronicznej obsłudze HTTP funkcję tą należy wywołać, jeśli
170 * zmieniło się coś na obserwowanym deskryptorze.
171 *
172 * - h - struktura opisująca połączenie
173 *
174 * jeśli wszystko poszło dobrze to 0, inaczej -1. połączenie będzie
175 * zakończone, jeśli h->state == GG_STATE_PARSING. jeśli wystąpi jakiś
176 * błąd, to będzie tam GG_STATE_ERROR i odpowiedni kod błędu w h->error.
177 */
178 int gg_http_watch_fd(struct gg_http *h)
179 {
180 gg_debug(GG_DEBUG_FUNCTION, "** gg_http_watch_fd(%p);\n", h);
181
182 if (!h) {
183 gg_debug(GG_DEBUG_MISC, "// gg_http_watch_fd() invalid arguments\n");
184 errno = EFAULT;
185 return -1;
186 }
187
188 if (h->state == GG_STATE_RESOLVING) {
189 struct in_addr a;
190
191 gg_debug(GG_DEBUG_MISC, "=> http, resolving done\n");
192
193 if (read(h->fd, &a, sizeof(a)) < (signed)sizeof(a) || a.s_addr == INADDR_NONE) {
194 gg_debug(GG_DEBUG_MISC, "=> http, resolver thread failed\n");
195 gg_http_error(GG_ERROR_RESOLVING);
196 }
197
198 close(h->fd);
199 h->fd = -1;
200
201 #ifndef __GG_LIBGADU_HAVE_PTHREAD
202 waitpid(h->pid, NULL, 0);
203 #else
204 if (h->resolver) {
205 pthread_cancel(*((pthread_t *) h->resolver));
206 free(h->resolver);
207 h->resolver = NULL;
208 }
209 #endif
210
211 gg_debug(GG_DEBUG_MISC, "=> http, connecting to %s:%d\n", inet_ntoa(a), h->port);
212
213 if ((h->fd = gg_connect(&a, h->port, h->async)) == -1) {
214 gg_debug(GG_DEBUG_MISC, "=> http, connection failed (errno=%d, %s)\n", errno, strerror(errno));
215 gg_http_error(GG_ERROR_CONNECTING);
216 }
217
218 h->state = GG_STATE_CONNECTING;
219 h->check = GG_CHECK_WRITE;
220 h->timeout = GG_DEFAULT_TIMEOUT;
221
222 return 0;
223 }
224
225 if (h->state == GG_STATE_CONNECTING) {
226 int res = 0;
227 unsigned int res_size = sizeof(res);
228
229 if (h->async && (getsockopt(h->fd, SOL_SOCKET, SO_ERROR, &res, &res_size) || res)) {
230 gg_debug(GG_DEBUG_MISC, "=> http, async connection failed (errno=%d, %s)\n", (res) ? res : errno , strerror((res) ? res : errno));
231 close(h->fd);
232 h->fd = -1;
233 h->state = GG_STATE_ERROR;
234 h->error = GG_ERROR_CONNECTING;
235 if (res)
236 errno = res;
237 return 0;
238 }
239
240 gg_debug(GG_DEBUG_MISC, "=> http, connected, sending request\n");
241
242 h->state = GG_STATE_SENDING_QUERY;
243 }
244
245 if (h->state == GG_STATE_SENDING_QUERY) {
246 int res;
247
248 if ((res = write(h->fd, h->query, strlen(h->query))) < 1) {
249 gg_debug(GG_DEBUG_MISC, "=> http, write() failed (len=%d, res=%d, errno=%d)\n", strlen(h->query), res, errno);
250 gg_http_error(GG_ERROR_WRITING);
251 }
252
253 if (res < strlen(h->query)) {
254 gg_debug(GG_DEBUG_MISC, "=> http, partial header sent (led=%d, sent=%d)\n", strlen(h->query), res);
255
256 memmove(h->query, h->query + res, strlen(h->query) - res + 1);
257 h->state = GG_STATE_SENDING_QUERY;
258 h->check = GG_CHECK_WRITE;
259 h->timeout = GG_DEFAULT_TIMEOUT;
260 } else {
261 gg_debug(GG_DEBUG_MISC, "=> http, request sent (len=%d)\n", strlen(h->query));
262 free(h->query);
263 h->query = NULL;
264
265 h->state = GG_STATE_READING_HEADER;
266 h->check = GG_CHECK_READ;
267 h->timeout = GG_DEFAULT_TIMEOUT;
268 }
269
270 return 0;
271 }
272
273 if (h->state == GG_STATE_READING_HEADER) {
274 char buf[1024], *tmp;
275 int res;
276
277 if ((res = read(h->fd, buf, sizeof(buf))) == -1) {
278 gg_debug(GG_DEBUG_MISC, "=> http, reading header failed (errno=%d)\n", errno);
279 if (h->header) {
280 free(h->header);
281 h->header = NULL;
282 }
283 gg_http_error(GG_ERROR_READING);
284 }
285
286 if (!res) {
287 gg_debug(GG_DEBUG_MISC, "=> http, connection reset by peer\n");
288 if (h->header) {
289 free(h->header);
290 h->header = NULL;
291 }
292 gg_http_error(GG_ERROR_READING);
293 }
294
295 gg_debug(GG_DEBUG_MISC, "=> http, read %d bytes of header\n", res);
296
297 if (!(tmp = realloc(h->header, h->header_size + res + 1))) {
298 gg_debug(GG_DEBUG_MISC, "=> http, not enough memory for header\n");
299 free(h->header);
300 h->header = NULL;
301 gg_http_error(GG_ERROR_READING);
302 }
303
304 h->header = tmp;
305
306 memcpy(h->header + h->header_size, buf, res);
307 h->header_size += res;
308
309 gg_debug(GG_DEBUG_MISC, "=> http, header_buf=%p, header_size=%d\n", h->header, h->header_size);
310
311 h->header[h->header_size] = 0;
312
313 if ((tmp = strstr(h->header, "\r\n\r\n")) || (tmp = strstr(h->header, "\n\n"))) {
314 int sep_len = (*tmp == '\r') ? 4 : 2;
315 unsigned int left;
316 char *line;
317
318 left = h->header_size - ((long)(tmp) - (long)(h->header) + sep_len);
319
320 gg_debug(GG_DEBUG_MISC, "=> http, got all header (%d bytes, %d left)\n", h->header_size - left, left);
321
322 /* HTTP/1.1 200 OK */
323 if (strlen(h->header) < 16 || strncmp(h->header + 9, "200", 3)) {
324 gg_debug(GG_DEBUG_MISC, "=> -----BEGIN-HTTP-HEADER-----\n%s\n=> -----END-HTTP-HEADER-----\n", h->header);
325
326 gg_debug(GG_DEBUG_MISC, "=> http, didn't get 200 OK -- no results\n");
327 free(h->header);
328 h->header = NULL;
329 gg_http_error(GG_ERROR_CONNECTING);
330 }
331
332 h->body_size = 0;
333 line = h->header;
334 *tmp = 0;
335
336 gg_debug(GG_DEBUG_MISC, "=> -----BEGIN-HTTP-HEADER-----\n%s\n=> -----END-HTTP-HEADER-----\n", h->header);
337
338 while (line) {
339 if (!strncasecmp(line, "Content-length: ", 16)) {
340 h->body_size = atoi(line + 16);
341 }
342 line = strchr(line, '\n');
343 if (line)
344 line++;
345 }
346
347 if (h->body_size <= 0) {
348 gg_debug(GG_DEBUG_MISC, "=> http, content-length not found\n");
349 h->body_size = left;
350 }
351
352 if (left > h->body_size) {
353 gg_debug(GG_DEBUG_MISC, "=> http, oversized reply (%d bytes needed, %d bytes left)\n", h->body_size, left);
354 h->body_size = left;
355 }
356
357 gg_debug(GG_DEBUG_MISC, "=> http, body_size=%d\n", h->body_size);
358
359 if (!(h->body = malloc(h->body_size + 1))) {
360 gg_debug(GG_DEBUG_MISC, "=> http, not enough memory (%d bytes for body_buf)\n", h->body_size + 1);
361 free(h->header);
362 h->header = NULL;
363 gg_http_error(GG_ERROR_READING);
364 }
365
366 if (left) {
367 memcpy(h->body, tmp + sep_len, left);
368 h->body_done = left;
369 }
370
371 h->body[left] = 0;
372
373 h->state = GG_STATE_READING_DATA;
374 h->check = GG_CHECK_READ;
375 h->timeout = GG_DEFAULT_TIMEOUT;
376 }
377
378 return 0;
379 }
380
381 if (h->state == GG_STATE_READING_DATA) {
382 char buf[1024];
383 int res;
384
385 if ((res = read(h->fd, buf, sizeof(buf))) == -1) {
386 gg_debug(GG_DEBUG_MISC, "=> http, reading body failed (errno=%d)\n", errno);
387 if (h->body) {
388 free(h->body);
389 h->body = NULL;
390 }
391 gg_http_error(GG_ERROR_READING);
392 }
393
394 if (!res) {
395 if (h->body_done >= h->body_size) {
396 gg_debug(GG_DEBUG_MISC, "=> http, we're done, closing socket\n");
397 h->state = GG_STATE_PARSING;
398 close(h->fd);
399 h->fd = -1;
400 } else {
401 gg_debug(GG_DEBUG_MISC, "=> http, connection closed while reading (have %d, need %d)\n", h->body_done, h->body_size);
402 if (h->body) {
403 free(h->body);
404 h->body = NULL;
405 }
406 gg_http_error(GG_ERROR_READING);
407 }
408
409 return 0;
410 }
411
412 gg_debug(GG_DEBUG_MISC, "=> http, read %d bytes of body\n", res);
413
414 if (h->body_done + res > h->body_size) {
415 char *tmp;
416
417 gg_debug(GG_DEBUG_MISC, "=> http, too much data (%d bytes, %d needed), enlarging buffer\n", h->body_done + res, h->body_size);
418
419 if (!(tmp = realloc(h->body, h->body_done + res + 1))) {
420 gg_debug(GG_DEBUG_MISC, "=> http, not enough memory for data (%d needed)\n", h->body_done + res + 1);
421 free(h->body);
422 h->body = NULL;
423 gg_http_error(GG_ERROR_READING);
424 }
425
426 h->body = tmp;
427 h->body_size = h->body_done + res;
428 }
429
430 h->body[h->body_done + res] = 0;
431 memcpy(h->body + h->body_done, buf, res);
432 h->body_done += res;
433
434 gg_debug(GG_DEBUG_MISC, "=> body_done=%d, body_size=%d\n", h->body_done, h->body_size);
435
436 return 0;
437 }
438
439 if (h->fd != -1)
440 close(h->fd);
441
442 h->fd = -1;
443 h->state = GG_STATE_ERROR;
444 h->error = 0;
445
446 return -1;
447 }
448
449 #undef gg_http_error
450
451 /*
452 * gg_http_stop()
453 *
454 * jeśli połączenie jest w trakcie, przerywa je. nie zwalnia h->data.
455 *
456 * - h - struktura opisująca połączenie
457 */
458 void gg_http_stop(struct gg_http *h)
459 {
460 if (!h)
461 return;
462
463 if (h->state == GG_STATE_ERROR || h->state == GG_STATE_DONE)
464 return;
465
466 if (h->fd != -1)
467 close(h->fd);
468 h->fd = -1;
469 }
470
471 /*
472 * gg_http_free_fields() // funkcja wewnętrzna
473 *
474 * zwalnia pola struct gg_http, ale nie zwalnia samej struktury.
475 */
476 void gg_http_free_fields(struct gg_http *h)
477 {
478 if (!h)
479 return;
480
481 if (h->body) {
482 free(h->body);
483 h->body = NULL;
484 }
485
486 if (h->query) {
487 free(h->query);
488 h->query = NULL;
489 }
490
491 if (h->header) {
492 free(h->header);
493 h->header = NULL;
494 }
495 }
496
497 /*
498 * gg_http_free()
499 *
500 * próbuje zamknąć połączenie i zwalnia pamięć po nim.
501 *
502 * - h - struktura, którą należy zlikwidować
503 */
504 void gg_http_free(struct gg_http *h)
505 {
506 if (!h)
507 return;
508
509 gg_http_stop(h);
510 gg_http_free_fields(h);
511 free(h);
512 }
513
514 /*
515 * Local variables:
516 * c-indentation-style: k&r
517 * c-basic-offset: 8
518 * indent-tabs-mode: notnil
519 * End:
520 *
521 * vim: shiftwidth=8:
522 */

mercurial