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