comparison libpurple/protocols/gg/lib/http.c @ 15373:5fe8042783c1

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