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