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