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