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