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