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