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