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