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