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