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