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