Added cross-compile defaults to the AC_CHECK_SIZEOF macros
[privoxy.git] / parsers.c
1 const char parsers_rcs[] = "$Id: parsers.c,v 1.44 2001/12/14 01:22:54 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.44  2001/12/14 01:22:54  steudten
45  *    Remove 'user:pass@' from 'proto://user:pass@host' for the
46  *    new added header 'Host: ..'. (See Req ID 491818)
47  *
48  *    Revision 1.43  2001/11/23 00:26:38  jongfoster
49  *    Fixing two really stupid errors in my previous commit
50  *
51  *    Revision 1.42  2001/11/22 21:59:30  jongfoster
52  *    Adding code to handle +no-cookies-keep
53  *
54  *    Revision 1.41  2001/11/05 23:43:05  steudten
55  *    Add time+date to log files.
56  *
57  *    Revision 1.40  2001/10/26 20:13:09  jongfoster
58  *    ctype.h is needed in Windows, too.
59  *
60  *    Revision 1.39  2001/10/26 17:40:04  oes
61  *    Introduced get_header_value()
62  *    Removed http->user_agent, csp->referrer and csp->accept_types
63  *    Removed client_accept()
64  *
65  *    Revision 1.38  2001/10/25 03:40:48  david__schmidt
66  *    Change in porting tactics: OS/2's EMX porting layer doesn't allow multiple
67  *    threads to call select() simultaneously.  So, it's time to do a real, live,
68  *    native OS/2 port.  See defines for __EMX__ (the porting layer) vs. __OS2__
69  *    (native). Both versions will work, but using __OS2__ offers multi-threading.
70  *
71  *    Revision 1.37  2001/10/23 21:36:02  jongfoster
72  *    Documenting sed()'s error behaviou (doc change only)
73  *
74  *    Revision 1.36  2001/10/13 12:51:51  joergs
75  *    Removed client_host, (was only required for the old 2.0.2-11 http://noijb.
76  *    force-load), instead crumble Host: and add it (again) in client_host_adder
77  *    (in case we get a HTTP/1.0 request without Host: header and forward it to
78  *    a HTTP/1.1 server/proxy).
79  *
80  *    Revision 1.35  2001/10/09 22:39:21  jongfoster
81  *    assert.h is also required under Win32, so moving out of #ifndef _WIN32
82  *    block.
83  *
84  *    Revision 1.34  2001/10/07 18:50:55  oes
85  *    Added server_content_encoding, renamed server_transfer_encoding
86  *
87  *    Revision 1.33  2001/10/07 18:04:49  oes
88  *    Changed server_http11 to server_http and its pattern to "HTTP".
89  *      Additional functionality: it now saves the HTTP status into
90  *      csp->http->status and sets CT_TABOO for Status 206 (partial range)
91  *
92  *    Revision 1.32  2001/10/07 15:43:28  oes
93  *    Removed FEATURE_DENY_GZIP and replaced it with client_accept_encoding,
94  *       client_te and client_accept_encoding_adder, triggered by the new
95  *       +no-compression action. For HTTP/1.1 the Accept-Encoding header is
96  *       changed to allow only identity and chunked, and the TE header is
97  *       crunched. For HTTP/1.0, Accept-Encoding is crunched.
98  *
99  *    parse_http_request no longer does anything than parsing. The rewriting
100  *      of http->cmd and version mangling are gone. It now also recognizes
101  *      the put and delete methods and saves the url in http->url. Removed
102  *      unused variable.
103  *
104  *    renamed content_type and content_length to have the server_ prefix
105  *
106  *    server_content_type now only works if csp->content_type != CT_TABOO
107  *
108  *    added server_transfer_encoding, which
109  *      - Sets CT_TABOO to prohibit filtering if encoding compresses
110  *      - Raises the CSP_FLAG_CHUNKED flag if Encoding is "chunked"
111  *      - Change from "chunked" to "identity" if body was chunked
112  *        but has been de-chunked for filtering.
113  *
114  *    added server_content_md5 which crunches any Content-MD5 headers
115  *      if the body was modified.
116  *
117  *    made server_http11 conditional on +downgrade action
118  *
119  *    Replaced 6 boolean members of csp with one bitmap (csp->flags)
120  *
121  *    Revision 1.31  2001/10/05 14:25:02  oes
122  *    Crumble Keep-Alive from Server
123  *
124  *    Revision 1.30  2001/09/29 12:56:03  joergs
125  *    IJB now changes HTTP/1.1 to HTTP/1.0 in requests and answers.
126  *
127  *    Revision 1.29  2001/09/24 21:09:24  jongfoster
128  *    Fixing 2 memory leaks that Guy spotted, where the paramater to
129  *    enlist() was not being free()d.
130  *
131  *    Revision 1.28  2001/09/22 16:32:28  jongfoster
132  *    Removing unused #includes.
133  *
134  *    Revision 1.27  2001/09/20 15:45:25  steudten
135  *
136  *    add casting from size_t to int for printf()
137  *    remove local variable shadow s2
138  *
139  *    Revision 1.26  2001/09/16 17:05:14  jongfoster
140  *    Removing unused #include showarg.h
141  *
142  *    Revision 1.25  2001/09/16 13:21:27  jongfoster
143  *    Changes to use new list functions.
144  *
145  *    Revision 1.24  2001/09/13 23:05:50  jongfoster
146  *    Changing the string paramater to the header parsers a "const".
147  *
148  *    Revision 1.23  2001/09/12 18:08:19  steudten
149  *
150  *    In parse_http_request() header rewriting miss the host value, so
151  *    from http://www.mydomain.com the result was just " / " not
152  *    http://www.mydomain.com/ in case we forward.
153  *
154  *    Revision 1.22  2001/09/10 10:58:53  oes
155  *    Silenced compiler warnings
156  *
157  *    Revision 1.21  2001/07/31 14:46:00  oes
158  *     - Persistant connections now suppressed
159  *     - sed() no longer appends empty header to csp->headers
160  *
161  *    Revision 1.20  2001/07/30 22:08:36  jongfoster
162  *    Tidying up #defines:
163  *    - All feature #defines are now of the form FEATURE_xxx
164  *    - Permanently turned off WIN_GUI_EDIT
165  *    - Permanently turned on WEBDAV and SPLIT_PROXY_ARGS
166  *
167  *    Revision 1.19  2001/07/25 17:21:54  oes
168  *    client_uagent now saves copy of User-Agent: header value
169  *
170  *    Revision 1.18  2001/07/13 14:02:46  oes
171  *     - Included fix to repair broken HTTP requests that
172  *       don't contain a path, not even '/'.
173  *     - Removed all #ifdef PCRS
174  *     - content_type now always inspected and classified as
175  *       text, gif or other.
176  *     - formatting / comments
177  *
178  *    Revision 1.17  2001/06/29 21:45:41  oes
179  *    Indentation, CRLF->LF, Tab-> Space
180  *
181  *    Revision 1.16  2001/06/29 13:32:42  oes
182  *    - Fixed a comment
183  *    - Adapted free_http_request
184  *    - Removed logentry from cancelled commit
185  *
186  *    Revision 1.15  2001/06/03 19:12:38  oes
187  *    deleted const struct interceptors
188  *
189  *    Revision 1.14  2001/06/01 18:49:17  jongfoster
190  *    Replaced "list_share" with "list" - the tiny memory gain was not
191  *    worth the extra complexity.
192  *
193  *    Revision 1.13  2001/05/31 21:30:33  jongfoster
194  *    Removed list code - it's now in list.[ch]
195  *    Renamed "permission" to "action", and changed many features
196  *    to use the actions file rather than the global config.
197  *
198  *    Revision 1.12  2001/05/31 17:33:13  oes
199  *
200  *    CRLF -> LF
201  *
202  *    Revision 1.11  2001/05/29 20:11:19  joergs
203  *    '/ * inside comment' warning removed.
204  *
205  *    Revision 1.10  2001/05/29 09:50:24  jongfoster
206  *    Unified blocklist/imagelist/permissionslist.
207  *    File format is still under discussion, but the internal changes
208  *    are (mostly) done.
209  *
210  *    Also modified interceptor behaviour:
211  *    - We now intercept all URLs beginning with one of the following
212  *      prefixes (and *only* these prefixes):
213  *        * http://i.j.b/
214  *        * http://ijbswa.sf.net/config/
215  *        * http://ijbswa.sourceforge.net/config/
216  *    - New interceptors "home page" - go to http://i.j.b/ to see it.
217  *    - Internal changes so that intercepted and fast redirect pages
218  *      are not replaced with an image.
219  *    - Interceptors now have the option to send a binary page direct
220  *      to the client. (i.e. ijb-send-banner uses this)
221  *    - Implemented show-url-info interceptor.  (Which is why I needed
222  *      the above interceptors changes - a typical URL is
223  *      "http://i.j.b/show-url-info?url=www.somesite.com/banner.gif".
224  *      The previous mechanism would not have intercepted that, and
225  *      if it had been intercepted then it then it would have replaced
226  *      it with an image.)
227  *
228  *    Revision 1.9  2001/05/28 17:26:33  jongfoster
229  *    Fixing segfault if last header was crunched.
230  *    Fixing Windows build (snprintf() is _snprintf() under Win32, but we
231  *    can use the cross-platform sprintf() instead.)
232  *
233  *    Revision 1.8  2001/05/27 22:17:04  oes
234  *
235  *    - re_process_buffer no longer writes the modified buffer
236  *      to the client, which was very ugly. It now returns the
237  *      buffer, which it is then written by chat.
238  *
239  *    - content_length now adjusts the Content-Length: header
240  *      for modified documents rather than crunch()ing it.
241  *      (Length info in csp->content_length, which is 0 for
242  *      unmodified documents)
243  *
244  *    - For this to work, sed() is called twice when filtering.
245  *
246  *    Revision 1.7  2001/05/27 13:19:06  oes
247  *    Patched Joergs solution for the content-length in.
248  *
249  *    Revision 1.6  2001/05/26 13:39:32  jongfoster
250  *    Only crunches Content-Length header if applying RE filtering.
251  *    Without this fix, Microsoft Windows Update wouldn't work.
252  *
253  *    Revision 1.5  2001/05/26 00:28:36  jongfoster
254  *    Automatic reloading of config file.
255  *    Removed obsolete SIGHUP support (Unix) and Reload menu option (Win32).
256  *    Most of the global variables have been moved to a new
257  *    struct configuration_spec, accessed through csp->config->globalname
258  *    Most of the globals remaining are used by the Win32 GUI.
259  *
260  *    Revision 1.4  2001/05/22 18:46:04  oes
261  *
262  *    - Enabled filtering banners by size rather than URL
263  *      by adding patterns that replace all standard banner
264  *      sizes with the "Junkbuster" gif to the re_filterfile
265  *
266  *    - Enabled filtering WebBugs by providing a pattern
267  *      which kills all 1x1 images
268  *
269  *    - Added support for PCRE_UNGREEDY behaviour to pcrs,
270  *      which is selected by the (nonstandard and therefore
271  *      capital) letter 'U' in the option string.
272  *      It causes the quantifiers to be ungreedy by default.
273  *      Appending a ? turns back to greedy (!).
274  *
275  *    - Added a new interceptor ijb-send-banner, which
276  *      sends back the "Junkbuster" gif. Without imagelist or
277  *      MSIE detection support, or if tinygif = 1, or the
278  *      URL isn't recognized as an imageurl, a lame HTML
279  *      explanation is sent instead.
280  *
281  *    - Added new feature, which permits blocking remote
282  *      script redirects and firing back a local redirect
283  *      to the browser.
284  *      The feature is conditionally compiled, i.e. it
285  *      can be disabled with --disable-fast-redirects,
286  *      plus it must be activated by a "fast-redirects"
287  *      line in the config file, has its own log level
288  *      and of course wants to be displayed by show-proxy-args
289  *      Note: Boy, all the #ifdefs in 1001 locations and
290  *      all the fumbling with configure.in and acconfig.h
291  *      were *way* more work than the feature itself :-(
292  *
293  *    - Because a generic redirect template was needed for
294  *      this, tinygif = 3 now uses the same.
295  *
296  *    - Moved GIFs, and other static HTTP response templates
297  *      to project.h
298  *
299  *    - Some minor fixes
300  *
301  *    - Removed some >400 CRs again (Jon, you really worked
302  *      a lot! ;-)
303  *
304  *    Revision 1.3  2001/05/20 01:21:20  jongfoster
305  *    Version 2.9.4 checkin.
306  *    - Merged popupfile and cookiefile, and added control over PCRS
307  *      filtering, in new "permissionsfile".
308  *    - Implemented LOG_LEVEL_FATAL, so that if there is a configuration
309  *      file error you now get a message box (in the Win32 GUI) rather
310  *      than the program exiting with no explanation.
311  *    - Made killpopup use the PCRS MIME-type checking and HTTP-header
312  *      skipping.
313  *    - Removed tabs from "config"
314  *    - Moved duplicated url parsing code in "loaders.c" to a new funcition.
315  *    - Bumped up version number.
316  *
317  *    Revision 1.2  2001/05/17 23:02:36  oes
318  *     - Made referrer option accept 'L' as a substitute for '§'
319  *
320  *    Revision 1.1.1.1  2001/05/15 13:59:01  oes
321  *    Initial import of version 2.9.3 source tree
322  *
323  *
324  *********************************************************************/
325 \f
326
327 #include "config.h"
328
329 #ifndef _WIN32
330 #include <stdio.h>
331 #include <sys/types.h>
332 #endif
333
334 #include <stdlib.h>
335 #include <ctype.h>
336 #include <assert.h>
337 #include <string.h>
338
339 #if !defined(_WIN32) && !defined(__OS2__)
340 #include <unistd.h>
341 #endif
342
343 #include "project.h"
344 #include "list.h"
345 #include "parsers.h"
346 #include "encode.h"
347 #include "ssplit.h"
348 #include "errlog.h"
349 #include "jbsockets.h"
350 #include "miscutil.h"
351 #include "list.h"
352
353 const char parsers_h_rcs[] = PARSERS_H_VERSION;
354
355 /* Fix a problem with Solaris.  There should be no effect on other
356  * platforms.
357  * Solaris's isspace() is a macro which uses it's argument directly
358  * as an array index.  Therefore we need to make sure that high-bit
359  * characters generate +ve values, and ideally we also want to make
360  * the argument match the declared parameter type of "int".
361  *
362  * Why did they write a character function that can't take a simple
363  * "char" argument?  Doh!
364  */
365 #define ijb_isupper(__X) isupper((int)(unsigned char)(__X))
366 #define ijb_tolower(__X) tolower((int)(unsigned char)(__X))
367
368
369 const struct parsers client_patterns[] = {
370    { "referer:",                 8,    client_referrer },
371    { "user-agent:",              11,   client_uagent },
372    { "ua-",                      3,    client_ua },
373    { "from:",                    5,    client_from },
374    { "cookie:",                  7,    client_send_cookie },
375    { "x-forwarded-for:",         16,   client_x_forwarded },
376    { "Accept-Encoding:",         16,   client_accept_encoding },
377    { "TE:",                      3,    client_te },
378    { "Host:",                     5,   crumble },
379 /* { "if-modified-since:",       18,   crumble }, */
380    { "Keep-Alive:",              11,   crumble },
381    { "connection:",              11,   crumble },
382    { "proxy-connection:",        17,   crumble },
383    { NULL,                       0,    NULL }
384 };
385
386
387 const struct parsers server_patterns[] = {
388    { "HTTP",                4, server_http },
389    { "set-cookie:",        11, server_set_cookie },
390    { "connection:",        11, crumble },
391    { "Content-Type:",      13, server_content_type },
392    { "Content-Length:",    15, server_content_length },
393    { "Content-MD5:",       12, server_content_md5 },
394    { "Content-Encoding:",  17, server_content_encoding },
395    { "Transfer-Encoding:", 18, server_transfer_coding },
396    { "Keep-Alive:",        11, crumble },
397    { NULL, 0, NULL }
398 };
399
400
401 void (* const add_client_headers[])(struct client_state *) = {
402    client_host_adder,
403    client_cookie_adder,
404    client_x_forwarded_adder,
405    client_xtra_adder,
406    client_accept_encoding_adder,
407    connection_close_adder,
408    NULL
409 };
410
411
412 void (* const add_server_headers[])(struct client_state *) = {
413    connection_close_adder,
414    NULL
415 };
416
417
418 /*********************************************************************
419  *
420  * Function    :  flush_socket
421  *
422  * Description :  Write any pending "buffered" content.
423  *
424  * Parameters  :
425  *          1  :  fd = file descriptor of the socket to read
426  *          2  :  csp = Current client state (buffers, headers, etc...)
427  *
428  * Returns     :  On success, the number of bytes written are returned (zero
429  *                indicates nothing was written).  On error, -1 is returned,
430  *                and errno is set appropriately.  If count is zero and the
431  *                file descriptor refers to a regular file, 0 will be
432  *                returned without causing any other effect.  For a special
433  *                file, the results are not portable.
434  *
435  *********************************************************************/
436 int flush_socket(int fd, struct client_state *csp)
437 {
438    struct iob *iob = csp->iob;
439    int n = iob->eod - iob->cur;
440
441    if (n <= 0)
442    {
443       return(0);
444    }
445
446    n = write_socket(fd, iob->cur, n);
447    iob->eod = iob->cur = iob->buf;
448    return(n);
449
450 }
451
452
453 /*********************************************************************
454  *
455  * Function    :  add_to_iob
456  *
457  * Description :  Add content to the buffered page.
458  *
459  * Parameters  :
460  *          1  :  csp = Current client state (buffers, headers, etc...)
461  *          2  :  buf = holds the content to be added to the page
462  *          3  :  n = number of bytes to be added
463  *
464  * Returns     :  Number of bytes in the content buffer.
465  *
466  *********************************************************************/
467 int add_to_iob(struct client_state *csp, char *buf, int n)
468 {
469    struct iob *iob = csp->iob;
470    int have, need;
471    char *p;
472
473    have = iob->eod - iob->cur;
474
475    if (n <= 0)
476    {
477       return(have);
478    }
479
480    need = have + n;
481
482    if ((p = (char *)malloc(need + 1)) == NULL)
483    {
484       log_error(LOG_LEVEL_ERROR, "malloc() iob failed: %E");
485       return(-1);
486    }
487
488    if (have)
489    {
490       /* there is something in the buffer - save it */
491       memcpy(p, iob->cur, have);
492
493       /* replace the buffer with the new space */
494       freez(iob->buf);
495       iob->buf = p;
496
497       /* point to the end of the data */
498       p += have;
499    }
500    else
501    {
502       /* the buffer is empty, free it and reinitialize */
503       freez(iob->buf);
504       iob->buf = p;
505    }
506
507    /* copy the new data into the iob buffer */
508    memcpy(p, buf, n);
509
510    /* point to the end of the data */
511    p += n;
512
513    /* null terminate == cheap insurance */
514    *p = '\0';
515
516    /* set the pointers to the new values */
517    iob->cur = iob->buf;
518    iob->eod = p;
519
520    return(need);
521
522 }
523
524
525 /*********************************************************************
526  *
527  * Function    :  get_header
528  *
529  * Description :  This (odd) routine will parse the csp->iob
530  *
531  * Parameters  :
532  *          1  :  csp = Current client state (buffers, headers, etc...)
533  *
534  * Returns     :  Any one of the following:
535  *
536  * 1) a pointer to a dynamically allocated string that contains a header line
537  * 2) NULL  indicating that the end of the header was reached
538  * 3) ""    indicating that the end of the iob was reached before finding
539  *          a complete header line.
540  *
541  *********************************************************************/
542 char *get_header(struct client_state *csp)
543 {
544    struct iob *iob;
545    char *p, *q, *ret;
546    iob = csp->iob;
547
548    if ((iob->cur == NULL)
549       || ((p = strchr(iob->cur, '\n')) == NULL))
550    {
551       return(""); /* couldn't find a complete header */
552    }
553
554    *p = '\0';
555
556    ret = strdup(iob->cur);
557
558    iob->cur = p+1;
559
560    if ((q = strchr(ret, '\r'))) *q = '\0';
561
562    /* is this a blank linke (i.e. the end of the header) ? */
563    if (*ret == '\0')
564    {
565       freez(ret);
566       return(NULL);
567    }
568
569    return(ret);
570
571 }
572
573
574 /*********************************************************************
575  *
576  * Function    :  get_header_value
577  *
578  * Description :  Get the value of a given header from a chained list
579  *                of header lines or return NULL if no such header is
580  *                present in the list.
581  *
582  * Parameters  :
583  *          1  :  header_list = pointer to list
584  *          2  :  header_name = string with name of header to look for.
585  *                              Trailing colon required, capitalization
586  *                              doesn't matter.
587  *
588  * Returns     :  NULL if not found, else value of header
589  *
590  *********************************************************************/
591 char *get_header_value(const struct list *header_list, const char *header_name)
592 {
593    struct list_entry *cur_entry;
594    char *ret = NULL;
595    size_t length = 0;
596
597    assert(header_list);
598    assert(header_name);
599    length = strlen(header_name);
600
601    for (cur_entry = header_list->first; cur_entry ; cur_entry = cur_entry->next)
602    {
603       if (cur_entry->str)
604       {
605          if (!strncmpic(cur_entry->str, header_name, length))
606          {
607             /*
608              * Found: return pointer to start of value
609              */
610             ret = (char *) (cur_entry->str + length);
611             while (*ret && ijb_isspace(*ret)) ret++;
612             return(ret);
613          }
614       }
615    }
616
617    /* 
618     * Not found
619     */
620    return NULL;
621
622 }
623
624 /*********************************************************************
625  *
626  * Function    :  sed
627  *
628  * Description :  add, delete or modify lines in the HTTP header streams.
629  *                On entry, it receives a linked list of headers space
630  *                that was allocated dynamically (both the list nodes
631  *                and the header contents).
632  *
633  *                As a side effect it frees the space used by the original
634  *                header lines.
635  *
636  * Parameters  :
637  *          1  :  pats = list of patterns to match against headers
638  *          2  :  more_headers = list of functions to add more
639  *                headers (client or server)
640  *          3  :  csp = Current client state (buffers, headers, etc...)
641  *
642  * Returns     :  Single pointer to a fully formed header, or NULL
643  *                on out-of-memory error.
644  *
645  *********************************************************************/
646 char *sed(const struct parsers pats[], void (* const more_headers[])(struct client_state *), struct client_state *csp)
647 {
648    struct list_entry *p;
649    const struct parsers *v;
650    char *hdr;
651    void (* const *f)();
652
653    for (v = pats; v->str ; v++)
654    {
655       for (p = csp->headers->first; p ; p = p->next)
656       {
657          /* Header crunch()ed in previous run? -> ignore */
658          if (p->str == NULL) continue;
659
660          if (v == pats) log_error(LOG_LEVEL_HEADER, "scan: %s", p->str);
661
662          if (strncmpic(p->str, v->str, v->len) == 0)
663          {
664             hdr = v->parser(v, p->str, csp);
665             freez(p->str); /* FIXME: Yuck! patching a list...*/
666             p->str = hdr;
667          }
668       }
669    }
670
671    /* place any additional headers on the csp->headers list */
672    for (f = more_headers; *f ; f++)
673    {
674       (*f)(csp);
675    }
676
677    hdr = list_to_text(csp->headers);
678
679    return hdr;
680
681 }
682
683
684 /*********************************************************************
685  *
686  * Function    :  free_http_request
687  *
688  * Description :  Freez a http_request structure
689  *
690  * Parameters  :
691  *          1  :  http = points to a http_request structure to free
692  *
693  * Returns     :  N/A
694  *
695  *********************************************************************/
696 void free_http_request(struct http_request *http)
697 {
698    assert(http);
699
700    freez(http->cmd);
701    freez(http->gpc);
702    freez(http->host);
703    freez(http->url);
704    freez(http->hostport);
705    freez(http->path);
706    freez(http->ver);
707    freez(http->host_ip_addr_str);
708
709 }
710
711
712 /*********************************************************************
713  *
714  * Function    :  parse_http_request
715  *
716  * Description :  Parse out the host and port from the URL.  Find the
717  *                hostname & path, port (if ':'), and/or password (if '@')
718  *
719  * Parameters  :
720  *          1  :  req = URL (or is it URI?) to break down
721  *          2  :  http = pointer to the http structure to hold elements
722  *          3  :  csp = Current client state (buffers, headers, etc...)
723  *
724  * Returns     :  N/A
725  *
726  *********************************************************************/
727 void parse_http_request(char *req, struct http_request *http, struct client_state *csp)
728 {
729    char *buf, *v[10], *url, *p;
730    int n;
731
732    memset(http, '\0', sizeof(*http));
733    http->cmd = strdup(req);
734
735    buf = strdup(req);
736    n = ssplit(buf, " \r\n", v, SZ(v), 1, 1);
737
738    if (n == 3)
739    {
740       /* this could be a CONNECT request */
741       if (strcmpic(v[0], "connect") == 0)
742       {
743          http->ssl      = 1;
744          http->gpc      = strdup(v[0]);
745          http->hostport = strdup(v[1]);
746          http->ver      = strdup(v[2]);
747       }
748
749       /* or it could be any other basic HTTP request type */
750       if ((0 == strcmpic(v[0], "get"))
751        || (0 == strcmpic(v[0], "head"))
752        || (0 == strcmpic(v[0], "post"))
753        || (0 == strcmpic(v[0], "put"))
754        || (0 == strcmpic(v[0], "delete"))
755
756        /* or a webDAV extension (RFC2518) */
757        || (0 == strcmpic(v[0], "propfind"))
758        || (0 == strcmpic(v[0], "proppatch"))
759        || (0 == strcmpic(v[0], "move"))
760        || (0 == strcmpic(v[0], "copy"))
761        || (0 == strcmpic(v[0], "mkcol"))
762        || (0 == strcmpic(v[0], "lock"))
763        || (0 == strcmpic(v[0], "unlock"))
764        )
765       {
766          http->ssl    = 0;
767          http->gpc    = strdup(v[0]);
768          http->url    = strdup(v[1]);
769          http->ver    = strdup(v[2]);
770
771          url = v[1];
772          if (strncmpic(url, "http://",  7) == 0)
773          {
774             url += 7;
775          }
776          else if (strncmpic(url, "https://", 8) == 0)
777          {
778             url += 8;
779          }
780          else
781          {
782             url = NULL;
783          }
784
785          if (url)
786          {
787             if ((p = strchr(url, '/')))
788             {
789                http->path = strdup(p);
790                *p = '\0';
791                http->hostport = strdup(url);
792             }
793             /*
794              * Repair broken HTTP requests that don't contain a path
795              */
796             else
797             {
798                http->path = strdup("/");
799                http->hostport = strdup(url);
800             }
801          }
802       }
803    }
804
805    freez(buf);
806
807
808    if (http->hostport == NULL)
809    {
810       free_http_request(http);
811       return;
812    }
813
814    buf = strdup(http->hostport);
815
816
817    /* check if url contains password */
818    n = ssplit(buf, "@", v, SZ(v), 1, 1);
819    if (n == 2)
820    {
821       char * newbuf = NULL;
822       newbuf = strdup(v[1]);
823       freez(buf);
824       buf = newbuf;
825    }
826
827    n = ssplit(buf, ":", v, SZ(v), 1, 1);
828
829    if (n == 1)
830    {
831       http->host = strdup(v[0]);
832       http->port = 80;
833    }
834
835    if (n == 2)
836    {
837       http->host = strdup(v[0]);
838       http->port = atoi(v[1]);
839    }
840
841    freez(buf);
842
843    if (http->host == NULL)
844    {
845       free_http_request(http);
846    }
847
848    if (http->path == NULL)
849    {
850       http->path = strdup("/");
851    }
852
853 }
854
855
856 /* here begins the family of parser functions that reformat header lines */
857
858
859 /*********************************************************************
860  *
861  * Function    :  crumble
862  *
863  * Description :  This is called if a header matches a pattern to "crunch"
864  *
865  * Parameters  :
866  *          1  :  v = Pointer to parsers structure, which basically holds
867  *                headers (client or server) that we want to "crunch"
868  *          2  :  s = header (from sed) to "crunch"
869  *          3  :  csp = Current client state (buffers, headers, etc...)
870  *
871  * Returns     :  Always NULL.
872  *
873  *********************************************************************/
874 char *crumble(const struct parsers *v, const char *s, struct client_state *csp)
875 {
876    log_error(LOG_LEVEL_HEADER, "crunch!");
877    return(NULL);
878
879 }
880
881
882 /*********************************************************************
883  *
884  * Function    :  server_content_type
885  *
886  * Description :  Set the content-type for filterable types (text/.*,
887  *                javascript and image/gif) unless filtering has been
888  *                forbidden (CT_TABOO) while parsing earlier headers.
889  *
890  * Parameters  :
891  *          1  :  v = ignored
892  *          2  :  s = header string we are "considering"
893  *          3  :  csp = Current client state (buffers, headers, etc...)
894  *
895  * Returns     :  A duplicate string pointer to this header (ie. pass thru)
896  *
897  *********************************************************************/
898 char *server_content_type(const struct parsers *v, const char *s, struct client_state *csp)
899 {
900    if (csp->content_type != CT_TABOO)
901    {
902       if (strstr(s, " text/") || strstr(s, "application/x-javascript"))
903          csp->content_type = CT_TEXT;
904       else if (strstr(s, " image/gif"))
905          csp->content_type = CT_GIF;
906       else
907          csp->content_type = 0;
908    }
909
910    return(strdup(s));
911
912 }
913
914
915 /*********************************************************************
916  *
917  * Function    :  server_transfer_coding
918  *
919  * Description :  - Prohibit filtering (CT_TABOO) if transfer coding compresses
920  *                - Raise the CSP_FLAG_CHUNKED flag if coding is "chunked"
921  *                - Change from "chunked" to "identity" if body was chunked
922  *                  but has been de-chunked for filtering.
923  *
924  * Parameters  :
925  *          1  :  v = ignored
926  *          2  :  s = header string we are "considering"
927  *          3  :  csp = Current client state (buffers, headers, etc...)
928  *
929  * Returns     :  A duplicate string pointer to this header (ie. pass thru)
930  *
931  *********************************************************************/
932 char *server_transfer_coding(const struct parsers *v, const char *s, struct client_state *csp)
933 {
934    /*
935     * Turn off pcrs and gif filtering if body compressed
936     */
937    if (strstr(s, "gzip") || strstr(s, "compress") || strstr(s, "deflate"))
938    {
939       csp->content_type = CT_TABOO;
940    }
941
942    /*
943     * Raise flag if body chunked
944     */
945    if (strstr(s, "chunked"))
946    {
947       csp->flags |= CSP_FLAG_CHUNKED;
948
949       /*
950        * If the body was modified, it has been
951        * de-chunked first, so adjust the header:
952        */
953       if (csp->flags & CSP_FLAG_MODIFIED)
954       {
955          return(strdup("Transfer-Encoding: identity"));
956       }
957    }
958
959    return(strdup(s));
960
961 }
962
963
964 /*********************************************************************
965  *
966  * Function    :  server_content_encoding
967  *
968  * Description :  Prohibit filtering (CT_TABOO) if content encoding compresses
969  *
970  * Parameters  :
971  *          1  :  v = ignored
972  *          2  :  s = header string we are "considering"
973  *          3  :  csp = Current client state (buffers, headers, etc...)
974  *
975  * Returns     :  A duplicate string pointer to this header (ie. pass thru)
976  *
977  *********************************************************************/
978 char *server_content_encoding(const struct parsers *v, const char *s, struct client_state *csp)
979 {
980    /*
981     * Turn off pcrs and gif filtering if body compressed
982     */
983    if (strstr(s, "gzip") || strstr(s, "compress") || strstr(s, "deflate"))
984    {
985       csp->content_type = CT_TABOO;
986    }
987
988    return(strdup(s));
989
990 }
991
992
993 /*********************************************************************
994  *
995  * Function    :  server_content_length
996  *
997  * Description :  Adjust Content-Length header if we modified
998  *                the body.
999  *
1000  * Parameters  :
1001  *          1  :  v = ignored
1002  *          2  :  s = header string we are "considering"
1003  *          3  :  csp = Current client state (buffers, headers, etc...)
1004  *
1005  * Returns     :  A duplicate string pointer to this header (ie. pass thru)
1006  *
1007  *********************************************************************/
1008 char *server_content_length(const struct parsers *v, const char *s, struct client_state *csp)
1009 {
1010    if (csp->content_length != 0) /* Content length has been modified */
1011    {
1012       char * s2 = (char *) zalloc(100);
1013       sprintf(s2, "Content-Length: %d", (int) csp->content_length);
1014
1015       log_error(LOG_LEVEL_HEADER, "Adjust Content-Length to %d", (int) csp->content_length);
1016       return(s2);
1017    }
1018    else
1019    {
1020       return(strdup(s));
1021    }
1022
1023 }
1024
1025
1026 /*********************************************************************
1027  *
1028  * Function    :  server_content_md5
1029  *
1030  * Description :  Crumble any Content-MD5 headers if the document was
1031  *                modified. FIXME: Should we re-compute instead?
1032  *
1033  * Parameters  :
1034  *          1  :  v = ignored
1035  *          2  :  s = header string we are "considering"
1036  *          3  :  csp = Current client state (buffers, headers, etc...)
1037  *
1038  * Returns     :  A duplicate string pointer to this header (ie. pass thru)
1039  *
1040  *********************************************************************/
1041 char *server_content_md5(const struct parsers *v, const char *s, struct client_state *csp)
1042 {
1043    if (csp->flags & CSP_FLAG_MODIFIED)
1044    {
1045       log_error(LOG_LEVEL_HEADER, "Crunching Content-MD5");
1046       return(NULL);
1047    }
1048    else
1049    {
1050       return(strdup(s));
1051    }
1052
1053 }
1054
1055
1056 /*********************************************************************
1057  *
1058  * Function    :  client_accept_encoding
1059  *
1060  * Description :  Rewrite the client's Accept-Encoding header so that
1061  *                if doesn't allow compression, if the action applies.
1062  *                Note: For HTTP/1.0 the absence of the header is enough.
1063  *
1064  * Parameters  :
1065  *          1  :  v = ignored
1066  *          2  :  s = header string we are "considering"
1067  *          3  :  csp = Current client state (buffers, headers, etc...)
1068  *
1069  * Returns     :  A copy of the client's original or the modified header.
1070  *
1071  *********************************************************************/
1072 char *client_accept_encoding(const struct parsers *v, const char *s, struct client_state *csp)
1073 {
1074    if ((csp->action->flags & ACTION_NO_COMPRESSION) == 0)
1075    {
1076       return(strdup(s));
1077    }
1078    else
1079    {
1080       log_error(LOG_LEVEL_HEADER, "Supressed offer to compress content");
1081
1082       if (!strcmpic(csp->http->ver, "HTTP/1.1"))
1083       {
1084          return(strdup("Accept-Encoding: identity;q=1.0, *;q=0"));
1085       }
1086       else
1087       {
1088          return(NULL);
1089       }
1090    }
1091
1092 }
1093
1094
1095 /*********************************************************************
1096  *
1097  * Function    :  client_te
1098  *
1099  * Description :  Rewrite the client's TE header so that
1100  *                if doesn't allow compression, if the action applies.
1101  *
1102  * Parameters  :
1103  *          1  :  v = ignored
1104  *          2  :  s = header string we are "considering"
1105  *          3  :  csp = Current client state (buffers, headers, etc...)
1106  *
1107  * Returns     :  A copy of the client's original or the modified header.
1108  *
1109  *********************************************************************/
1110 char *client_te(const struct parsers *v, const char *s, struct client_state *csp)
1111 {
1112    if ((csp->action->flags & ACTION_NO_COMPRESSION) == 0)
1113    {
1114       return(strdup(s));
1115    }
1116    else
1117    {
1118       log_error(LOG_LEVEL_HEADER, "Supressed offer to compress transfer");
1119       return(NULL);
1120    }
1121
1122 }
1123
1124 /*********************************************************************
1125  *
1126  * Function    :  client_referrer
1127  *
1128  * Description :  Handle the "referer" config setting properly.
1129  *                Called from `sed'.
1130  *
1131  * Parameters  :
1132  *          1  :  v = ignored
1133  *          2  :  s = header (from sed) to "crunch"
1134  *          3  :  csp = Current client state (buffers, headers, etc...)
1135  *
1136  * Returns     :  NULL if crunched, or a malloc'ed string with the original
1137  *                or modified header
1138  *
1139  *********************************************************************/
1140 char *client_referrer(const struct parsers *v, const char *s, struct client_state *csp)
1141 {
1142    const char * newval;
1143    char * s2;
1144 #ifdef FEATURE_FORCE_LOAD
1145    /* Since the referrer can include the prefix even
1146     * even if the request itself is non-forced, we must
1147     * clean it unconditionally
1148     */
1149    strclean(s, FORCE_PREFIX);
1150 #endif /* def FEATURE_FORCE_LOAD */
1151
1152    /*
1153     * Are we sending referer?
1154     */
1155    if ((csp->action->flags & ACTION_HIDE_REFERER) == 0)
1156    {
1157       return(strdup(s));
1158    }
1159
1160    newval = csp->action->string[ACTION_STRING_REFERER];
1161
1162    /*
1163     * Are we blocking referer?
1164     */
1165    if ((newval == NULL) || (0 == strcmpic(newval, "block")) )
1166    {
1167       log_error(LOG_LEVEL_HEADER, "crunch!");
1168       return(NULL);
1169    }
1170
1171    /*
1172     * Are we forging referer?
1173     */
1174    if (0 == strcmpic(newval, "forge"))
1175    {
1176       /*
1177        * Forge a referer as http://[hostname:port of REQUEST]/
1178        * to fool stupid checks for in-site links
1179        */
1180       log_error(LOG_LEVEL_HEADER, "crunch+forge!");
1181       s2 = strsav(NULL, "Referer: ");
1182       s2 = strsav(s2, "http://");
1183       s2 = strsav(s2, csp->http->hostport);
1184       s2 = strsav(s2, "/");
1185       return(s2);
1186    }
1187
1188    /*
1189     * Have we got a fixed referer?
1190     */
1191    if (0 == strncmpic(newval, "http://", 7))
1192    {
1193       /*
1194        * We have a specific (fixed) referer we want to send.
1195        */
1196       char * s3;
1197
1198       log_error(LOG_LEVEL_HEADER, "modified");
1199
1200       s3 = strsav( NULL, "Referer: " );
1201       s3 = strsav( s3, newval );
1202       return(s3);
1203    }
1204
1205    /* Should never get here! */
1206    log_error(LOG_LEVEL_ERROR, "Bad parameter: +referer{%s}", newval);
1207
1208    /*
1209     * Forge is probably the best default.
1210     *
1211     * Forge a referer as http://[hostname:port of REQUEST]/
1212     * to fool stupid checks for in-site links
1213     */
1214    log_error(LOG_LEVEL_HEADER, "crunch+forge!");
1215    s2 = strsav(NULL, "Referer: ");
1216    s2 = strsav(s2, "http://");
1217    s2 = strsav(s2, csp->http->hostport);
1218    s2 = strsav(s2, "/");
1219    return(s2);
1220 }
1221
1222
1223 /*********************************************************************
1224  *
1225  * Function    :  client_uagent
1226  *
1227  * Description :  Handle the "user-agent" config setting properly
1228  *                and remember its original value to enable browser
1229  *                bug workarounds. Called from `sed'.
1230  *
1231  * Parameters  :
1232  *          1  :  v = ignored
1233  *          2  :  s = header (from sed) to "crunch"
1234  *          3  :  csp = Current client state (buffers, headers, etc...)
1235  *
1236  * Returns     :  A malloc'ed pointer to the default agent, or
1237  *                a malloc'ed string pointer to this header (ie. pass thru).
1238  *
1239  *********************************************************************/
1240 char *client_uagent(const struct parsers *v, const char *s, struct client_state *csp)
1241 {
1242    const char * newval;
1243    char * s2;
1244
1245    if ((csp->action->flags & ACTION_HIDE_USER_AGENT) == 0)
1246    {
1247       return(strdup(s));
1248    }
1249
1250    newval = csp->action->string[ACTION_STRING_USER_AGENT];
1251    if (newval == NULL)
1252    {
1253       return(strdup(s));
1254    }
1255
1256    log_error(LOG_LEVEL_HEADER, "modified");
1257
1258    s2 = strsav( NULL, "User-Agent: " );
1259    s2 = strsav( s2, newval );
1260    return(s2);
1261
1262 }
1263
1264
1265 /*********************************************************************
1266  *
1267  * Function    :  client_ua
1268  *
1269  * Description :  Handle "ua-" headers properly.  Called from `sed'.
1270  *
1271  * Parameters  :
1272  *          1  :  v = ignored
1273  *          2  :  s = header (from sed) to "crunch"
1274  *          3  :  csp = Current client state (buffers, headers, etc...)
1275  *
1276  * Returns     :  NULL if crunched, or a malloc'ed string to original header
1277  *
1278  *********************************************************************/
1279 char *client_ua(const struct parsers *v, const char *s, struct client_state *csp)
1280 {
1281    if ((csp->action->flags & ACTION_HIDE_USER_AGENT) == 0)
1282    {
1283       return(strdup(s));
1284    }
1285    else
1286    {
1287       log_error(LOG_LEVEL_HEADER, "crunch!");
1288       return(NULL);
1289    }
1290 }
1291
1292
1293 /*********************************************************************
1294  *
1295  * Function    :  client_from
1296  *
1297  * Description :  Handle the "from" config setting properly.
1298  *                Called from `sed'.
1299  *
1300  * Parameters  :
1301  *          1  :  v = ignored
1302  *          2  :  s = header (from sed) to "crunch"
1303  *          3  :  csp = Current client state (buffers, headers, etc...)
1304  *
1305  * Returns     :  NULL if crunched, or a malloc'ed string to
1306  *                modified/original header.
1307  *
1308  *********************************************************************/
1309 char *client_from(const struct parsers *v, const char *s, struct client_state *csp)
1310 {
1311    const char * newval;
1312    char * s2;
1313
1314    if ((csp->action->flags & ACTION_HIDE_FROM) == 0)
1315    {
1316       return(strdup(s));
1317    }
1318
1319    newval = csp->action->string[ACTION_STRING_FROM];
1320
1321    /*
1322     * Are we blocking referer?
1323     */
1324    if ((newval == NULL) || (0 == strcmpic(newval, "block")) )
1325    {
1326       log_error(LOG_LEVEL_HEADER, "crunch!");
1327       return(NULL);
1328    }
1329
1330    log_error(LOG_LEVEL_HEADER, " modified");
1331
1332    s2 = strsav( NULL, "From: " );
1333    s2 = strsav( s2, newval );
1334    return(s2);
1335
1336 }
1337
1338
1339 /*********************************************************************
1340  *
1341  * Function    :  client_send_cookie
1342  *
1343  * Description :  Handle the "cookie" header properly.  Called from `sed'.
1344  *                If cookie is accepted, add it to the cookie_list,
1345  *                else we crunch it.  Mmmmmmmmmmm ... cookie ......
1346  *
1347  * Parameters  :
1348  *          1  :  v = pattern of cookie `sed' found matching
1349  *          2  :  s = header (from sed) to "crunch"
1350  *          3  :  csp = Current client state (buffers, headers, etc...)
1351  *
1352  * Returns     :  Always NULL.
1353  *
1354  *********************************************************************/
1355 char *client_send_cookie(const struct parsers *v, const char *s, struct client_state *csp)
1356 {
1357    if ((csp->action->flags & ACTION_NO_COOKIE_READ) == 0)
1358    {
1359       enlist(csp->cookie_list, s + v->len + 1);
1360    }
1361    else
1362    {
1363       log_error(LOG_LEVEL_HEADER, " crunch!");
1364    }
1365
1366    /*
1367     * Always return NULL here.  The cookie header
1368     * will be sent at the end of the header.
1369     */
1370    return(NULL);
1371
1372 }
1373
1374
1375 /*********************************************************************
1376  *
1377  * Function    :  client_x_forwarded
1378  *
1379  * Description :  Handle the "x-forwarded-for" config setting properly,
1380  *                also used in the add_client_headers list.  Called from `sed'.
1381  *
1382  * Parameters  :
1383  *          1  :  v = ignored
1384  *          2  :  s = header (from sed) to "crunch"
1385  *          3  :  csp = Current client state (buffers, headers, etc...)
1386  *
1387  * Returns     :  Always NULL.
1388  *
1389  *********************************************************************/
1390 char *client_x_forwarded(const struct parsers *v, const char *s, struct client_state *csp)
1391 {
1392    if ((csp->action->flags & ACTION_HIDE_FORWARDED) == 0)
1393    {
1394       /* Save it so we can re-add it later */
1395       csp->x_forwarded = strdup(s);
1396    }
1397
1398    /*
1399     * Always return NULL, since this information
1400     * will be sent at the end of the header.
1401     */
1402
1403    return(NULL);
1404
1405 }
1406
1407 /* the following functions add headers directly to the header list */
1408
1409 /*********************************************************************
1410  *
1411  * Function    :  client_host_adder
1412  *
1413  * Description :  (re)adds the host header. Called from `sed'.
1414  *
1415  * Parameters  :
1416  *          1  :  csp = Current client state (buffers, headers, etc...)
1417  *
1418  * Returns     :  N/A
1419  *
1420  *********************************************************************/
1421 void client_host_adder(struct client_state *csp)
1422 {
1423    char *p = NULL,
1424         *pos = NULL;
1425
1426    if ( !csp->http->hostport || !*(csp->http->hostport)) return;
1427    p = strsav(p, "Host: ");
1428    /*
1429    ** remove 'user:pass@' from 'proto://user:pass@host'
1430    */
1431    if ( (pos = strchr( csp->http->hostport, '@')) != NULL )
1432    {
1433        p = strsav(p, pos+1);
1434    }
1435    else
1436    {
1437       p = strsav(p, csp->http->hostport);
1438    }
1439    log_error(LOG_LEVEL_HEADER, "addh: %s", p);
1440    enlist(csp->headers, p);
1441
1442    freez(p);
1443 }
1444
1445
1446 /*********************************************************************
1447  *
1448  * Function    :  client_cookie_adder
1449  *
1450  * Description :  Used in the add_client_headers list.  Called from `sed'.
1451  *
1452  * Parameters  :
1453  *          1  :  csp = Current client state (buffers, headers, etc...)
1454  *
1455  * Returns     :  N/A
1456  *
1457  *********************************************************************/
1458 void client_cookie_adder(struct client_state *csp)
1459 {
1460    struct list_entry *lst;
1461    char *tmp = NULL;
1462    char *e;
1463
1464    for (lst = csp->cookie_list->first; lst ; lst = lst->next)
1465    {
1466       if (tmp)
1467       {
1468          tmp = strsav(tmp, "; ");
1469       }
1470       tmp = strsav(tmp, lst->str);
1471    }
1472
1473    for (lst = csp->action->multi[ACTION_MULTI_WAFER]->first;  lst ; lst = lst->next)
1474    {
1475       if (tmp)
1476       {
1477          tmp = strsav(tmp, "; ");
1478       }
1479
1480       if ((e = cookie_encode(lst->str)))
1481       {
1482          tmp = strsav(tmp, e);
1483          freez(e);
1484       }
1485    }
1486
1487    if (tmp)
1488    {
1489       char *ret;
1490
1491       ret = strdup("Cookie: ");
1492       ret = strsav(ret, tmp);
1493       log_error(LOG_LEVEL_HEADER, "addh: %s", ret);
1494       enlist(csp->headers, ret);
1495       freez(tmp);
1496       freez(ret);
1497    }
1498
1499 }
1500
1501
1502 /*********************************************************************
1503  *
1504  * Function    :  client_accept_encoding_adder
1505  *
1506  * Description :  Add an Accept-Encoding header to the client's request
1507  *                that disables compression if the action applies, and
1508  *                the header is not already there. Called from `sed'.
1509  *                Note: For HTTP/1.0, the absence of the header is enough.
1510  *
1511  * Parameters  :
1512  *          1  :  csp = Current client state (buffers, headers, etc...)
1513  *
1514  * Returns     :  N/A
1515  *
1516  *********************************************************************/
1517 void client_accept_encoding_adder(struct client_state *csp)
1518 {
1519    if (   ((csp->action->flags & ACTION_NO_COMPRESSION) != 0)
1520        && (!strcmpic(csp->http->ver, "HTTP/1.1")) )
1521    {
1522       enlist_unique(csp->headers, "Accept-Encoding: identity;q=1.0, *;q=0", 16);
1523    }
1524
1525 }
1526
1527
1528 /*********************************************************************
1529  *
1530  * Function    :  client_xtra_adder
1531  *
1532  * Description :  Used in the add_client_headers list.  Called from `sed'.
1533  *
1534  * Parameters  :
1535  *          1  :  csp = Current client state (buffers, headers, etc...)
1536  *
1537  * Returns     :  N/A
1538  *
1539  *********************************************************************/
1540 void client_xtra_adder(struct client_state *csp)
1541 {
1542    struct list_entry *lst;
1543
1544    for (lst = csp->action->multi[ACTION_MULTI_ADD_HEADER]->first;
1545         lst ; lst = lst->next)
1546    {
1547       log_error(LOG_LEVEL_HEADER, "addh: %s", lst->str);
1548       enlist(csp->headers, lst->str);
1549    }
1550
1551 }
1552
1553
1554 /*********************************************************************
1555  *
1556  * Function    :  client_x_forwarded_adder
1557  *
1558  * Description :  Used in the add_client_headers list.  Called from `sed'.
1559  *
1560  * Parameters  :
1561  *          1  :  csp = Current client state (buffers, headers, etc...)
1562  *
1563  * Returns     :  N/A
1564  *
1565  *********************************************************************/
1566 void client_x_forwarded_adder(struct client_state *csp)
1567 {
1568    char *p = NULL;
1569
1570    if ((csp->action->flags & ACTION_HIDE_FORWARDED) != 0)
1571    {
1572       return;
1573    }
1574
1575    if (csp->x_forwarded)
1576    {
1577       p = strsav(p, csp->x_forwarded);
1578       p = strsav(p, ", ");
1579       p = strsav(p, csp->ip_addr_str);
1580    }
1581    else
1582    {
1583       p = strsav(p, "X-Forwarded-For: ");
1584       p = strsav(p, csp->ip_addr_str);
1585    }
1586
1587    log_error(LOG_LEVEL_HEADER, "addh: %s", p);
1588    enlist(csp->headers, p);
1589
1590    freez(p);
1591 }
1592
1593
1594 /*********************************************************************
1595  *
1596  * Function    :  connection_close_adder
1597  *
1598  * Description :  Adds a "Connection: close" header to csp->headers
1599  *                as a temporary fix for the needed but missing HTTP/1.1
1600  *                support. Called from `sed'.
1601  *                FIXME: This whole function shouldn't be neccessary!
1602  *
1603  * Parameters  :
1604  *          1  :  csp = Current client state (buffers, headers, etc...)
1605  *
1606  * Returns     :  N/A
1607  *
1608  *********************************************************************/
1609 void connection_close_adder(struct client_state *csp)
1610 {
1611    enlist(csp->headers, "Connection: close");
1612 }
1613
1614
1615 /*********************************************************************
1616  *
1617  * Function    :  server_http
1618  *
1619  * Description :  - Save the HTTP Status into csp->http->status
1620  *                - Set CT_TABOO to prevent filtering if the answer
1621  *                  is a partial range (HTTP status 206)
1622  *                - Rewrite HTTP/1.1 answers to HTTP/1.0 if +downgrade
1623  *                  action applies.
1624  *
1625  * Parameters  :
1626  *          1  :  v = parser pattern that matched this header
1627  *          2  :  s = header that matched this pattern
1628  *          3  :  csp = Current client state (buffers, headers, etc...)
1629  *
1630  * Returns     :  Copy of changed  or original answer.
1631  *
1632  *********************************************************************/
1633 char *server_http(const struct parsers *v, const char *s, struct client_state *csp)
1634 {
1635    char *ret = strdup(s);
1636
1637    sscanf(ret, "HTTP/%*d.%*d %d", &(csp->http->status));
1638    if (csp->http->status == 206)
1639    {
1640       csp->content_type = CT_TABOO;
1641    }
1642
1643    if ((csp->action->flags & ACTION_DOWNGRADE) != 0)
1644    {
1645       ret[7] = '0';
1646       log_error(LOG_LEVEL_HEADER, "Downgraded answer to HTTP/1.0");
1647    }
1648    return(ret);
1649
1650 }
1651
1652
1653 /*********************************************************************
1654  *
1655  * Function    :  server_set_cookie
1656  *
1657  * Description :  Handle the server "cookie" header properly.
1658  *                Log cookie to the jar file.  Then "crunch" it,
1659  *                or accept it.  Called from `sed'.
1660  *
1661  * Parameters  :
1662  *          1  :  v = parser pattern that matched this header
1663  *          2  :  s = header that matched this pattern
1664  *          3  :  csp = Current client state (buffers, headers, etc...)
1665  *
1666  * Returns     :  `crumble' or a newly malloc'ed string.
1667  *
1668  *********************************************************************/
1669 char *server_set_cookie(const struct parsers *v, const char *s, struct client_state *csp)
1670 {
1671 #ifdef FEATURE_COOKIE_JAR
1672    if (csp->config->jar)
1673    {
1674       /*
1675        * Write timestamp into outbuf.
1676        *
1677        * Complex because not all OSs have tm_gmtoff or
1678        * the %z field in strftime()
1679        */
1680       char tempbuf[ BUFFER_SIZE ];
1681       time_t now; 
1682       struct tm tm_now; 
1683       time (&now); 
1684 #ifdef HAVE_LOCALTIME_R
1685        tm_now = *localtime_r(&now, &tm_now);
1686 #else
1687        tm_now = *localtime (&now); 
1688 #endif
1689       strftime(tempbuf, BUFFER_SIZE-6, "%b %d %H:%M:%S ", &tm_now); 
1690
1691       fprintf(csp->config->jar, "%s %s\t%s\n", tempbuf, csp->http->host, (s + v->len + 1));
1692    }
1693 #endif /* def FEATURE_COOKIE_JAR */
1694
1695    if ((csp->action->flags & ACTION_NO_COOKIE_SET) != 0)
1696    {
1697       return(crumble(v, s, csp));
1698    }
1699    else if ((csp->action->flags & ACTION_NO_COOKIE_KEEP) != 0)
1700    {
1701       /* Flag whether or not to log a message */
1702       int changed = 0;
1703
1704       /* A variable to store the tag we're working on */
1705       char * cur_tag;
1706
1707       /* Make a copy of the header we can write to */
1708       char * result = strdup(s);
1709       if (result == NULL)
1710       {
1711          /* FIXME: This error handling is incorrect */
1712          return NULL;
1713       }
1714
1715       /* Skip "Set-Cookie:" (11 characters) in header */
1716       cur_tag = result + 11;
1717
1718       /* skip whitespace between "Set-Cookie:" and value */
1719       while (*cur_tag && ijb_isspace(*cur_tag))
1720       {
1721          cur_tag++;
1722       }
1723
1724       /* Loop through each tag in the cookie */
1725       while (*cur_tag)
1726       {
1727          /* Find next tag */
1728          char * next_tag = strchr(cur_tag, ';');
1729          if (next_tag != NULL)
1730          {
1731             /* Skip the ';' character itself */
1732             next_tag++;
1733
1734             /* skip whitespace ";" and start of tag */
1735             while (*next_tag && ijb_isspace(*next_tag))
1736             {
1737                next_tag++;
1738             }
1739          }
1740          else
1741          {
1742             /* "Next tag" is the end of the string */
1743             next_tag = cur_tag + strlen(cur_tag);
1744          }
1745
1746          /* Is this the "Expires" tag? */
1747          if (strncmpic(cur_tag, "expires=", 8) == 0)
1748          {
1749             /* Delete the tag by copying the rest of the string over it.
1750              * (Note that we cannot just use "strcpy(cur_tag, next_tag)",
1751              * since the behaviour of strcpy is undefined for overlapping
1752              * strings.)
1753              */
1754             memmove(cur_tag, next_tag, strlen(next_tag) + 1);
1755
1756             /* That changed the header, need to issue a log message */
1757             changed = 1;
1758
1759             /* Note that the next tag has now been moved to *cur_tag,
1760              * so we do not need to update the cur_tag pointer.
1761              */
1762          }
1763          else
1764          {
1765             /* Move on to next cookie tag */
1766             cur_tag = next_tag;
1767          }
1768       }
1769
1770       if (changed)
1771       {
1772          log_error(LOG_LEVEL_HEADER, "Changed cookie to a temporary one.");
1773       }
1774
1775       return result;
1776    }
1777
1778    return(strdup(s));
1779 }
1780
1781
1782 #ifdef FEATURE_FORCE_LOAD
1783 /*********************************************************************
1784  *
1785  * Function    :  strclean
1786  *
1787  * Description :  In-Situ-Eliminate all occurances of substring in
1788  *                string
1789  *
1790  * Parameters  :
1791  *          1  :  string = string to clean
1792  *          2  :  substring = substring to eliminate
1793  *
1794  * Returns     :  Number of eliminations
1795  *
1796  *********************************************************************/
1797 int strclean(const char *string, const char *substring)
1798 {
1799    int hits = 0, len = strlen(substring);
1800    char *pos, *p;
1801
1802    while((pos = strstr(string, substring)))
1803    {
1804       p = pos + len;
1805       do
1806       {
1807          *(p - len) = *p;
1808       }
1809       while (*p++ != '\0');
1810
1811       hits++;
1812    }
1813
1814    return(hits);
1815 }
1816 #endif /* def FEATURE_FORCE_LOAD */
1817
1818
1819 /*
1820   Local Variables:
1821   tab-width: 3
1822   end:
1823 */