14192
|
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 */
|