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