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