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