IJB now changes HTTP/1.1 to HTTP/1.0 in requests and answers.
[privoxy.git] / parsers.c
1 const char parsers_rcs[] = "$Id: parsers.c,v 1.29 2001/09/24 21:09:24 jongfoster Exp $";
2 /*********************************************************************
3  *
4  * File        :  $Source: /cvsroot/ijbswa/current/parsers.c,v $
5  *
6  * Purpose     :  Declares functions to parse/crunch headers and pages.
7  *                Functions declared include:
8  *                   `add_to_iob', `client_cookie_adder', `client_from',
9  *                   `client_referrer', `client_send_cookie', `client_ua',
10  *                   `client_uagent', `client_x_forwarded',
11  *                   `client_x_forwarded_adder', `client_xtra_adder',
12  *                   `content_type', `crumble', `destroy_list', `enlist',
13  *                   `flush_socket', `free_http_request', `get_header',
14  *                   `list_to_text', `parse_http_request', `sed',
15  *                   and `server_set_cookie'.
16  *
17  * Copyright   :  Written by and Copyright (C) 2001 the SourceForge
18  *                IJBSWA team.  http://ijbswa.sourceforge.net
19  *
20  *                Based on the Internet Junkbuster originally written
21  *                by and Copyright (C) 1997 Anonymous Coders and 
22  *                Junkbusters Corporation.  http://www.junkbusters.com
23  *
24  *                This program is free software; you can redistribute it 
25  *                and/or modify it under the terms of the GNU General
26  *                Public License as published by the Free Software
27  *                Foundation; either version 2 of the License, or (at
28  *                your option) any later version.
29  *
30  *                This program is distributed in the hope that it will
31  *                be useful, but WITHOUT ANY WARRANTY; without even the
32  *                implied warranty of MERCHANTABILITY or FITNESS FOR A
33  *                PARTICULAR PURPOSE.  See the GNU General Public
34  *                License for more details.
35  *
36  *                The GNU General Public License should be included with
37  *                this file.  If not, you can view it at
38  *                http://www.gnu.org/copyleft/gpl.html
39  *                or write to the Free Software Foundation, Inc., 59
40  *                Temple Place - Suite 330, Boston, MA  02111-1307, USA.
41  *
42  * Revisions   :
43  *    $Log: parsers.c,v $
44  *    Revision 1.29  2001/09/24 21:09:24  jongfoster
45  *    Fixing 2 memory leaks that Guy spotted, where the paramater to
46  *    enlist() was not being free()d.
47  *
48  *    Revision 1.28  2001/09/22 16:32:28  jongfoster
49  *    Removing unused #includes.
50  *
51  *    Revision 1.27  2001/09/20 15:45:25  steudten
52  *
53  *    add casting from size_t to int for printf()
54  *    remove local variable shadow s2
55  *
56  *    Revision 1.26  2001/09/16 17:05:14  jongfoster
57  *    Removing unused #include showarg.h
58  *
59  *    Revision 1.25  2001/09/16 13:21:27  jongfoster
60  *    Changes to use new list functions.
61  *
62  *    Revision 1.24  2001/09/13 23:05:50  jongfoster
63  *    Changing the string paramater to the header parsers a "const".
64  *
65  *    Revision 1.23  2001/09/12 18:08:19  steudten
66  *
67  *    In parse_http_request() header rewriting miss the host value, so
68  *    from http://www.mydomain.com the result was just " / " not
69  *    http://www.mydomain.com/ in case we forward.
70  *
71  *    Revision 1.22  2001/09/10 10:58:53  oes
72  *    Silenced compiler warnings
73  *
74  *    Revision 1.21  2001/07/31 14:46:00  oes
75  *     - Persistant connections now suppressed
76  *     - sed() no longer appends empty header to csp->headers
77  *
78  *    Revision 1.20  2001/07/30 22:08:36  jongfoster
79  *    Tidying up #defines:
80  *    - All feature #defines are now of the form FEATURE_xxx
81  *    - Permanently turned off WIN_GUI_EDIT
82  *    - Permanently turned on WEBDAV and SPLIT_PROXY_ARGS
83  *
84  *    Revision 1.19  2001/07/25 17:21:54  oes
85  *    client_uagent now saves copy of User-Agent: header value
86  *
87  *    Revision 1.18  2001/07/13 14:02:46  oes
88  *     - Included fix to repair broken HTTP requests that
89  *       don't contain a path, not even '/'.
90  *     - Removed all #ifdef PCRS
91  *     - content_type now always inspected and classified as
92  *       text, gif or other.
93  *     - formatting / comments
94  *
95  *    Revision 1.17  2001/06/29 21:45:41  oes
96  *    Indentation, CRLF->LF, Tab-> Space
97  *
98  *    Revision 1.16  2001/06/29 13:32:42  oes
99  *    - Fixed a comment
100  *    - Adapted free_http_request
101  *    - Removed logentry from cancelled commit
102  *
103  *    Revision 1.15  2001/06/03 19:12:38  oes
104  *    deleted const struct interceptors
105  *
106  *    Revision 1.14  2001/06/01 18:49:17  jongfoster
107  *    Replaced "list_share" with "list" - the tiny memory gain was not
108  *    worth the extra complexity.
109  *
110  *    Revision 1.13  2001/05/31 21:30:33  jongfoster
111  *    Removed list code - it's now in list.[ch]
112  *    Renamed "permission" to "action", and changed many features
113  *    to use the actions file rather than the global config.
114  *
115  *    Revision 1.12  2001/05/31 17:33:13  oes
116  *
117  *    CRLF -> LF
118  *
119  *    Revision 1.11  2001/05/29 20:11:19  joergs
120  *    '/ * inside comment' warning removed.
121  *
122  *    Revision 1.10  2001/05/29 09:50:24  jongfoster
123  *    Unified blocklist/imagelist/permissionslist.
124  *    File format is still under discussion, but the internal changes
125  *    are (mostly) done.
126  *
127  *    Also modified interceptor behaviour:
128  *    - We now intercept all URLs beginning with one of the following
129  *      prefixes (and *only* these prefixes):
130  *        * http://i.j.b/
131  *        * http://ijbswa.sf.net/config/
132  *        * http://ijbswa.sourceforge.net/config/
133  *    - New interceptors "home page" - go to http://i.j.b/ to see it.
134  *    - Internal changes so that intercepted and fast redirect pages
135  *      are not replaced with an image.
136  *    - Interceptors now have the option to send a binary page direct
137  *      to the client. (i.e. ijb-send-banner uses this)
138  *    - Implemented show-url-info interceptor.  (Which is why I needed
139  *      the above interceptors changes - a typical URL is
140  *      "http://i.j.b/show-url-info?url=www.somesite.com/banner.gif".
141  *      The previous mechanism would not have intercepted that, and
142  *      if it had been intercepted then it then it would have replaced
143  *      it with an image.)
144  *
145  *    Revision 1.9  2001/05/28 17:26:33  jongfoster
146  *    Fixing segfault if last header was crunched.
147  *    Fixing Windows build (snprintf() is _snprintf() under Win32, but we
148  *    can use the cross-platform sprintf() instead.)
149  *
150  *    Revision 1.8  2001/05/27 22:17:04  oes
151  *
152  *    - re_process_buffer no longer writes the modified buffer
153  *      to the client, which was very ugly. It now returns the
154  *      buffer, which it is then written by chat.
155  *
156  *    - content_length now adjusts the Content-Length: header
157  *      for modified documents rather than crunch()ing it.
158  *      (Length info in csp->content_length, which is 0 for
159  *      unmodified documents)
160  *
161  *    - For this to work, sed() is called twice when filtering.
162  *
163  *    Revision 1.7  2001/05/27 13:19:06  oes
164  *    Patched Joergs solution for the content-length in.
165  *
166  *    Revision 1.6  2001/05/26 13:39:32  jongfoster
167  *    Only crunches Content-Length header if applying RE filtering.
168  *    Without this fix, Microsoft Windows Update wouldn't work.
169  *
170  *    Revision 1.5  2001/05/26 00:28:36  jongfoster
171  *    Automatic reloading of config file.
172  *    Removed obsolete SIGHUP support (Unix) and Reload menu option (Win32).
173  *    Most of the global variables have been moved to a new
174  *    struct configuration_spec, accessed through csp->config->globalname
175  *    Most of the globals remaining are used by the Win32 GUI.
176  *
177  *    Revision 1.4  2001/05/22 18:46:04  oes
178  *
179  *    - Enabled filtering banners by size rather than URL
180  *      by adding patterns that replace all standard banner
181  *      sizes with the "Junkbuster" gif to the re_filterfile
182  *
183  *    - Enabled filtering WebBugs by providing a pattern
184  *      which kills all 1x1 images
185  *
186  *    - Added support for PCRE_UNGREEDY behaviour to pcrs,
187  *      which is selected by the (nonstandard and therefore
188  *      capital) letter 'U' in the option string.
189  *      It causes the quantifiers to be ungreedy by default.
190  *      Appending a ? turns back to greedy (!).
191  *
192  *    - Added a new interceptor ijb-send-banner, which
193  *      sends back the "Junkbuster" gif. Without imagelist or
194  *      MSIE detection support, or if tinygif = 1, or the
195  *      URL isn't recognized as an imageurl, a lame HTML
196  *      explanation is sent instead.
197  *
198  *    - Added new feature, which permits blocking remote
199  *      script redirects and firing back a local redirect
200  *      to the browser.
201  *      The feature is conditionally compiled, i.e. it
202  *      can be disabled with --disable-fast-redirects,
203  *      plus it must be activated by a "fast-redirects"
204  *      line in the config file, has its own log level
205  *      and of course wants to be displayed by show-proxy-args
206  *      Note: Boy, all the #ifdefs in 1001 locations and
207  *      all the fumbling with configure.in and acconfig.h
208  *      were *way* more work than the feature itself :-(
209  *
210  *    - Because a generic redirect template was needed for
211  *      this, tinygif = 3 now uses the same.
212  *
213  *    - Moved GIFs, and other static HTTP response templates
214  *      to project.h
215  *
216  *    - Some minor fixes
217  *
218  *    - Removed some >400 CRs again (Jon, you really worked
219  *      a lot! ;-)
220  *
221  *    Revision 1.3  2001/05/20 01:21:20  jongfoster
222  *    Version 2.9.4 checkin.
223  *    - Merged popupfile and cookiefile, and added control over PCRS
224  *      filtering, in new "permissionsfile".
225  *    - Implemented LOG_LEVEL_FATAL, so that if there is a configuration
226  *      file error you now get a message box (in the Win32 GUI) rather
227  *      than the program exiting with no explanation.
228  *    - Made killpopup use the PCRS MIME-type checking and HTTP-header
229  *      skipping.
230  *    - Removed tabs from "config"
231  *    - Moved duplicated url parsing code in "loaders.c" to a new funcition.
232  *    - Bumped up version number.
233  *
234  *    Revision 1.2  2001/05/17 23:02:36  oes
235  *     - Made referrer option accept 'L' as a substitute for 'ยง'
236  *
237  *    Revision 1.1.1.1  2001/05/15 13:59:01  oes
238  *    Initial import of version 2.9.3 source tree
239  *
240  *
241  *********************************************************************/
242 \f
243
244 #include "config.h"
245
246 #ifndef _WIN32
247 #include <stdio.h>
248 #include <sys/types.h>
249 #include <stdlib.h>
250 #include <ctype.h>
251 #endif
252
253 #include <string.h>
254
255 #ifndef _WIN32
256 #include <unistd.h>
257 #endif
258
259 #include "project.h"
260 #include "list.h"
261 #include "parsers.h"
262 #include "encode.h"
263 #include "ssplit.h"
264 #include "errlog.h"
265 #include "jbsockets.h"
266 #include "miscutil.h"
267
268 const char parsers_h_rcs[] = PARSERS_H_VERSION;
269
270 /* Fix a problem with Solaris.  There should be no effect on other
271  * platforms.
272  * Solaris's isspace() is a macro which uses it's argument directly
273  * as an array index.  Therefore we need to make sure that high-bit
274  * characters generate +ve values, and ideally we also want to make
275  * the argument match the declared parameter type of "int".
276  * 
277  * Why did they write a character function that can't take a simple 
278  * "char" argument?  Doh!
279  */
280 #define ijb_isupper(__X) isupper((int)(unsigned char)(__X))
281 #define ijb_tolower(__X) tolower((int)(unsigned char)(__X))
282
283
284 const struct parsers client_patterns[] = {
285    { "referer:",                 8,    client_referrer },
286    { "user-agent:",              11,   client_uagent },
287    { "ua-",                      3,    client_ua },
288    { "from:",                    5,    client_from },
289    { "cookie:",                  7,    client_send_cookie },
290    { "x-forwarded-for:",         16,   client_x_forwarded },
291 #ifdef FEATURE_DENY_GZIP
292    { "Accept-Encoding: gzip",    21,   crumble },
293 #endif /* def FEATURE_DENY_GZIP */
294 #if defined(FEATURE_IMAGE_DETECT_MSIE)
295    { "Accept:",                   7,   client_accept },
296 #endif /* defined(FEATURE_IMAGE_DETECT_MSIE) */
297 #ifdef FEATURE_FORCE_LOAD
298    { "Host:",                     5,   client_host },
299 #endif /* def FEATURE_FORCE_LOAD */
300 /* { "if-modified-since:",       18,   crumble }, */
301    { "Keep-Alive:",              11,   crumble },
302    { "connection:",              11,   crumble },
303    { "proxy-connection:",        17,   crumble },        
304    { NULL,                       0,    NULL }
305 };
306
307
308 const struct parsers server_patterns[] = {
309    { "HTTP/1.1 ",           9, server_http11 },
310    { "set-cookie:",        11, server_set_cookie },
311    { "connection:",        11, crumble },
312    { "Content-Type:",      13, content_type },
313    { "Content-Length:",    15, content_length },
314    { NULL, 0, NULL }
315 };
316
317
318 void (* const add_client_headers[])(struct client_state *) = {
319    client_cookie_adder,
320    client_x_forwarded_adder,
321    client_xtra_adder,
322    connection_close_adder,   
323    NULL
324 };
325
326
327 void (* const add_server_headers[])(struct client_state *) = {
328    connection_close_adder, 
329    NULL
330 };
331
332
333 /*********************************************************************
334  *
335  * Function    :  flush_socket
336  *
337  * Description :  Write any pending "buffered" content.
338  *
339  * Parameters  :
340  *          1  :  fd = file descriptor of the socket to read
341  *          2  :  csp = Current client state (buffers, headers, etc...)
342  *
343  * Returns     :  On success, the number of bytes written are returned (zero
344  *                indicates nothing was written).  On error, -1 is returned,
345  *                and errno is set appropriately.  If count is zero and the
346  *                file descriptor refers to a regular file, 0 will be
347  *                returned without causing any other effect.  For a special
348  *                file, the results are not portable.
349  *
350  *********************************************************************/
351 int flush_socket(int fd, struct client_state *csp)
352 {
353    struct iob *iob = csp->iob;
354    int n = iob->eod - iob->cur;
355
356    if (n <= 0)
357    {
358       return(0);
359    }
360
361    n = write_socket(fd, iob->cur, n);
362    iob->eod = iob->cur = iob->buf;
363    return(n);
364
365 }
366
367
368 /*********************************************************************
369  *
370  * Function    :  add_to_iob
371  *
372  * Description :  Add content to the buffered page.
373  *
374  * Parameters  :
375  *          1  :  csp = Current client state (buffers, headers, etc...)
376  *          2  :  buf = holds the content to be added to the page
377  *          3  :  n = number of bytes to be added
378  *
379  * Returns     :  Number of bytes in the content buffer.
380  *
381  *********************************************************************/
382 int add_to_iob(struct client_state *csp, char *buf, int n)
383 {
384    struct iob *iob = csp->iob;
385    int have, need;
386    char *p;
387
388    have = iob->eod - iob->cur;
389
390    if (n <= 0)
391    {
392       return(have);
393    }
394
395    need = have + n;
396
397    if ((p = (char *)malloc(need + 1)) == NULL)
398    {
399       log_error(LOG_LEVEL_ERROR, "malloc() iob failed: %E");
400       return(-1);
401    }
402
403    if (have)
404    {
405       /* there is something in the buffer - save it */
406       memcpy(p, iob->cur, have);
407
408       /* replace the buffer with the new space */
409       freez(iob->buf);
410       iob->buf = p;
411
412       /* point to the end of the data */
413       p += have;
414    }
415    else
416    {
417       /* the buffer is empty, free it and reinitialize */
418       freez(iob->buf);
419       iob->buf = p;
420    }
421
422    /* copy the new data into the iob buffer */
423    memcpy(p, buf, n);
424
425    /* point to the end of the data */
426    p += n;
427
428    /* null terminate == cheap insurance */
429    *p = '\0';
430
431    /* set the pointers to the new values */
432    iob->cur = iob->buf;
433    iob->eod = p;
434
435    return(need);
436
437 }
438
439
440 /*********************************************************************
441  *
442  * Function    :  get_header
443  *
444  * Description :  This (odd) routine will parse the csp->iob
445  *
446  * Parameters  :
447  *          1  :  csp = Current client state (buffers, headers, etc...)
448  *
449  * Returns     :  Any one of the following:
450  *
451  * 1) a pointer to a dynamically allocated string that contains a header line
452  * 2) NULL  indicating that the end of the header was reached
453  * 3) ""    indicating that the end of the iob was reached before finding
454  *          a complete header line.
455  *
456  *********************************************************************/
457 char *get_header(struct client_state *csp)
458 {
459    struct iob *iob;
460    char *p, *q, *ret;
461    iob = csp->iob;
462
463    if ((iob->cur == NULL)
464       || ((p = strchr(iob->cur, '\n')) == NULL))
465    {
466       return(""); /* couldn't find a complete header */
467    }
468
469    *p = '\0';
470
471    ret = strdup(iob->cur);
472
473    iob->cur = p+1;
474
475    if ((q = strchr(ret, '\r'))) *q = '\0';
476
477    /* is this a blank linke (i.e. the end of the header) ? */
478    if (*ret == '\0')
479    {
480       freez(ret);
481       return(NULL);
482    }
483
484    return(ret);
485
486 }
487
488
489 /*********************************************************************
490  *
491  * Function    :  sed
492  *
493  * Description :  add, delete or modify lines in the HTTP header streams.
494  *                On entry, it receives a linked list of headers space
495  *                that was allocated dynamically (both the list nodes
496  *                and the header contents).
497  *
498  *                As a side effect it frees the space used by the original
499  *                header lines.
500  *
501  * Parameters  :
502  *          1  :  pats = list of patterns to match against headers
503  *          2  :  more_headers = list of functions to add more
504  *                headers (client or server)
505  *          3  :  csp = Current client state (buffers, headers, etc...)
506  *
507  * Returns     :  Single pointer to a fully formed header.
508  *
509  *********************************************************************/
510 char *sed(const struct parsers pats[], void (* const more_headers[])(struct client_state *), struct client_state *csp)
511 {
512    struct list_entry *p;
513    const struct parsers *v;
514    char *hdr;
515    void (* const *f)();
516
517    for (v = pats; v->str ; v++)
518    {
519       for (p = csp->headers->first; p ; p = p->next)
520       {
521          /* Header crunch()ed in previous run? -> ignore */
522          if (p->str == NULL) continue;
523
524          if (v == pats) log_error(LOG_LEVEL_HEADER, "scan: %s", p->str);
525
526          if (strncmpic(p->str, v->str, v->len) == 0)
527          {
528             hdr = v->parser(v, p->str, csp);
529             freez((char *)p->str); /* FIXME: Yuck! patching a list...*/
530             p->str = hdr;
531          }
532       }
533    }
534
535    /* place any additional headers on the csp->headers list */
536    for (f = more_headers; *f ; f++)
537    {
538       (*f)(csp);
539    }
540
541    hdr = list_to_text(csp->headers);
542
543    return(hdr);
544
545 }
546
547
548 /*********************************************************************
549  *
550  * Function    :  free_http_request
551  *
552  * Description :  Freez a http_request structure
553  *
554  * Parameters  :
555  *          1  :  http = points to a http_request structure to free
556  *
557  * Returns     :  N/A
558  *
559  *********************************************************************/
560 void free_http_request(struct http_request *http)
561 {
562    freez(http->cmd);
563    freez(http->gpc);
564    freez(http->host);
565    freez(http->hostport);
566    freez(http->path);
567    freez(http->ver);
568    freez(http->host_ip_addr_str);
569    freez(http->user_agent);
570
571 }
572
573
574 /*********************************************************************
575  *
576  * Function    :  parse_http_request
577  *
578  * Description :  Parse out the host and port from the URL.  Find the
579  *                hostname & path, port (if ':'), and/or password (if '@')
580  *
581  * Parameters  :
582  *          1  :  req = URL (or is it URI?) to break down
583  *          2  :  http = pointer to the http structure to hold elements
584  *          3  :  csp = Current client state (buffers, headers, etc...)
585  *
586  * Returns     :  N/A
587  *
588  *********************************************************************/
589 void parse_http_request(char *req, struct http_request *http, struct client_state *csp)
590 {
591    char *buf, *v[10], *url, *p, *save_url;
592    int n;
593
594    memset(http, '\0', sizeof(*http));
595
596    http->cmd = strdup(req);
597
598    buf = strdup(req);
599    n = ssplit(buf, " \r\n", v, SZ(v), 1, 1);
600
601    if (n == 3)
602    {
603       /* this could be a CONNECT request */
604       if (strcmpic(v[0], "connect") == 0)
605       {
606          http->ssl      = 1;
607          http->gpc      = strdup(v[0]);
608          http->hostport = strdup(v[1]);
609          http->ver      = strdup(v[2]);
610       }
611
612       /* or it could be a GET or a POST (possibly webDAV too) */
613       if ((0 == strcmpic(v[0], "get"))
614        || (0 == strcmpic(v[0], "head"))
615        || (0 == strcmpic(v[0], "post"))
616        /* These are the headers as defined in RFC2518 to add webDAV support: */
617        || (0 == strcmpic(v[0], "propfind"))
618        || (0 == strcmpic(v[0], "proppatch"))
619        || (0 == strcmpic(v[0], "move"))
620        || (0 == strcmpic(v[0], "copy"))
621        || (0 == strcmpic(v[0], "mkcol"))
622        || (0 == strcmpic(v[0], "lock"))
623        || (0 == strcmpic(v[0], "unlock"))
624        )
625       {
626          http->ssl    = 0;
627          http->gpc    = strdup(v[0]);
628          url          = v[1];
629          /* since we don't support HTTP/1.1 we must not send it */
630          if (!strcmpic(v[2], "HTTP/1.1"))
631          {
632             http->ver = strdup("HTTP/1.0");
633             /* change cmd too (forwaring) */
634             freez(http->cmd);
635             http->cmd = strsav(http->cmd, http->gpc);
636             http->cmd = strsav(http->cmd, " ");
637             http->cmd = strsav(http->cmd, url);
638             http->cmd = strsav(http->cmd, " ");
639             http->cmd = strsav(http->cmd, http->ver);
640          }
641          else
642          {
643             http->ver = strdup(v[2]);
644          }
645
646          save_url = url;
647          if (strncmpic(url, "http://",  7) == 0)
648          {
649             url += 7;
650          }
651          else if (strncmpic(url, "https://", 8) == 0)
652          {
653             url += 8;
654          }
655          else
656          {
657             url = NULL;
658          }
659
660          if (url)
661          {
662             if ((p = strchr(url, '/')))
663             {
664                http->path = strdup(p);
665                *p = '\0';
666                http->hostport = strdup(url);
667             }
668             /* 
669              * Repair broken HTTP requests that don't contain a path
670              */
671             else
672             {
673                /* Repair hostport & path */
674                http->path = strdup("/");
675                http->hostport = strdup(url);
676
677                /* Even repair cmd in case we're just forwarding. Boy are we nice ;-)  */
678                freez(http->cmd);
679                http->cmd = strsav(http->cmd, http->gpc);
680                http->cmd = strsav(http->cmd, " ");
681                http->cmd = strsav(http->cmd, save_url);
682                http->cmd = strsav(http->cmd, "/ ");
683                http->cmd = strsav(http->cmd, http->ver);
684             }
685          }
686       }
687    }
688
689    freez(buf);
690
691
692    if (http->hostport == NULL)
693    {
694       free_http_request(http);
695       return;
696    }
697
698    buf = strdup(http->hostport);
699
700
701    /* check if url contains password */
702    n = ssplit(buf, "@", v, SZ(v), 1, 1);
703    if (n == 2)
704    {
705       char * newbuf = NULL;
706       newbuf = strdup(v[1]);
707       freez(buf);
708       buf = newbuf;
709    }
710
711    n = ssplit(buf, ":", v, SZ(v), 1, 1);
712
713    if (n == 1)
714    {
715       http->host = strdup(v[0]);
716       http->port = 80;
717    }
718
719    if (n == 2)
720    {
721       http->host = strdup(v[0]);
722       http->port = atoi(v[1]);
723    }
724
725    freez(buf);
726
727    if (http->host == NULL)
728    {
729       free_http_request(http);
730    }
731
732    if (http->path == NULL)
733    {
734       http->path = strdup("");
735    }
736
737 }
738
739
740 /* here begins the family of parser functions that reformat header lines */
741
742
743 /*********************************************************************
744  *
745  * Function    :  crumble
746  *
747  * Description :  This is called if a header matches a pattern to "crunch"
748  *
749  * Parameters  :
750  *          1  :  v = Pointer to parsers structure, which basically holds
751  *                headers (client or server) that we want to "crunch"
752  *          2  :  s = header (from sed) to "crunch"
753  *          3  :  csp = Current client state (buffers, headers, etc...)
754  *
755  * Returns     :  Always NULL.
756  *
757  *********************************************************************/
758 char *crumble(const struct parsers *v, const char *s, struct client_state *csp)
759 {
760    log_error(LOG_LEVEL_HEADER, "crunch!");
761    return(NULL);
762
763 }
764
765
766 /*********************************************************************
767  *
768  * Function    :  content_type
769  *
770  * Description :  Is this a text/.* or javascript MIME Type?
771  *
772  * Parameters  :
773  *          1  :  v = ignored
774  *          2  :  s = header string we are "considering"
775  *          3  :  csp = Current client state (buffers, headers, etc...)
776  *
777  * Returns     :  A duplicate string pointer to this header (ie. pass thru)
778  *
779  *********************************************************************/
780 char *content_type(const struct parsers *v, const char *s, struct client_state *csp)
781 {
782    if (strstr(s, " text/") || strstr(s, "application/x-javascript"))
783       csp->content_type = CT_TEXT;
784    else if (strstr(s, " image/gif"))
785       csp->content_type = CT_GIF;
786    else
787       csp->content_type = 0;
788
789    return(strdup(s));
790
791 }
792
793
794 /*********************************************************************
795  *
796  * Function    :  content_length
797  *
798  * Description :  Adjust Content-Length header if we modified
799  *                the body.
800  *
801  * Parameters  :
802  *          1  :  v = ignored
803  *          2  :  s = header string we are "considering"
804  *          3  :  csp = Current client state (buffers, headers, etc...)
805  *
806  * Returns     :  A duplicate string pointer to this header (ie. pass thru)
807  *
808  *********************************************************************/
809 char *content_length(const struct parsers *v, const char *s, struct client_state *csp)
810 {
811    if (csp->content_length != 0) /* Content has been modified */
812    {
813       char * s2 = (char *) zalloc(100);
814       sprintf(s2, "Content-Length: %d", (int) csp->content_length);
815
816           log_error(LOG_LEVEL_HEADER, "Adjust Content-Length to %d", (int) csp->content_length);
817       return(s2);
818    }
819    else
820    {
821       return(strdup(s));
822    }
823
824 }
825
826
827 /*********************************************************************
828  *
829  * Function    :  client_referrer
830  *
831  * Description :  Handle the "referer" config setting properly.
832  *                Called from `sed'.
833  *
834  * Parameters  :
835  *          1  :  v = ignored
836  *          2  :  s = header (from sed) to "crunch"
837  *          3  :  csp = Current client state (buffers, headers, etc...)
838  *
839  * Returns     :  NULL if crunched, or a malloc'ed string with the original
840  *                or modified header
841  *
842  *********************************************************************/
843 char *client_referrer(const struct parsers *v, const char *s, struct client_state *csp)
844 {
845    const char * newval;
846    char * s2;
847 #ifdef FEATURE_FORCE_LOAD
848    /* Since the referrer can include the prefix even
849     * even if the request itself is non-forced, we must
850     * clean it unconditionally 
851     */
852    strclean(s, FORCE_PREFIX);
853 #endif /* def FEATURE_FORCE_LOAD */
854
855 #ifdef FEATURE_TRUST
856    csp->referrer = strdup(s);
857 #endif /* def FEATURE_TRUST */
858
859    /*
860     * Are we sending referer?
861     */
862    if ((csp->action->flags & ACTION_HIDE_REFERER) == 0)
863    {
864       return(strdup(s));
865    }
866
867    newval = csp->action->string[ACTION_STRING_REFERER];
868
869    /*
870     * Are we blocking referer?
871     */
872    if ((newval == NULL) || (0 == strcmpic(newval, "block")) )
873    {
874       log_error(LOG_LEVEL_HEADER, "crunch!");
875       return(NULL);
876    }
877
878    /*
879     * Are we forging referer?
880     */
881    if (0 == strcmpic(newval, "forge"))
882    {
883       /*
884        * Forge a referer as http://[hostname:port of REQUEST]/
885        * to fool stupid checks for in-site links
886        */
887       log_error(LOG_LEVEL_HEADER, "crunch+forge!");
888       s2 = strsav(NULL, "Referer: ");
889       s2 = strsav(s2, "http://");
890       s2 = strsav(s2, csp->http->hostport);
891       s2 = strsav(s2, "/");
892       return(s2);
893    }
894
895    /*
896     * Have we got a fixed referer?
897     */
898    if (0 == strncmpic(newval, "http://", 7))
899    {
900       /*
901        * We have a specific (fixed) referer we want to send.
902        */
903       char * s3;
904
905       log_error(LOG_LEVEL_HEADER, "modified");
906
907       s3 = strsav( NULL, "Referer: " );
908       s3 = strsav( s3, newval );
909       return(s3);
910    }
911
912    /* Should never get here! */
913    log_error(LOG_LEVEL_ERROR, "Bad parameter: +referer{%s}", newval);
914
915    /*
916     * Forge is probably the best default.
917     *
918     * Forge a referer as http://[hostname:port of REQUEST]/
919     * to fool stupid checks for in-site links
920     */
921    log_error(LOG_LEVEL_HEADER, "crunch+forge!");
922    s2 = strsav(NULL, "Referer: ");
923    s2 = strsav(s2, "http://");
924    s2 = strsav(s2, csp->http->hostport);
925    s2 = strsav(s2, "/");
926    return(s2);
927 }
928
929
930 /*********************************************************************
931  *
932  * Function    :  client_uagent
933  *
934  * Description :  Handle the "user-agent" config setting properly
935  *                and remember its original value to enable browser
936  *                bug workarounds. Called from `sed'.
937  *
938  * Parameters  :
939  *          1  :  v = ignored
940  *          2  :  s = header (from sed) to "crunch"
941  *          3  :  csp = Current client state (buffers, headers, etc...)
942  *
943  * Returns     :  A malloc'ed pointer to the default agent, or
944  *                a malloc'ed string pointer to this header (ie. pass thru).
945  *
946  *********************************************************************/
947 char *client_uagent(const struct parsers *v, const char *s, struct client_state *csp)
948 {
949    const char * newval;
950    char * s2;
951
952    /* Save the client's User-Agent: value */
953    if (strlen(s) >= 12)
954    {
955       csp->http->user_agent = strdup(s + 12);
956    }
957
958 #ifdef FEATURE_IMAGE_DETECT_MSIE
959    if (strstr (s, "MSIE "))
960    {
961       /* This is Microsoft Internet Explorer.
962        * Enable auto-detect.
963        */
964       csp->accept_types |= ACCEPT_TYPE_IS_MSIE;
965    }
966 #endif /* def FEATURE_IMAGE_DETECT_MSIE */
967
968    if ((csp->action->flags & ACTION_HIDE_USER_AGENT) == 0)
969    {
970       return(strdup(s));
971    }
972
973    newval = csp->action->string[ACTION_STRING_USER_AGENT];
974    if (newval == NULL)
975    {
976       return(strdup(s));
977    }
978
979    log_error(LOG_LEVEL_HEADER, "modified");
980
981    s2 = strsav( NULL, "User-Agent: " );
982    s2 = strsav( s2, newval );
983    return(s2);
984
985 }
986
987
988 /*********************************************************************
989  *
990  * Function    :  client_ua
991  *
992  * Description :  Handle "ua-" headers properly.  Called from `sed'.
993  *
994  * Parameters  :
995  *          1  :  v = ignored
996  *          2  :  s = header (from sed) to "crunch"
997  *          3  :  csp = Current client state (buffers, headers, etc...)
998  *
999  * Returns     :  NULL if crunched, or a malloc'ed string to original header
1000  *
1001  *********************************************************************/
1002 char *client_ua(const struct parsers *v, const char *s, struct client_state *csp)
1003 {
1004    if ((csp->action->flags & ACTION_HIDE_USER_AGENT) == 0)
1005    {
1006       return(strdup(s));
1007    }
1008    else
1009    {
1010       log_error(LOG_LEVEL_HEADER, "crunch!");
1011       return(NULL);
1012    }
1013 }
1014
1015
1016 /*********************************************************************
1017  *
1018  * Function    :  client_from
1019  *
1020  * Description :  Handle the "from" config setting properly.
1021  *                Called from `sed'.
1022  *
1023  * Parameters  :
1024  *          1  :  v = ignored
1025  *          2  :  s = header (from sed) to "crunch"
1026  *          3  :  csp = Current client state (buffers, headers, etc...)
1027  *
1028  * Returns     :  NULL if crunched, or a malloc'ed string to
1029  *                modified/original header.
1030  *
1031  *********************************************************************/
1032 char *client_from(const struct parsers *v, const char *s, struct client_state *csp)
1033 {
1034    const char * newval;
1035    char * s2;
1036
1037    if ((csp->action->flags & ACTION_HIDE_FROM) == 0)
1038    {
1039       return(strdup(s));
1040    }
1041
1042    newval = csp->action->string[ACTION_STRING_FROM];
1043
1044    /*
1045     * Are we blocking referer?
1046     */
1047    if ((newval == NULL) || (0 == strcmpic(newval, "block")) )
1048    {
1049       log_error(LOG_LEVEL_HEADER, "crunch!");
1050       return(NULL);
1051    }
1052
1053    log_error(LOG_LEVEL_HEADER, " modified");
1054
1055    s2 = strsav( NULL, "From: " );
1056    s2 = strsav( s2, newval );
1057    return(s2);
1058
1059 }
1060
1061
1062 /*********************************************************************
1063  *
1064  * Function    :  client_send_cookie
1065  *
1066  * Description :  Handle the "cookie" header properly.  Called from `sed'.
1067  *                If cookie is accepted, add it to the cookie_list,
1068  *                else we crunch it.  Mmmmmmmmmmm ... cookie ......
1069  *
1070  * Parameters  :
1071  *          1  :  v = pattern of cookie `sed' found matching
1072  *          2  :  s = header (from sed) to "crunch"
1073  *          3  :  csp = Current client state (buffers, headers, etc...)
1074  *
1075  * Returns     :  Always NULL.
1076  *
1077  *********************************************************************/
1078 char *client_send_cookie(const struct parsers *v, const char *s, struct client_state *csp)
1079 {
1080    if ((csp->action->flags & ACTION_NO_COOKIE_READ) == 0)
1081    {
1082       enlist(csp->cookie_list, s + v->len + 1);
1083    }
1084    else
1085    {
1086       log_error(LOG_LEVEL_HEADER, " crunch!");
1087    }
1088
1089    /*
1090     * Always return NULL here.  The cookie header
1091     * will be sent at the end of the header.
1092     */
1093    return(NULL);
1094
1095 }
1096
1097
1098 /*********************************************************************
1099  *
1100  * Function    :  client_x_forwarded
1101  *
1102  * Description :  Handle the "x-forwarded-for" config setting properly,
1103  *                also used in the add_client_headers list.  Called from `sed'.
1104  *
1105  * Parameters  :
1106  *          1  :  v = ignored
1107  *          2  :  s = header (from sed) to "crunch"
1108  *          3  :  csp = Current client state (buffers, headers, etc...)
1109  *
1110  * Returns     :  Always NULL.
1111  *
1112  *********************************************************************/
1113 char *client_x_forwarded(const struct parsers *v, const char *s, struct client_state *csp)
1114 {
1115    if ((csp->action->flags & ACTION_HIDE_FORWARDED) == 0)
1116    {
1117       /* Save it so we can re-add it later */
1118       csp->x_forwarded = strdup(s);
1119    }
1120
1121    /*
1122     * Always return NULL, since this information
1123     * will be sent at the end of the header.
1124     */
1125
1126    return(NULL);
1127
1128 }
1129
1130 #if defined(FEATURE_IMAGE_DETECT_MSIE)
1131 /*********************************************************************
1132  *
1133  * Function    :  client_accept
1134  *
1135  * Description :  Detect whether the client wants HTML or an image.
1136  *                Clients do not always make this information available
1137  *                in a sane way.  Always passes the header through
1138  *                the proxy unchanged.
1139  *
1140  * Parameters  :
1141  *          1  :  v = Ignored.
1142  *          2  :  s = Header string.  Null terminated.
1143  *          3  :  csp = Current client state (buffers, headers, etc...)
1144  *
1145  * Returns     :  Duplicate of argument s.
1146  *
1147  *********************************************************************/
1148 char *client_accept(const struct parsers *v, const char *s, struct client_state *csp)
1149 {
1150 #ifdef FEATURE_IMAGE_DETECT_MSIE
1151    if (strstr (s, "image/gif"))
1152    {
1153       /* Client will accept HTML.  If this seems counterintuitive,
1154        * blame Microsoft. 
1155        */
1156       csp->accept_types |= ACCEPT_TYPE_MSIE_HTML;
1157    }
1158    else
1159    {
1160       csp->accept_types |= ACCEPT_TYPE_MSIE_IMAGE;
1161    }
1162 #endif /* def FEATURE_IMAGE_DETECT_MSIE */
1163
1164    return(strdup(s));
1165
1166 }
1167 #endif /* defined(FEATURE_IMAGE_DETECT_MSIE) */
1168
1169
1170
1171 /* the following functions add headers directly to the header list */
1172
1173
1174 /*********************************************************************
1175  *
1176  * Function    :  client_cookie_adder
1177  *
1178  * Description :  Used in the add_client_headers list.  Called from `sed'.
1179  *
1180  * Parameters  :
1181  *          1  :  csp = Current client state (buffers, headers, etc...)
1182  *
1183  * Returns     :  N/A
1184  *
1185  *********************************************************************/
1186 void client_cookie_adder(struct client_state *csp)
1187 {
1188    struct list_entry *lst;
1189    char *tmp = NULL;
1190    char *e;
1191
1192    for (lst = csp->cookie_list->first; lst ; lst = lst->next)
1193    {
1194       if (tmp)
1195       {
1196          tmp = strsav(tmp, "; ");
1197       }
1198       tmp = strsav(tmp, lst->str);
1199    }
1200
1201    for (lst = csp->action->multi[ACTION_MULTI_WAFER]->first;  lst ; lst = lst->next)
1202    {
1203       if (tmp)
1204       {
1205          tmp = strsav(tmp, "; ");
1206       }
1207
1208       if ((e = cookie_encode(lst->str)))
1209       {
1210          tmp = strsav(tmp, e);
1211          freez(e);
1212       }
1213    }
1214
1215    if (tmp)
1216    {
1217       char *ret;
1218
1219       ret = strdup("Cookie: ");
1220       ret = strsav(ret, tmp);
1221       log_error(LOG_LEVEL_HEADER, "addh: %s", ret);
1222       enlist(csp->headers, ret);
1223       freez(tmp);
1224       freez(ret);
1225    }
1226
1227 }
1228
1229
1230 /*********************************************************************
1231  *
1232  * Function    :  client_xtra_adder
1233  *
1234  * Description :  Used in the add_client_headers list.  Called from `sed'.
1235  *
1236  * Parameters  :
1237  *          1  :  csp = Current client state (buffers, headers, etc...)
1238  *
1239  * Returns     :  N/A
1240  *
1241  *********************************************************************/
1242 void client_xtra_adder(struct client_state *csp)
1243 {
1244    struct list_entry *lst;
1245
1246    for (lst = csp->action->multi[ACTION_MULTI_ADD_HEADER]->first;
1247         lst ; lst = lst->next)
1248    {
1249       log_error(LOG_LEVEL_HEADER, "addh: %s", lst->str);
1250       enlist(csp->headers, lst->str);
1251    }
1252
1253 }
1254
1255
1256 /*********************************************************************
1257  *
1258  * Function    :  client_x_forwarded_adder
1259  *
1260  * Description :  Used in the add_client_headers list.  Called from `sed'.
1261  *
1262  * Parameters  :
1263  *          1  :  csp = Current client state (buffers, headers, etc...)
1264  *
1265  * Returns     :  N/A
1266  *
1267  *********************************************************************/
1268 void client_x_forwarded_adder(struct client_state *csp)
1269 {
1270    char *p = NULL;
1271
1272    if ((csp->action->flags & ACTION_HIDE_FORWARDED) != 0)
1273    {
1274       return;
1275    }
1276
1277    if (csp->x_forwarded)
1278    {
1279       p = strsav(p, csp->x_forwarded);
1280       p = strsav(p, ", ");
1281       p = strsav(p, csp->ip_addr_str);
1282    }
1283    else
1284    {
1285       p = strsav(p, "X-Forwarded-For: ");
1286       p = strsav(p, csp->ip_addr_str);
1287    }
1288
1289    log_error(LOG_LEVEL_HEADER, "addh: %s", p);
1290    enlist(csp->headers, p);
1291
1292    freez(p);
1293 }
1294
1295
1296 /*********************************************************************
1297  *
1298  * Function    :  connection_close_adder
1299  *
1300  * Description :  Adds a "Connection: close" header to csp->headers
1301  *                as a temporary fix for the needed but missing HTTP/1.1
1302  *                support. Called from `sed'.
1303  *                FIXME: This whole function shouldn't be neccessary!
1304  *
1305  * Parameters  :
1306  *          1  :  csp = Current client state (buffers, headers, etc...)
1307  *
1308  * Returns     :  N/A
1309  *
1310  *********************************************************************/
1311 void connection_close_adder(struct client_state *csp)
1312 {
1313    enlist(csp->headers, "Connection: close");
1314 }
1315
1316
1317 /*********************************************************************
1318  *
1319  * Function    :  server_http11
1320  *
1321  * Description :  Rewrite HTTP/1.1 answers to HTTP/1.0 until we add
1322  *                HTTP/1.1 support. Called from `sed'.
1323  *
1324  * Parameters  :
1325  *          1  :  v = parser pattern that matched this header
1326  *          2  :  s = header that matched this pattern
1327  *          3  :  csp = Current client state (buffers, headers, etc...)
1328  *
1329  * Returns     :  "HTTP/1.0" answer.
1330  *
1331  *********************************************************************/
1332 char *server_http11(const struct parsers *v, const char *s, struct client_state *csp)
1333 {
1334    char *ret;
1335
1336    ret = strdup(s);
1337    ret[7] = '0'; /* "HTTP/1.1 ..." -> "HTTP/1.0 ..." */
1338
1339    return ret;
1340 }
1341
1342
1343 /*********************************************************************
1344  *
1345  * Function    :  server_set_cookie
1346  *
1347  * Description :  Handle the server "cookie" header properly.
1348  *                Log cookie to the jar file.  Then "crunch" it,
1349  *                or accept it.  Called from `sed'.
1350  *
1351  * Parameters  :
1352  *          1  :  v = parser pattern that matched this header
1353  *          2  :  s = header that matched this pattern
1354  *          3  :  csp = Current client state (buffers, headers, etc...)
1355  *
1356  * Returns     :  `crumble' or a newly malloc'ed string.
1357  *
1358  *********************************************************************/
1359 char *server_set_cookie(const struct parsers *v, const char *s, struct client_state *csp)
1360 {
1361 #ifdef FEATURE_COOKIE_JAR
1362    if (csp->config->jar)
1363    {
1364       fprintf(csp->config->jar, "%s\t%s\n", csp->http->host, (s + v->len + 1));
1365    }
1366 #endif /* def FEATURE_COOKIE_JAR */
1367
1368    if ((csp->action->flags & ACTION_NO_COOKIE_SET) != 0)
1369    {
1370       return(crumble(v, s, csp));
1371    }
1372
1373    return(strdup(s));
1374
1375 }
1376
1377
1378 #ifdef FEATURE_FORCE_LOAD
1379 /*********************************************************************
1380  *
1381  * Function    :  client_host
1382  *
1383  * Description :  Clean the FORCE_PREFIX out of the 'host' http
1384  *                header, if we use force
1385  *
1386  * Parameters  :
1387  *          1  :  v = ignored
1388  *          2  :  s = header (from sed) to clean
1389  *          3  :  csp = Current client state (buffers, headers, etc...)
1390  *
1391  * Returns     :  A malloc'ed pointer to the cleaned host header 
1392  *
1393  *********************************************************************/
1394 char *client_host(const struct parsers *v, const char *s, struct client_state *csp)
1395 {
1396    char *cleanhost = strdup(s);
1397  
1398    if(csp->force)
1399    {
1400       strclean(cleanhost, FORCE_PREFIX);
1401    }
1402  
1403    return(cleanhost);
1404 }
1405 #endif /* def FEATURE_FORCE_LOAD */
1406  
1407  
1408 #ifdef FEATURE_FORCE_LOAD 
1409 /*********************************************************************
1410  *
1411  * Function    :  strclean
1412  *
1413  * Description :  In-Situ-Eliminate all occurances of substring in 
1414  *                string
1415  *
1416  * Parameters  :
1417  *          1  :  string = string to clean
1418  *          2  :  substring = substring to eliminate
1419  *
1420  * Returns     :  Number of eliminations
1421  *
1422  *********************************************************************/
1423 int strclean(const char *string, const char *substring)
1424 {
1425    int hits = 0, len = strlen(substring);
1426    char *pos, *p;
1427
1428    while((pos = strstr(string, substring)))
1429    {
1430       p = pos + len;
1431       do
1432       {
1433          *(p - len) = *p; 
1434       }
1435       while (*p++ != '\0');
1436
1437       hits++;
1438    }
1439
1440    return(hits);
1441 }
1442 #endif /* def FEATURE_FORCE_LOAD */
1443
1444
1445 /*
1446   Local Variables:
1447   tab-width: 3
1448   end:
1449 */