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