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