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