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