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