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