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