1 /*
2 * File: http.c
3 *
4 * Copyright (C) 2000-2007 Jorge Arellano Cid <jcid@dillo.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
10 */
11
12 /*
13 * HTTP connect functions
14 */
15
16
17 #include <config.h>
18
19 #include <ctype.h> /* isdigit */
20 #include <unistd.h>
21 #include <errno.h> /* for errno */
22 #include <stdlib.h>
23 #include <fcntl.h>
24 #include <assert.h>
25 #include <sys/socket.h> /* for lots of socket stuff */
26 #include <netinet/in.h> /* for ntohl and stuff */
27 #include <arpa/inet.h> /* for inet_ntop */
28
29 #include "IO.h"
30 #include "Url.h"
31 #include "../msg.h"
32 #include "../klist.h"
33 #include "../dns.h"
34 #include "../web.hh"
35 #include "../cookies.h"
36 #include "../auth.h"
37 #include "../prefs.h"
38 #include "../misc.h"
39
40 #include "../uicmd.hh"
41
42 /* Used to send a message to the bw's status bar */
43 #define MSG_BW(web, root, ...) \
44 D_STMT_START { \
45 if (a_Web_valid((web)) && (!(root) || (web)->flags & WEB_RootUrl)) \
46 a_UIcmd_set_msg((web)->bw, __VA_ARGS__); \
47 } D_STMT_END
48
49 #define _MSG_BW(web, root, ...)
50
51 static const int HTTP_SOCKET_USE_PROXY = 0x1;
52 static const int HTTP_SOCKET_QUEUED = 0x4;
53 static const int HTTP_SOCKET_TO_BE_FREED = 0x8;
54
55 /* 'Url' and 'web' are just references (no need to deallocate them here). */
56 typedef struct {
57 int SockFD;
58 uint_t port; /* need a separate port in order to support PROXY */
59 uint_t flags;
60 DilloWeb *web; /* reference to client's web structure */
61 Dlist *addr_list; /* Holds the DNS answer */
62 int Err; /* Holds the errno of the connect() call */
63 ChainLink *Info; /* Used for CCC asynchronous operations */
64 char *connected_to; /* Used for per-host connection limit */
65 } SocketData_t;
66
67 /* Data structures and functions to queue sockets that need to be
68 * delayed due to the per host connection limit.
69 */
70 typedef struct SocketQueueEntry {
71 SocketData_t* sock;
72 struct SocketQueueEntry *next ;
73 } SocketQueueEntry_t;
74
75 typedef struct {
76 SocketQueueEntry_t *head;
77 SocketQueueEntry_t *tail;
78 } SocketQueue_t;
79
80 typedef struct {
81 char *host;
82 int active_connections;
83 SocketQueue_t queue;
84 } HostConnection_t;
85
86 static void Http_socket_queue_init(SocketQueue_t *sq);
87 static void Http_socket_enqueue(SocketQueue_t *sq, SocketData_t* sock);
88 static SocketData_t* Http_socket_dequeue(SocketQueue_t *sq);
89 static HostConnection_t *Http_host_connection_get(const char *host);
90 static void Http_host_connection_remove(HostConnection_t *hc);
91 static int Http_connect_socket(ChainLink *Info);
92 static void Http_socket_free(int SKey);
93
94 /*
95 * Local data
96 */
97 static Klist_t *ValidSocks = NULL; /* Active sockets list. It holds pointers to
98 * SocketData_t structures. */
99 static DilloUrl *HTTP_Proxy = NULL;
100 static char *HTTP_Proxy_Auth_base64 = NULL;
101 static char *HTTP_Language_hdr = NULL;
102 static Dlist *host_connections;
103
104 /*
105 * Initialize proxy vars and Accept-Language header
106 */
a_Http_init(void)107 int a_Http_init(void)
108 {
109 char *env_proxy = getenv("http_proxy");
110
111 HTTP_Language_hdr = prefs.http_language ?
112 dStrconcat("Accept-Language: ", prefs.http_language, "\r\n", NULL) :
113 dStrdup("");
114
115 if (env_proxy && strlen(env_proxy))
116 HTTP_Proxy = a_Url_new(env_proxy, NULL);
117 if (!HTTP_Proxy && prefs.http_proxy)
118 HTTP_Proxy = a_Url_dup(prefs.http_proxy);
119
120 /* This allows for storing the proxy password in "user:passwd" format
121 * in dillorc, but as this constitutes a security problem, it was disabled.
122 *
123 if (HTTP_Proxy && prefs.http_proxyuser && strchr(prefs.http_proxyuser, ':'))
124 HTTP_Proxy_Auth_base64 = a_Misc_encode_base64(prefs.http_proxyuser);
125 */
126
127 host_connections = dList_new(5);
128
129 return 0;
130 }
131
132 /*
133 * Tell whether the proxy auth is already set (user:password)
134 * Return: 1 Yes, 0 No
135 */
a_Http_proxy_auth(void)136 int a_Http_proxy_auth(void)
137 {
138 return (HTTP_Proxy_Auth_base64 ? 1 : 0);
139 }
140
141 /*
142 * Activate entered proxy password for HTTP.
143 */
a_Http_set_proxy_passwd(const char * str)144 void a_Http_set_proxy_passwd(const char *str)
145 {
146 char *http_proxyauth = dStrconcat(prefs.http_proxyuser, ":", str, NULL);
147 HTTP_Proxy_Auth_base64 = a_Misc_encode_base64(http_proxyauth);
148 dFree(http_proxyauth);
149 }
150
151 /*
152 * Create and init a new SocketData_t struct, insert into ValidSocks,
153 * and return a primary key for it.
154 */
Http_sock_new(void)155 static int Http_sock_new(void)
156 {
157 SocketData_t *S = dNew0(SocketData_t, 1);
158 return a_Klist_insert(&ValidSocks, S);
159 }
160
Http_connect_queued_sockets(HostConnection_t * hc)161 static void Http_connect_queued_sockets(HostConnection_t *hc)
162 {
163 SocketData_t *sd;
164 while (hc->active_connections < prefs.http_max_conns &&
165 (sd = Http_socket_dequeue(&hc->queue))) {
166
167 sd->flags &= ~HTTP_SOCKET_QUEUED;
168
169 if (sd->flags & HTTP_SOCKET_TO_BE_FREED) {
170 dFree(sd);
171 } else if (a_Web_valid(sd->web)) {
172 /* start connecting the socket */
173 if (Http_connect_socket(sd->Info) < 0) {
174 ChainLink *Info = sd->Info;
175 MSG_BW(sd->web, 1, "ERROR: %s", dStrerror(sd->Err));
176 a_Chain_bfcb(OpAbort, Info, NULL, "Both");
177 Http_socket_free(VOIDP2INT(Info->LocalKey)); /* free sd */
178 dFree(Info);
179 } else {
180 sd->connected_to = hc->host;
181 hc->active_connections++;
182 }
183 }
184 }
185 }
186
187 /*
188 * Free SocketData_t struct
189 */
Http_socket_free(int SKey)190 static void Http_socket_free(int SKey)
191 {
192 SocketData_t *S;
193
194 if ((S = a_Klist_get_data(ValidSocks, SKey))) {
195 a_Klist_remove(ValidSocks, SKey);
196
197 if (S->flags & HTTP_SOCKET_QUEUED) {
198 S->flags |= HTTP_SOCKET_TO_BE_FREED;
199 } else {
200 if (S->connected_to) {
201 HostConnection_t *hc = Http_host_connection_get(S->connected_to);
202 hc->active_connections--;
203 Http_connect_queued_sockets(hc);
204 if (hc->active_connections == 0)
205 Http_host_connection_remove(hc);
206 }
207 dFree(S);
208 }
209 }
210 }
211
212 /*
213 * Make the HTTP header's Referer line according to preferences
214 * (default is "host" i.e. "scheme://hostname/" )
215 */
Http_get_referer(const DilloUrl * url)216 static char *Http_get_referer(const DilloUrl *url)
217 {
218 char *referer = NULL;
219
220 if (!strcmp(prefs.http_referer, "host")) {
221 referer = dStrconcat("Referer: ", URL_SCHEME(url), "://",
222 URL_AUTHORITY(url), "/", "\r\n", NULL);
223 } else if (!strcmp(prefs.http_referer, "path")) {
224 referer = dStrconcat("Referer: ", URL_SCHEME(url), "://",
225 URL_AUTHORITY(url),
226 URL_PATH_(url) ? URL_PATH(url) : "/", "\r\n", NULL);
227 }
228 if (!referer)
229 referer = dStrdup("");
230 _MSG("http, referer='%s'\n", referer);
231 return referer;
232 }
233
234 /*
235 * Generate Content-Type header value for a POST query.
236 */
Http_make_content_type(const DilloUrl * url)237 static Dstr *Http_make_content_type(const DilloUrl *url)
238 {
239 Dstr *dstr;
240
241 if (URL_FLAGS(url) & URL_MultipartEnc) {
242 _MSG("submitting multipart/form-data!\n");
243 dstr = dStr_new("multipart/form-data; boundary=\"");
244 if (URL_DATA(url)->len > 2) {
245 /* boundary lines have "--" prepended. Skip that. */
246 const char *start = URL_DATA(url)->str + 2;
247 char *eol = strchr(start, '\r');
248 if (eol)
249 dStr_append_l(dstr, start, eol - start);
250 } else {
251 /* Zero parts; arbitrary boundary */
252 dStr_append_c(dstr, '0');
253 }
254 dStr_append_c(dstr,'"');
255 } else {
256 dstr = dStr_new("application/x-www-form-urlencoded");
257 }
258 return dstr;
259 }
260
261 /*
262 * Make the http query string
263 */
a_Http_make_query_str(const DilloUrl * url,const DilloUrl * requester,int web_flags,bool_t use_proxy)264 Dstr *a_Http_make_query_str(const DilloUrl *url, const DilloUrl *requester,
265 int web_flags, bool_t use_proxy)
266 {
267 char *ptr, *cookies, *referer, *auth;
268 Dstr *query = dStr_new(""),
269 *request_uri = dStr_new(""),
270 *proxy_auth = dStr_new("");
271
272 /* BUG: dillo doesn't actually understand application/xml yet */
273 const char *accept_hdr_value =
274 web_flags & WEB_Image ? "image/png,image/*;q=0.8,*/*;q=0.5" :
275 web_flags & WEB_Stylesheet ? "text/css,*/*;q=0.1" :
276 "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8";
277
278 if (use_proxy) {
279 dStr_sprintfa(request_uri, "%s%s",
280 URL_STR(url),
281 (URL_PATH_(url) || URL_QUERY_(url)) ? "" : "/");
282 if ((ptr = strrchr(request_uri->str, '#')))
283 dStr_truncate(request_uri, ptr - request_uri->str);
284 if (HTTP_Proxy_Auth_base64)
285 dStr_sprintf(proxy_auth, "Proxy-Authorization: Basic %s\r\n",
286 HTTP_Proxy_Auth_base64);
287 } else {
288 dStr_sprintfa(request_uri, "%s%s%s%s",
289 URL_PATH(url),
290 URL_QUERY_(url) ? "?" : "",
291 URL_QUERY(url),
292 (URL_PATH_(url) || URL_QUERY_(url)) ? "" : "/");
293 }
294
295 cookies = a_Cookies_get_query(url, requester);
296 auth = a_Auth_get_auth_str(url, request_uri->str);
297 referer = Http_get_referer(url);
298 if (URL_FLAGS(url) & URL_Post) {
299 Dstr *content_type = Http_make_content_type(url);
300 dStr_sprintfa(
301 query,
302 "POST %s HTTP/1.1\r\n"
303 "Host: %s\r\n"
304 "User-Agent: %s\r\n"
305 "Accept: %s\r\n"
306 "%s" /* language */
307 "Accept-Encoding: gzip, deflate\r\n"
308 "%s" /* auth */
309 "DNT: 1\r\n"
310 "%s" /* proxy auth */
311 "%s" /* referer */
312 "Connection: close\r\n"
313 "Content-Type: %s\r\n"
314 "Content-Length: %ld\r\n"
315 "%s" /* cookies */
316 "\r\n",
317 request_uri->str, URL_AUTHORITY(url), prefs.http_user_agent,
318 accept_hdr_value, HTTP_Language_hdr, auth ? auth : "",
319 proxy_auth->str, referer, content_type->str, (long)URL_DATA(url)->len,
320 cookies);
321 dStr_append_l(query, URL_DATA(url)->str, URL_DATA(url)->len);
322 dStr_free(content_type, TRUE);
323 } else {
324 dStr_sprintfa(
325 query,
326 "GET %s HTTP/1.1\r\n"
327 "Host: %s\r\n"
328 "User-Agent: %s\r\n"
329 "Accept: %s\r\n"
330 "%s" /* language */
331 "Accept-Encoding: gzip, deflate\r\n"
332 "%s" /* auth */
333 "DNT: 1\r\n"
334 "%s" /* proxy auth */
335 "%s" /* referer */
336 "Connection: close\r\n"
337 "%s" /* cache control */
338 "%s" /* cookies */
339 "\r\n",
340 request_uri->str, URL_AUTHORITY(url), prefs.http_user_agent,
341 accept_hdr_value, HTTP_Language_hdr, auth ? auth : "",
342 proxy_auth->str, referer,
343 (URL_FLAGS(url) & URL_E2EQuery) ?
344 "Pragma: no-cache\r\nCache-Control: no-cache\r\n" : "",
345 cookies);
346 }
347 dFree(referer);
348 dFree(cookies);
349 dFree(auth);
350
351 dStr_free(request_uri, TRUE);
352 dStr_free(proxy_auth, TRUE);
353 _MSG("Query: {%s}\n", dStr_printable(query, 8192));
354 return query;
355 }
356
357 /*
358 * Create and submit the HTTP query to the IO engine
359 */
Http_send_query(ChainLink * Info,SocketData_t * S)360 static void Http_send_query(ChainLink *Info, SocketData_t *S)
361 {
362 Dstr *query;
363 DataBuf *dbuf;
364
365 /* Create the query */
366 query = a_Http_make_query_str(S->web->url, S->web->requester, S->web->flags,
367 S->flags & HTTP_SOCKET_USE_PROXY);
368 dbuf = a_Chain_dbuf_new(query->str, query->len, 0);
369
370 /* actually this message is sent too early.
371 * It should go when the socket is ready for writing (i.e. connected) */
372 _MSG_BW(S->web, 1, "Sending query to %s...", URL_HOST_(S->web->url));
373
374 /* send query */
375 a_Chain_bcb(OpSend, Info, dbuf, NULL);
376 dFree(dbuf);
377 dStr_free(query, 1);
378 }
379
380 /*
381 * This function gets called after the DNS succeeds solving a hostname.
382 * Task: Finish socket setup and start connecting the socket.
383 * Return value: 0 on success; -1 on error.
384 */
Http_connect_socket(ChainLink * Info)385 static int Http_connect_socket(ChainLink *Info)
386 {
387 int i, status;
388 #ifdef ENABLE_IPV6
389 struct sockaddr_in6 name;
390 #else
391 struct sockaddr_in name;
392 #endif
393 SocketData_t *S;
394 DilloHost *dh;
395 socklen_t socket_len = 0;
396
397 S = a_Klist_get_data(ValidSocks, VOIDP2INT(Info->LocalKey));
398
399 /* TODO: iterate this address list until success, or end-of-list */
400 for (i = 0; (dh = dList_nth_data(S->addr_list, i)); ++i) {
401 if ((S->SockFD = socket(dh->af, SOCK_STREAM, IPPROTO_TCP)) < 0) {
402 S->Err = errno;
403 MSG("Http_connect_socket ERROR: %s\n", dStrerror(errno));
404 continue;
405 }
406 /* set NONBLOCKING and close on exec. */
407 fcntl(S->SockFD, F_SETFL, O_NONBLOCK | fcntl(S->SockFD, F_GETFL));
408 fcntl(S->SockFD, F_SETFD, FD_CLOEXEC | fcntl(S->SockFD, F_GETFD));
409
410 /* Some OSes require this... */
411 memset(&name, 0, sizeof(name));
412 /* Set remaining parms. */
413 switch (dh->af) {
414 case AF_INET:
415 {
416 struct sockaddr_in *sin = (struct sockaddr_in *)&name;
417 socket_len = sizeof(struct sockaddr_in);
418 sin->sin_family = dh->af;
419 sin->sin_port = S->port ? htons(S->port) : htons(DILLO_URL_HTTP_PORT);
420 memcpy(&sin->sin_addr, dh->data, (size_t)dh->alen);
421 if (a_Web_valid(S->web) && (S->web->flags & WEB_RootUrl))
422 MSG("Connecting to %s\n", inet_ntoa(sin->sin_addr));
423 break;
424 }
425 #ifdef ENABLE_IPV6
426 case AF_INET6:
427 {
428 char buf[128];
429 struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&name;
430 socket_len = sizeof(struct sockaddr_in6);
431 sin6->sin6_family = dh->af;
432 sin6->sin6_port =
433 S->port ? htons(S->port) : htons(DILLO_URL_HTTP_PORT);
434 memcpy(&sin6->sin6_addr, dh->data, dh->alen);
435 inet_ntop(dh->af, dh->data, buf, sizeof(buf));
436 if (a_Web_valid(S->web) && (S->web->flags & WEB_RootUrl))
437 MSG("Connecting to %s\n", buf);
438 break;
439 }
440 #endif
441 }/*switch*/
442
443 MSG_BW(S->web, 1, "Contacting host...");
444 status = connect(S->SockFD, (struct sockaddr *)&name, socket_len);
445 if (status == -1 && errno != EINPROGRESS) {
446 S->Err = errno;
447 dClose(S->SockFD);
448 MSG("Http_connect_socket ERROR: %s\n", dStrerror(S->Err));
449 } else {
450 a_Chain_bcb(OpSend, Info, &S->SockFD, "FD");
451 a_Chain_fcb(OpSend, Info, &S->SockFD, "FD");
452 Http_send_query(S->Info, S);
453 return 0; /* Success */
454 }
455 }
456
457 return -1;
458 }
459
460 /*
461 * Test proxy settings and check the no_proxy domains list
462 * Return value: whether to use proxy or not.
463 */
Http_must_use_proxy(const DilloUrl * url)464 static int Http_must_use_proxy(const DilloUrl *url)
465 {
466 char *np, *p, *tok;
467 int ret = 0;
468
469 if (HTTP_Proxy) {
470 ret = 1;
471 if (prefs.no_proxy) {
472 const char *host = URL_HOST(url);
473 size_t host_len = strlen(host);
474
475 np = dStrdup(prefs.no_proxy);
476 for (p = np; (tok = dStrsep(&p, " ")); ) {
477 int start = host_len - strlen(tok);
478
479 if (start >= 0 && dStrAsciiCasecmp(host + start, tok) == 0) {
480 /* no_proxy token is suffix of host string */
481 ret = 0;
482 break;
483 }
484 }
485 dFree(np);
486 }
487 }
488 _MSG("Http_must_use_proxy: %s\n %s\n", URL_STR(url), ret ? "YES":"NO");
489 return ret;
490 }
491
492 /*
493 * Return a new string for the request used to tunnel HTTPS through a proxy.
494 * As of 2009, the best reference appears to be section 5 of RFC 2817.
495 */
a_Http_make_connect_str(const DilloUrl * url)496 char *a_Http_make_connect_str(const DilloUrl *url)
497 {
498 Dstr *dstr;
499 const char *auth1;
500 int auth_len;
501 char *auth2, *proxy_auth, *retstr;
502
503 dReturn_val_if_fail(Http_must_use_proxy(url), NULL);
504
505 dstr = dStr_new("");
506 auth1 = URL_AUTHORITY(url);
507 auth_len = strlen(auth1);
508 if (auth_len > 0 && !isdigit(auth1[auth_len - 1]))
509 /* if no port number, add HTTPS port */
510 auth2 = dStrconcat(auth1, ":443", NULL);
511 else
512 auth2 = dStrdup(auth1);
513 proxy_auth = HTTP_Proxy_Auth_base64 ?
514 dStrconcat ("Proxy-Authorization: Basic ",
515 HTTP_Proxy_Auth_base64, "\r\n", NULL) :
516 dStrdup("");
517 dStr_sprintfa(
518 dstr,
519 "CONNECT %s HTTP/1.1\r\n"
520 "Host: %s\r\n"
521 "%s"
522 "\r\n",
523 auth2,
524 auth2,
525 proxy_auth);
526
527 dFree(auth2);
528 dFree(proxy_auth);
529 retstr = dstr->str;
530 dStr_free(dstr, 0);
531 return retstr;
532 }
533
534 /*
535 * Return URL string of HTTP proxy, if any
536 */
a_Http_get_proxy_urlstr()537 const char *a_Http_get_proxy_urlstr()
538 {
539 return HTTP_Proxy ? URL_STR(HTTP_Proxy) : NULL;
540 }
541
542 /*
543 * Callback function for the DNS resolver.
544 * Continue connecting the socket, or abort upon error condition.
545 * S->web is checked to assert the operation wasn't aborted while waiting.
546 */
Http_dns_cb(int Status,Dlist * addr_list,void * data)547 static void Http_dns_cb(int Status, Dlist *addr_list, void *data)
548 {
549 int SKey = VOIDP2INT(data);
550 SocketData_t *S;
551 HostConnection_t *hc;
552
553 S = a_Klist_get_data(ValidSocks, SKey);
554 if (S) {
555 if (!a_Web_valid(S->web)) {
556 a_Chain_bfcb(OpAbort, S->Info, NULL, "Both");
557 dFree(S->Info);
558 Http_socket_free(SKey);
559
560 } else if (Status == 0 && addr_list) {
561 /* Successful DNS answer; save the IP */
562 S->addr_list = addr_list;
563 S->flags |= HTTP_SOCKET_QUEUED;
564 if (S->flags & HTTP_SOCKET_USE_PROXY)
565 hc = Http_host_connection_get(URL_HOST(HTTP_Proxy));
566 else
567 hc = Http_host_connection_get(URL_HOST(S->web->url));
568 Http_socket_enqueue(&hc->queue, S);
569 Http_connect_queued_sockets(hc);
570 } else {
571 /* DNS wasn't able to resolve the hostname */
572 MSG_BW(S->web, 0, "ERROR: Dns can't resolve %s",
573 (S->flags & HTTP_SOCKET_USE_PROXY) ? URL_HOST_(HTTP_Proxy) :
574 URL_HOST_(S->web->url));
575 a_Chain_bfcb(OpAbort, S->Info, NULL, "Both");
576 dFree(S->Info);
577 Http_socket_free(SKey);
578 }
579 }
580 }
581
582 /*
583 * Asynchronously create a new http connection for 'Url'
584 * We'll set some socket parameters; the rest will be set later
585 * when the IP is known.
586 * ( Data1 = Web structure )
587 * Return value: 0 on success, -1 otherwise
588 */
Http_get(ChainLink * Info,void * Data1)589 static int Http_get(ChainLink *Info, void *Data1)
590 {
591 SocketData_t *S;
592 char *hostname;
593
594 S = a_Klist_get_data(ValidSocks, VOIDP2INT(Info->LocalKey));
595 /* Reference Web data */
596 S->web = Data1;
597 /* Reference Info data */
598 S->Info = Info;
599
600 /* Proxy support */
601 if (Http_must_use_proxy(S->web->url)) {
602 hostname = dStrdup(URL_HOST(HTTP_Proxy));
603 S->port = URL_PORT(HTTP_Proxy);
604 S->flags |= HTTP_SOCKET_USE_PROXY;
605 } else {
606 hostname = dStrdup(URL_HOST(S->web->url));
607 S->port = URL_PORT(S->web->url);
608 S->flags &= ~HTTP_SOCKET_USE_PROXY;
609 }
610
611 /* Let the user know what we'll do */
612 MSG_BW(S->web, 1, "DNS resolving %s", URL_HOST_(S->web->url));
613
614 /* Let the DNS engine resolve the hostname, and when done,
615 * we'll try to connect the socket from the callback function */
616 a_Dns_resolve(hostname, Http_dns_cb, Info->LocalKey);
617
618 dFree(hostname);
619 return 0;
620 }
621
622 /*
623 * CCC function for the HTTP module
624 */
a_Http_ccc(int Op,int Branch,int Dir,ChainLink * Info,void * Data1,void * Data2)625 void a_Http_ccc(int Op, int Branch, int Dir, ChainLink *Info,
626 void *Data1, void *Data2)
627 {
628 int SKey = VOIDP2INT(Info->LocalKey);
629
630 (void)Data2; /* suppress unused parameter warning */
631
632 dReturn_if_fail( a_Chain_check("a_Http_ccc", Op, Branch, Dir, Info) );
633
634 if (Branch == 1) {
635 if (Dir == BCK) {
636 /* HTTP query branch */
637 switch (Op) {
638 case OpStart:
639 /* ( Data1 = Web ) */
640 SKey = Http_sock_new();
641 Info->LocalKey = INT2VOIDP(SKey);
642 /* link IO */
643 a_Chain_link_new(Info, a_Http_ccc, BCK, a_IO_ccc, 1, 1);
644 a_Chain_bcb(OpStart, Info, NULL, NULL);
645 /* async. connection */
646 Http_get(Info, Data1);
647 break;
648 case OpEnd:
649 /* finished the HTTP query branch */
650 a_Chain_bcb(OpEnd, Info, NULL, NULL);
651 Http_socket_free(SKey);
652 dFree(Info);
653 break;
654 case OpAbort:
655 /* something bad happened... */
656 a_Chain_bcb(OpAbort, Info, NULL, NULL);
657 Http_socket_free(SKey);
658 dFree(Info);
659 break;
660 }
661 } else { /* 1 FWD */
662 /* HTTP send-query status branch */
663 switch (Op) {
664 default:
665 MSG_WARN("Unused CCC\n");
666 break;
667 }
668 }
669 }
670 }
671
672
Http_socket_queue_init(SocketQueue_t * sq)673 static void Http_socket_queue_init(SocketQueue_t *sq)
674 {
675 sq->head = NULL;
676 sq->tail = NULL;
677 }
678
Http_socket_enqueue(SocketQueue_t * sq,SocketData_t * sock)679 static void Http_socket_enqueue(SocketQueue_t *sq, SocketData_t* sock)
680 {
681 SocketQueueEntry_t *se = dNew(SocketQueueEntry_t, 1);
682
683 se->sock = sock;
684 se->next = NULL;
685
686 if (sq->tail)
687 sq->tail->next = se;
688 sq->tail = se;
689
690 if (! sq->head)
691 sq->head = se;
692 }
693
Http_socket_dequeue(SocketQueue_t * sq)694 static SocketData_t* Http_socket_dequeue(SocketQueue_t *sq)
695 {
696 SocketQueueEntry_t *se = sq->head;
697 SocketData_t *sd = NULL;
698
699 if (se) {
700 sq->head = se->next;
701 if (sq->tail == se)
702 sq->tail = NULL;
703 sd = se->sock;
704 dFree(se);
705 }
706
707 return sd;
708 }
709
Http_host_connection_get(const char * host)710 static HostConnection_t *Http_host_connection_get(const char *host)
711 {
712 int i;
713 HostConnection_t *hc;
714
715 for (i = 0; i < dList_length(host_connections); i++) {
716 hc = (HostConnection_t*) dList_nth_data(host_connections, i);
717
718 if (dStrAsciiCasecmp(host, hc->host) == 0)
719 return hc;
720 }
721
722 hc = dNew0(HostConnection_t, 1);
723 Http_socket_queue_init(&hc->queue);
724 hc->host = dStrdup(host);
725 dList_append(host_connections, hc);
726
727 return hc;
728 }
729
Http_host_connection_remove(HostConnection_t * hc)730 static void Http_host_connection_remove(HostConnection_t *hc)
731 {
732 assert(hc->queue.head == NULL);
733 dList_remove_fast(host_connections, hc);
734 dFree(hc->host);
735 dFree(hc);
736 }
737
Http_host_connection_remove_all()738 static void Http_host_connection_remove_all()
739 {
740 HostConnection_t *hc;
741
742 while (dList_length(host_connections) > 0) {
743 hc = (HostConnection_t*) dList_nth_data(host_connections, 0);
744 while (Http_socket_dequeue(&hc->queue));
745 Http_host_connection_remove(hc);
746 }
747 dList_free(host_connections);
748 }
749
750 /*
751 * Deallocate memory used by http module
752 * (Call this one at exit time)
753 */
a_Http_freeall(void)754 void a_Http_freeall(void)
755 {
756 Http_host_connection_remove_all();
757 a_Klist_free(&ValidSocks);
758 a_Url_free(HTTP_Proxy);
759 dFree(HTTP_Proxy_Auth_base64);
760 dFree(HTTP_Language_hdr);
761 }
762