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