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