Fix +hide-if-modified-since{-n}.
[privoxy.git] / parsers.c
1 const char parsers_rcs[] = "$Id: parsers.c,v 1.70 2006/09/08 12:06:34 fabiankeil 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  *                Privoxy team. http://www.privoxy.org/
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.70  2006/09/08 12:06:34  fabiankeil
44  *    Have hide-if-modified-since interpret the random
45  *    range value as minutes instead of hours. Allows
46  *    more fine-grained configuration.
47  *
48  *    Revision 1.69  2006/09/06 16:25:51  fabiankeil
49  *    Always have parse_header_time return a pointer
50  *    that actual makes sense, even though we currently
51  *    only need it to detect problems.
52  *
53  *    Revision 1.68  2006/09/06 10:43:32  fabiankeil
54  *    Added config option enable-remote-http-toggle
55  *    to specify if Privoxy should recognize special
56  *    headers (currently only X-Filter) to change its
57  *    behaviour. Disabled by default.
58  *
59  *    Revision 1.67  2006/09/04 11:01:26  fabiankeil
60  *    After filtering de-chunked instances, remove
61  *    "Transfer-Encoding" header entirely instead of changing
62  *    it to "Transfer-Encoding: identity", which is invalid.
63  *    Thanks Michael Shields <shields@msrl.com>. Fixes PR 1318658.
64  *
65  *    Don't use localtime in parse_header_time. An empty time struct
66  *    is good enough, it gets overwritten by strptime anyway.
67  *
68  *    Revision 1.66  2006/09/03 19:38:28  fabiankeil
69  *    Use gmtime_r if available, fallback to gmtime with mutex
70  *    protection for MacOSX and use vanilla gmtime for the rest.
71  *
72  *    Revision 1.65  2006/08/22 10:55:56  fabiankeil
73  *    Changed client_referrer to use the right type (size_t) for
74  *    hostlenght and to shorten the temporary referrer string with
75  *    '\0' instead of adding a useless line break.
76  *
77  *    Revision 1.64  2006/08/17 17:15:10  fabiankeil
78  *    - Back to timegm() using GnuPG's replacement if necessary.
79  *      Using mktime() and localtime() could add a on hour offset if
80  *      the randomize factor was big enough to lead to a summer/wintertime
81  *      switch.
82  *
83  *    - Removed now-useless Privoxy 3.0.3 compatibility glue.
84  *
85  *    - Moved randomization code into pick_from_range().
86  *
87  *    - Changed parse_header_time definition.
88  *      time_t isn't guaranteed to be signed and
89  *      if it isn't, -1 isn't available as error code.
90  *      Changed some variable types in client_if_modified_since()
91  *      because of the same reason.
92  *
93  *    Revision 1.63  2006/08/14 13:18:08  david__schmidt
94  *    OS/2 compilation compatibility fixups
95  *
96  *    Revision 1.62  2006/08/14 08:58:42  fabiankeil
97  *    Changed include from strptime.c to strptime.h
98  *
99  *    Revision 1.61  2006/08/14 08:25:19  fabiankeil
100  *    Split filter-headers{} into filter-client-headers{}
101  *    and filter-server-headers{}.
102  *    Added parse_header_time() to share some code.
103  *    Replaced timegm() with mktime().
104  *
105  *    Revision 1.60  2006/08/12 03:54:37  david__schmidt
106  *    Windows service integration
107  *
108  *    Revision 1.59  2006/08/03 02:46:41  david__schmidt
109  *    Incorporate Fabian Keil's patch work:\rhttp://www.fabiankeil.de/sourcecode/privoxy/
110  *
111  *    Revision 1.58  2006/07/18 14:48:47  david__schmidt
112  *    Reorganizing the repository: swapping out what was HEAD (the old 3.1 branch)
113  *    with what was really the latest development (the v_3_0_branch branch)
114  *
115  *    Revision 1.56.2.10  2006/01/21 16:16:08  david__schmidt
116  *    Thanks to  Edward Carrel for his patch to modernize OSX's\rpthreads support.  See bug #1409623.
117  *
118  *    Revision 1.56.2.9  2004/10/03 12:53:45  david__schmidt
119  *    Add the ability to check jpeg images for invalid
120  *    lengths of comment blocks.  Defensive strategy
121  *    against the exploit:
122  *       Microsoft Security Bulletin MS04-028
123  *       Buffer Overrun in JPEG Processing (GDI+) Could
124  *       Allow Code Execution (833987)
125  *    Enabled with +inspect-jpegs in actions files.
126  *
127  *    Revision 1.56.2.8  2003/07/11 13:21:25  oes
128  *    Excluded text/plain objects from filtering. This fixes a
129  *    couple of client-crashing, download corruption and
130  *    Privoxy performance issues, whose root cause lies in
131  *    web servers labelling content of unknown type as text/plain.
132  *
133  *    Revision 1.56.2.7  2003/05/06 12:07:26  oes
134  *    Fixed bug #729900: Suspicious HOST: headers are now killed and regenerated if necessary
135  *
136  *    Revision 1.56.2.6  2003/04/14 21:28:30  oes
137  *    Completing the previous change
138  *
139  *    Revision 1.56.2.5  2003/04/14 12:08:16  oes
140  *    Added temporary workaround for bug in PHP < 4.2.3
141  *
142  *    Revision 1.56.2.4  2003/03/07 03:41:05  david__schmidt
143  *    Wrapping all *_r functions (the non-_r versions of them) with mutex semaphores for OSX.  Hopefully this will take care of all of those pesky crash reports.
144  *
145  *    Revision 1.56.2.3  2002/11/10 04:20:02  hal9
146  *    Fix typo: supressed -> suppressed
147  *
148  *    Revision 1.56.2.2  2002/09/25 14:59:53  oes
149  *    Improved cookie logging
150  *
151  *    Revision 1.56.2.1  2002/09/25 14:52:45  oes
152  *    Added basic support for OPTIONS and TRACE HTTP methods:
153  *     - New parser function client_max_forwards which decrements
154  *       the Max-Forwards HTTP header field of OPTIONS and TRACE
155  *       requests by one before forwarding
156  *     - New parser function client_host which extracts the host
157  *       and port information from the HTTP header field if the
158  *       request URI was not absolute
159  *     - Don't crumble and re-add the Host: header, but only generate
160  *       and append if missing
161  *
162  *    Revision 1.56  2002/05/12 15:34:22  jongfoster
163  *    Fixing typo in a comment
164  *
165  *    Revision 1.55  2002/05/08 16:01:07  oes
166  *    Optimized add_to_iob:
167  *     - Use realloc instead of malloc(), memcpy(), free()
168  *     - Expand to powers of two if possible, to get
169  *       O(log n) reallocs instead of O(n).
170  *     - Moved check for buffer limit here from chat
171  *     - Report failure via returncode
172  *
173  *    Revision 1.54  2002/04/02 15:03:16  oes
174  *    Tiny code cosmetics
175  *
176  *    Revision 1.53  2002/03/26 22:29:55  swa
177  *    we have a new homepage!
178  *
179  *    Revision 1.52  2002/03/24 13:25:43  swa
180  *    name change related issues
181  *
182  *    Revision 1.51  2002/03/13 00:27:05  jongfoster
183  *    Killing warnings
184  *
185  *    Revision 1.50  2002/03/12 01:45:35  oes
186  *    More verbose logging
187  *
188  *    Revision 1.49  2002/03/09 20:03:52  jongfoster
189  *    - Making various functions return int rather than size_t.
190  *      (Undoing a recent change).  Since size_t is unsigned on
191  *      Windows, functions like read_socket that return -1 on
192  *      error cannot return a size_t.
193  *
194  *      THIS WAS A MAJOR BUG - it caused frequent, unpredictable
195  *      crashes, and also frequently caused JB to jump to 100%
196  *      CPU and stay there.  (Because it thought it had just
197  *      read ((unsigned)-1) == 4Gb of data...)
198  *
199  *    - The signature of write_socket has changed, it now simply
200  *      returns success=0/failure=nonzero.
201  *
202  *    - Trying to get rid of a few warnings --with-debug on
203  *      Windows, I've introduced a new type "jb_socket".  This is
204  *      used for the socket file descriptors.  On Windows, this
205  *      is SOCKET (a typedef for unsigned).  Everywhere else, it's
206  *      an int.  The error value can't be -1 any more, so it's
207  *      now JB_INVALID_SOCKET (which is -1 on UNIX, and in
208  *      Windows it maps to the #define INVALID_SOCKET.)
209  *
210  *    - The signature of bind_port has changed.
211  *
212  *    Revision 1.48  2002/03/07 03:46:53  oes
213  *    Fixed compiler warnings etc
214  *
215  *    Revision 1.47  2002/02/20 23:15:13  jongfoster
216  *    Parsing functions now handle out-of-memory gracefully by returning
217  *    an error code.
218  *
219  *    Revision 1.46  2002/01/17 21:03:47  jongfoster
220  *    Moving all our URL and URL pattern parsing code to urlmatch.c.
221  *
222  *    Revision 1.45  2002/01/09 14:33:03  oes
223  *    Added support for localtime_r.
224  *
225  *    Revision 1.44  2001/12/14 01:22:54  steudten
226  *    Remove 'user:pass@' from 'proto://user:pass@host' for the
227  *    new added header 'Host: ..'. (See Req ID 491818)
228  *
229  *    Revision 1.43  2001/11/23 00:26:38  jongfoster
230  *    Fixing two really stupid errors in my previous commit
231  *
232  *    Revision 1.42  2001/11/22 21:59:30  jongfoster
233  *    Adding code to handle +no-cookies-keep
234  *
235  *    Revision 1.41  2001/11/05 23:43:05  steudten
236  *    Add time+date to log files.
237  *
238  *    Revision 1.40  2001/10/26 20:13:09  jongfoster
239  *    ctype.h is needed in Windows, too.
240  *
241  *    Revision 1.39  2001/10/26 17:40:04  oes
242  *    Introduced get_header_value()
243  *    Removed http->user_agent, csp->referrer and csp->accept_types
244  *    Removed client_accept()
245  *
246  *    Revision 1.38  2001/10/25 03:40:48  david__schmidt
247  *    Change in porting tactics: OS/2's EMX porting layer doesn't allow multiple
248  *    threads to call select() simultaneously.  So, it's time to do a real, live,
249  *    native OS/2 port.  See defines for __EMX__ (the porting layer) vs. __OS2__
250  *    (native). Both versions will work, but using __OS2__ offers multi-threading.
251  *
252  *    Revision 1.37  2001/10/23 21:36:02  jongfoster
253  *    Documenting sed()'s error behaviou (doc change only)
254  *
255  *    Revision 1.36  2001/10/13 12:51:51  joergs
256  *    Removed client_host, (was only required for the old 2.0.2-11 http://noijb.
257  *    force-load), instead crumble Host: and add it (again) in client_host_adder
258  *    (in case we get a HTTP/1.0 request without Host: header and forward it to
259  *    a HTTP/1.1 server/proxy).
260  *
261  *    Revision 1.35  2001/10/09 22:39:21  jongfoster
262  *    assert.h is also required under Win32, so moving out of #ifndef _WIN32
263  *    block.
264  *
265  *    Revision 1.34  2001/10/07 18:50:55  oes
266  *    Added server_content_encoding, renamed server_transfer_encoding
267  *
268  *    Revision 1.33  2001/10/07 18:04:49  oes
269  *    Changed server_http11 to server_http and its pattern to "HTTP".
270  *      Additional functionality: it now saves the HTTP status into
271  *      csp->http->status and sets CT_TABOO for Status 206 (partial range)
272  *
273  *    Revision 1.32  2001/10/07 15:43:28  oes
274  *    Removed FEATURE_DENY_GZIP and replaced it with client_accept_encoding,
275  *       client_te and client_accept_encoding_adder, triggered by the new
276  *       +no-compression action. For HTTP/1.1 the Accept-Encoding header is
277  *       changed to allow only identity and chunked, and the TE header is
278  *       crunched. For HTTP/1.0, Accept-Encoding is crunched.
279  *
280  *    parse_http_request no longer does anything than parsing. The rewriting
281  *      of http->cmd and version mangling are gone. It now also recognizes
282  *      the put and delete methods and saves the url in http->url. Removed
283  *      unused variable.
284  *
285  *    renamed content_type and content_length to have the server_ prefix
286  *
287  *    server_content_type now only works if csp->content_type != CT_TABOO
288  *
289  *    added server_transfer_encoding, which
290  *      - Sets CT_TABOO to prohibit filtering if encoding compresses
291  *      - Raises the CSP_FLAG_CHUNKED flag if Encoding is "chunked"
292  *      - Change from "chunked" to "identity" if body was chunked
293  *        but has been de-chunked for filtering.
294  *
295  *    added server_content_md5 which crunches any Content-MD5 headers
296  *      if the body was modified.
297  *
298  *    made server_http11 conditional on +downgrade action
299  *
300  *    Replaced 6 boolean members of csp with one bitmap (csp->flags)
301  *
302  *    Revision 1.31  2001/10/05 14:25:02  oes
303  *    Crumble Keep-Alive from Server
304  *
305  *    Revision 1.30  2001/09/29 12:56:03  joergs
306  *    IJB now changes HTTP/1.1 to HTTP/1.0 in requests and answers.
307  *
308  *    Revision 1.29  2001/09/24 21:09:24  jongfoster
309  *    Fixing 2 memory leaks that Guy spotted, where the paramater to
310  *    enlist() was not being free()d.
311  *
312  *    Revision 1.28  2001/09/22 16:32:28  jongfoster
313  *    Removing unused #includes.
314  *
315  *    Revision 1.27  2001/09/20 15:45:25  steudten
316  *
317  *    add casting from size_t to int for printf()
318  *    remove local variable shadow s2
319  *
320  *    Revision 1.26  2001/09/16 17:05:14  jongfoster
321  *    Removing unused #include showarg.h
322  *
323  *    Revision 1.25  2001/09/16 13:21:27  jongfoster
324  *    Changes to use new list functions.
325  *
326  *    Revision 1.24  2001/09/13 23:05:50  jongfoster
327  *    Changing the string paramater to the header parsers a "const".
328  *
329  *    Revision 1.23  2001/09/12 18:08:19  steudten
330  *
331  *    In parse_http_request() header rewriting miss the host value, so
332  *    from http://www.mydomain.com the result was just " / " not
333  *    http://www.mydomain.com/ in case we forward.
334  *
335  *    Revision 1.22  2001/09/10 10:58:53  oes
336  *    Silenced compiler warnings
337  *
338  *    Revision 1.21  2001/07/31 14:46:00  oes
339  *     - Persistant connections now suppressed
340  *     - sed() no longer appends empty header to csp->headers
341  *
342  *    Revision 1.20  2001/07/30 22:08:36  jongfoster
343  *    Tidying up #defines:
344  *    - All feature #defines are now of the form FEATURE_xxx
345  *    - Permanently turned off WIN_GUI_EDIT
346  *    - Permanently turned on WEBDAV and SPLIT_PROXY_ARGS
347  *
348  *    Revision 1.19  2001/07/25 17:21:54  oes
349  *    client_uagent now saves copy of User-Agent: header value
350  *
351  *    Revision 1.18  2001/07/13 14:02:46  oes
352  *     - Included fix to repair broken HTTP requests that
353  *       don't contain a path, not even '/'.
354  *     - Removed all #ifdef PCRS
355  *     - content_type now always inspected and classified as
356  *       text, gif or other.
357  *     - formatting / comments
358  *
359  *    Revision 1.17  2001/06/29 21:45:41  oes
360  *    Indentation, CRLF->LF, Tab-> Space
361  *
362  *    Revision 1.16  2001/06/29 13:32:42  oes
363  *    - Fixed a comment
364  *    - Adapted free_http_request
365  *    - Removed logentry from cancelled commit
366  *
367  *    Revision 1.15  2001/06/03 19:12:38  oes
368  *    deleted const struct interceptors
369  *
370  *    Revision 1.14  2001/06/01 18:49:17  jongfoster
371  *    Replaced "list_share" with "list" - the tiny memory gain was not
372  *    worth the extra complexity.
373  *
374  *    Revision 1.13  2001/05/31 21:30:33  jongfoster
375  *    Removed list code - it's now in list.[ch]
376  *    Renamed "permission" to "action", and changed many features
377  *    to use the actions file rather than the global config.
378  *
379  *    Revision 1.12  2001/05/31 17:33:13  oes
380  *
381  *    CRLF -> LF
382  *
383  *    Revision 1.11  2001/05/29 20:11:19  joergs
384  *    '/ * inside comment' warning removed.
385  *
386  *    Revision 1.10  2001/05/29 09:50:24  jongfoster
387  *    Unified blocklist/imagelist/permissionslist.
388  *    File format is still under discussion, but the internal changes
389  *    are (mostly) done.
390  *
391  *    Also modified interceptor behaviour:
392  *    - We now intercept all URLs beginning with one of the following
393  *      prefixes (and *only* these prefixes):
394  *        * http://i.j.b/
395  *        * http://ijbswa.sf.net/config/
396  *        * http://ijbswa.sourceforge.net/config/
397  *    - New interceptors "home page" - go to http://i.j.b/ to see it.
398  *    - Internal changes so that intercepted and fast redirect pages
399  *      are not replaced with an image.
400  *    - Interceptors now have the option to send a binary page direct
401  *      to the client. (i.e. ijb-send-banner uses this)
402  *    - Implemented show-url-info interceptor.  (Which is why I needed
403  *      the above interceptors changes - a typical URL is
404  *      "http://i.j.b/show-url-info?url=www.somesite.com/banner.gif".
405  *      The previous mechanism would not have intercepted that, and
406  *      if it had been intercepted then it then it would have replaced
407  *      it with an image.)
408  *
409  *    Revision 1.9  2001/05/28 17:26:33  jongfoster
410  *    Fixing segfault if last header was crunched.
411  *    Fixing Windows build (snprintf() is _snprintf() under Win32, but we
412  *    can use the cross-platform sprintf() instead.)
413  *
414  *    Revision 1.8  2001/05/27 22:17:04  oes
415  *
416  *    - re_process_buffer no longer writes the modified buffer
417  *      to the client, which was very ugly. It now returns the
418  *      buffer, which it is then written by chat.
419  *
420  *    - content_length now adjusts the Content-Length: header
421  *      for modified documents rather than crunch()ing it.
422  *      (Length info in csp->content_length, which is 0 for
423  *      unmodified documents)
424  *
425  *    - For this to work, sed() is called twice when filtering.
426  *
427  *    Revision 1.7  2001/05/27 13:19:06  oes
428  *    Patched Joergs solution for the content-length in.
429  *
430  *    Revision 1.6  2001/05/26 13:39:32  jongfoster
431  *    Only crunches Content-Length header if applying RE filtering.
432  *    Without this fix, Microsoft Windows Update wouldn't work.
433  *
434  *    Revision 1.5  2001/05/26 00:28:36  jongfoster
435  *    Automatic reloading of config file.
436  *    Removed obsolete SIGHUP support (Unix) and Reload menu option (Win32).
437  *    Most of the global variables have been moved to a new
438  *    struct configuration_spec, accessed through csp->config->globalname
439  *    Most of the globals remaining are used by the Win32 GUI.
440  *
441  *    Revision 1.4  2001/05/22 18:46:04  oes
442  *
443  *    - Enabled filtering banners by size rather than URL
444  *      by adding patterns that replace all standard banner
445  *      sizes with the "Junkbuster" gif to the re_filterfile
446  *
447  *    - Enabled filtering WebBugs by providing a pattern
448  *      which kills all 1x1 images
449  *
450  *    - Added support for PCRE_UNGREEDY behaviour to pcrs,
451  *      which is selected by the (nonstandard and therefore
452  *      capital) letter 'U' in the option string.
453  *      It causes the quantifiers to be ungreedy by default.
454  *      Appending a ? turns back to greedy (!).
455  *
456  *    - Added a new interceptor ijb-send-banner, which
457  *      sends back the "Junkbuster" gif. Without imagelist or
458  *      MSIE detection support, or if tinygif = 1, or the
459  *      URL isn't recognized as an imageurl, a lame HTML
460  *      explanation is sent instead.
461  *
462  *    - Added new feature, which permits blocking remote
463  *      script redirects and firing back a local redirect
464  *      to the browser.
465  *      The feature is conditionally compiled, i.e. it
466  *      can be disabled with --disable-fast-redirects,
467  *      plus it must be activated by a "fast-redirects"
468  *      line in the config file, has its own log level
469  *      and of course wants to be displayed by show-proxy-args
470  *      Note: Boy, all the #ifdefs in 1001 locations and
471  *      all the fumbling with configure.in and acconfig.h
472  *      were *way* more work than the feature itself :-(
473  *
474  *    - Because a generic redirect template was needed for
475  *      this, tinygif = 3 now uses the same.
476  *
477  *    - Moved GIFs, and other static HTTP response templates
478  *      to project.h
479  *
480  *    - Some minor fixes
481  *
482  *    - Removed some >400 CRs again (Jon, you really worked
483  *      a lot! ;-)
484  *
485  *    Revision 1.3  2001/05/20 01:21:20  jongfoster
486  *    Version 2.9.4 checkin.
487  *    - Merged popupfile and cookiefile, and added control over PCRS
488  *      filtering, in new "permissionsfile".
489  *    - Implemented LOG_LEVEL_FATAL, so that if there is a configuration
490  *      file error you now get a message box (in the Win32 GUI) rather
491  *      than the program exiting with no explanation.
492  *    - Made killpopup use the PCRS MIME-type checking and HTTP-header
493  *      skipping.
494  *    - Removed tabs from "config"
495  *    - Moved duplicated url parsing code in "loaders.c" to a new funcition.
496  *    - Bumped up version number.
497  *
498  *    Revision 1.2  2001/05/17 23:02:36  oes
499  *     - Made referrer option accept 'L' as a substitute for '§'
500  *
501  *    Revision 1.1.1.1  2001/05/15 13:59:01  oes
502  *    Initial import of version 2.9.3 source tree
503  *
504  *
505  *********************************************************************/
506 \f
507
508 #include "config.h"
509
510 #ifndef _WIN32
511 #include <stdio.h>
512 #include <sys/types.h>
513 #endif
514
515 #include <stdlib.h>
516 #include <ctype.h>
517 #include <assert.h>
518 #include <string.h>
519 #include <time.h>
520
521 #if !defined(_WIN32) && !defined(__OS2__)
522 #include <unistd.h>
523 #endif
524
525 #include "project.h"
526
527 #ifdef OSX_DARWIN
528 #include <pthread.h>
529 #include "jcc.h"
530 /* jcc.h is for mutex semapores only */
531 #endif /* def OSX_DARWIN */
532 #include "list.h"
533 #include "parsers.h"
534 #include "encode.h"
535 #include "ssplit.h"
536 #include "errlog.h"
537 #include "jbsockets.h"
538 #include "miscutil.h"
539 #include "list.h"
540
541 #ifndef HAVE_STRPTIME
542 #include "strptime.h"
543 #endif
544
545 const char parsers_h_rcs[] = PARSERS_H_VERSION;
546
547 /* Fix a problem with Solaris.  There should be no effect on other
548  * platforms.
549  * Solaris's isspace() is a macro which uses it's argument directly
550  * as an array index.  Therefore we need to make sure that high-bit
551  * characters generate +ve values, and ideally we also want to make
552  * the argument match the declared parameter type of "int".
553  *
554  * Why did they write a character function that can't take a simple
555  * "char" argument?  Doh!
556  */
557 #define ijb_isupper(__X) isupper((int)(unsigned char)(__X))
558 #define ijb_tolower(__X) tolower((int)(unsigned char)(__X))
559
560
561 const struct parsers client_patterns[] = {
562    { "referer:",                  8,   client_referrer },
563    { "user-agent:",              11,   client_uagent },
564    { "ua-",                       3,   client_ua },
565    { "from:",                     5,   client_from },
566    { "cookie:",                   7,   client_send_cookie },
567    { "x-forwarded-for:",         16,   client_x_forwarded },
568    { "Accept-Encoding:",         16,   client_accept_encoding },
569    { "TE:",                       3,   client_te },
570    { "Host:",                     5,   client_host },
571    { "if-modified-since:",       18,   client_if_modified_since },
572    { "Keep-Alive:",              11,   crumble },
573    { "connection:",              11,   crumble },
574    { "proxy-connection:",        17,   crumble },
575    { "max-forwards:",            13,   client_max_forwards },
576    { "Accept-Language:",         16,   client_accept_language },
577    { "if-none-match:",           14,   client_if_none_match },
578    { "X-Filter:",                 9,   client_x_filter },
579    { "*",                         0,   crunch_client_header },
580    { "*",                         0,   filter_client_header },
581    { NULL,                        0,   NULL }
582 };
583
584 const struct parsers server_patterns[] = {
585    { "HTTP",                      4, server_http },
586    { "set-cookie:",              11, server_set_cookie },
587    { "connection:",              11, crumble },
588    { "Content-Type:",            13, server_content_type },
589    { "Content-Length:",          15, server_content_length },
590    { "Content-MD5:",             12, server_content_md5 },
591    { "Content-Encoding:",        17, server_content_encoding },
592    { "Transfer-Encoding:",       18, server_transfer_coding },
593    { "Keep-Alive:",              11, crumble },
594    { "content-disposition:",     20, server_content_disposition },
595    { "Last-Modified:",           14, server_last_modified },
596    { "*",                         0, crunch_server_header },
597    { "*",                         0, filter_server_header },
598    { NULL, 0, NULL }
599 };
600
601 const struct parsers server_patterns_light[] = {
602    { "Content-Length:",          15, server_content_length },
603    { "Transfer-Encoding:",       18, server_transfer_coding },
604    { NULL, 0, NULL }
605 };
606
607 const add_header_func_ptr add_client_headers[] = {
608    client_host_adder,
609    client_cookie_adder,
610    client_x_forwarded_adder,
611    client_xtra_adder,
612    /* Temporarily disabled:    client_accept_encoding_adder, */
613    connection_close_adder,
614    NULL
615 };
616
617
618 const add_header_func_ptr add_server_headers[] = {
619    connection_close_adder,
620    NULL
621 };
622
623 /*********************************************************************
624  *
625  * Function    :  flush_socket
626  *
627  * Description :  Write any pending "buffered" content.
628  *
629  * Parameters  :
630  *          1  :  fd = file descriptor of the socket to read
631  *          2  :  csp = Current client state (buffers, headers, etc...)
632  *
633  * Returns     :  On success, the number of bytes written are returned (zero
634  *                indicates nothing was written).  On error, -1 is returned,
635  *                and errno is set appropriately.  If count is zero and the
636  *                file descriptor refers to a regular file, 0 will be
637  *                returned without causing any other effect.  For a special
638  *                file, the results are not portable.
639  *
640  *********************************************************************/
641 int flush_socket(jb_socket fd, struct client_state *csp)
642 {
643    struct iob *iob = csp->iob;
644    int len = iob->eod - iob->cur;
645
646    if (len <= 0)
647    {
648       return(0);
649    }
650
651    if (write_socket(fd, iob->cur, (size_t)len))
652    {
653       return(-1);
654    }
655    iob->eod = iob->cur = iob->buf;
656    return(len);
657
658 }
659
660
661 /*********************************************************************
662  *
663  * Function    :  add_to_iob
664  *
665  * Description :  Add content to the buffered page, expanding the
666  *                buffer if necessary.
667  *
668  * Parameters  :
669  *          1  :  csp = Current client state (buffers, headers, etc...)
670  *          2  :  buf = holds the content to be added to the page
671  *          3  :  n = number of bytes to be added
672  *
673  * Returns     :  JB_ERR_OK on success, JB_ERR_MEMORY if out-of-memory
674  *                or buffer limit reached.
675  *
676  *********************************************************************/
677 jb_err add_to_iob(struct client_state *csp, char *buf, int n)
678 {
679    struct iob *iob = csp->iob;
680    size_t used, offset, need, want;
681    char *p;
682
683    if (n <= 0) return JB_ERR_OK;
684
685    used   = iob->eod - iob->buf;
686    offset = iob->cur - iob->buf;
687    need   = used + n + 1;
688
689    /*
690     * If the buffer can't hold the new data, extend it first.
691     * Use the next power of two if possible, else use the actual need.
692     */
693    if (need > csp->config->buffer_limit)
694    {
695       log_error(LOG_LEVEL_ERROR, "Buffer limit reached while extending the buffer (iob)");
696       return JB_ERR_MEMORY;
697    }
698
699    if (need > iob->size)
700    {
701       for (want = csp->iob->size ? csp->iob->size : 512; want <= need;) want *= 2;
702       
703       if (want <= csp->config->buffer_limit && NULL != (p = (char *)realloc(iob->buf, want)))
704       {
705          iob->size = want;
706       }
707       else if (NULL != (p = (char *)realloc(iob->buf, need)))
708       {
709          iob->size = need;
710       }
711       else
712       {
713          log_error(LOG_LEVEL_ERROR, "Extending the buffer (iob) failed: %E");
714          return JB_ERR_MEMORY;
715       }
716
717       /* Update the iob pointers */
718       iob->cur = p + offset;
719       iob->eod = p + used;
720       iob->buf = p;
721    }
722
723    /* copy the new data into the iob buffer */
724    memcpy(iob->eod, buf, (size_t)n);
725
726    /* point to the end of the data */
727    iob->eod += n;
728
729    /* null terminate == cheap insurance */
730    *iob->eod = '\0';
731
732    return JB_ERR_OK;
733
734 }
735
736
737 /*********************************************************************
738  *
739  * Function    :  get_header
740  *
741  * Description :  This (odd) routine will parse the csp->iob
742  *
743  * Parameters  :
744  *          1  :  csp = Current client state (buffers, headers, etc...)
745  *
746  * Returns     :  Any one of the following:
747  *
748  * 1) a pointer to a dynamically allocated string that contains a header line
749  * 2) NULL  indicating that the end of the header was reached
750  * 3) ""    indicating that the end of the iob was reached before finding
751  *          a complete header line.
752  *
753  *********************************************************************/
754 char *get_header(struct client_state *csp)
755 {
756    struct iob *iob;
757    char *p, *q, *ret;
758    iob = csp->iob;
759
760    if ((iob->cur == NULL)
761       || ((p = strchr(iob->cur, '\n')) == NULL))
762    {
763       return(""); /* couldn't find a complete header */
764    }
765
766    *p = '\0';
767
768    ret = strdup(iob->cur);
769    if (ret == NULL)
770    {
771       /* FIXME No way to handle error properly */
772       log_error(LOG_LEVEL_FATAL, "Out of memory in get_header()");
773    }
774
775    iob->cur = p+1;
776
777    if ((q = strchr(ret, '\r')) != NULL) *q = '\0';
778
779    /* is this a blank line (i.e. the end of the header) ? */
780    if (*ret == '\0')
781    {
782       freez(ret);
783       return(NULL);
784    }
785
786    return(ret);
787
788 }
789
790
791 /*********************************************************************
792  *
793  * Function    :  get_header_value
794  *
795  * Description :  Get the value of a given header from a chained list
796  *                of header lines or return NULL if no such header is
797  *                present in the list.
798  *
799  * Parameters  :
800  *          1  :  header_list = pointer to list
801  *          2  :  header_name = string with name of header to look for.
802  *                              Trailing colon required, capitalization
803  *                              doesn't matter.
804  *
805  * Returns     :  NULL if not found, else value of header
806  *
807  *********************************************************************/
808 char *get_header_value(const struct list *header_list, const char *header_name)
809 {
810    struct list_entry *cur_entry;
811    char *ret = NULL;
812    size_t length = 0;
813
814    assert(header_list);
815    assert(header_name);
816    length = strlen(header_name);
817
818    for (cur_entry = header_list->first; cur_entry ; cur_entry = cur_entry->next)
819    {
820       if (cur_entry->str)
821       {
822          if (!strncmpic(cur_entry->str, header_name, length))
823          {
824             /*
825              * Found: return pointer to start of value
826              */
827             ret = (char *) (cur_entry->str + length);
828             while (*ret && ijb_isspace(*ret)) ret++;
829             return(ret);
830          }
831       }
832    }
833
834    /* 
835     * Not found
836     */
837    return NULL;
838
839 }
840
841 /*********************************************************************
842  *
843  * Function    :  sed
844  *
845  * Description :  add, delete or modify lines in the HTTP header streams.
846  *                On entry, it receives a linked list of headers space
847  *                that was allocated dynamically (both the list nodes
848  *                and the header contents).
849  *
850  *                As a side effect it frees the space used by the original
851  *                header lines.
852  *
853  * Parameters  :
854  *          1  :  pats = list of patterns to match against headers
855  *          2  :  more_headers = list of functions to add more
856  *                headers (client or server)
857  *          3  :  csp = Current client state (buffers, headers, etc...)
858  *
859  * Returns     :  Single pointer to a fully formed header, or NULL
860  *                on out-of-memory error.
861  *
862  *********************************************************************/
863 char *sed(const struct parsers pats[],
864           const add_header_func_ptr more_headers[],
865           struct client_state *csp)
866 {
867    struct list_entry *p;
868    const struct parsers *v;
869    const add_header_func_ptr *f;
870    jb_err err = JB_ERR_OK;
871    int first_run;
872
873    /*
874     * If filtering is enabled, sed is run twice,
875     * but most of the work needs to be done only once.
876     */
877    first_run = (more_headers != NULL ) ? 1 : 0;
878
879    if (first_run) /* Parse and print */
880    {
881       for (v = pats; (err == JB_ERR_OK) && (v->str != NULL) ; v++)
882       {
883          for (p = csp->headers->first; (err == JB_ERR_OK) && (p != NULL) ; p = p->next)
884          {
885             /* Header crunch()ed in previous run? -> ignore */
886             if (p->str == NULL) continue;
887
888             if (v == pats) log_error(LOG_LEVEL_HEADER, "scan: %s", p->str);
889
890             /* Does the current parser handle this header? */
891             if ((strncmpic(p->str, v->str, v->len) == 0) || (v->len == CHECK_EVERY_HEADER_REMAINING))
892             {
893                err = v->parser(csp, (char **)&(p->str));
894             }
895          }
896       }
897       /* place any additional headers on the csp->headers list */
898       for (f = more_headers; (err == JB_ERR_OK) && (*f) ; f++)
899       {
900          err = (*f)(csp);
901       }
902    }
903    else /* Parse only */
904    {
905       /*
906        * The second run is only needed if the body was modified
907        * and the content-lenght has changed.
908        */
909       if (strncmpic(csp->http->cmd, "HEAD", 4))
910       {
911          /*XXX: Code duplication*/
912          for (v = pats; (err == JB_ERR_OK) && (v->str != NULL) ; v++)
913          {
914             for (p = csp->headers->first; (err == JB_ERR_OK) && (p != NULL) ; p = p->next)
915             {
916                /* Header crunch()ed in previous run? -> ignore */
917                if (p->str == NULL) continue;
918
919                /* Does the current parser handle this header? */
920                if (strncmpic(p->str, v->str, v->len) == 0)
921                {
922                   err = v->parser(csp, (char **)&(p->str));
923                }
924             }
925          }
926       }
927    }
928
929    if (err != JB_ERR_OK)
930    {
931       return NULL;
932    }
933
934    return list_to_text(csp->headers);
935 }
936
937
938 /* here begins the family of parser functions that reformat header lines */
939
940 /*********************************************************************
941  *
942  * Function    :  filter_server_header
943  *
944  * Description :  Checks if server header filtering is enabled.
945  *                If it is, filter_header is called to do the work. 
946  *
947  * Parameters  :
948  *          1  :  csp = Current client state (buffers, headers, etc...)
949  *          2  :  header = On input, pointer to header to modify.
950  *                On output, pointer to the modified header, or NULL
951  *                to remove the header.  This function frees the
952  *                original string if necessary.
953  *
954  * Returns     :  JB_ERR_OK on success and always succeeds
955  *
956  *********************************************************************/
957 jb_err filter_server_header(struct client_state *csp, char **header)
958 {
959    if (csp->action->flags & ACTION_FILTER_SERVER_HEADERS)
960    {
961       filter_header(csp, header);
962    }
963    return(JB_ERR_OK);
964 }
965
966 /*********************************************************************
967  *
968  * Function    :  filter_client_header
969  *
970  * Description :  Checks if client header filtering is enabled.
971  *                If it is, filter_header is called to do the work. 
972  *
973  * Parameters  :
974  *          1  :  csp = Current client state (buffers, headers, etc...)
975  *          2  :  header = On input, pointer to header to modify.
976  *                On output, pointer to the modified header, or NULL
977  *                to remove the header.  This function frees the
978  *                original string if necessary.
979  *
980  * Returns     :  JB_ERR_OK on success and always succeeds
981  *
982  *********************************************************************/
983 jb_err filter_client_header(struct client_state *csp, char **header)
984 {
985    if (csp->action->flags & ACTION_FILTER_CLIENT_HEADERS)
986    {
987       filter_header(csp, header);
988    }
989    return(JB_ERR_OK);
990 }
991
992 /*********************************************************************
993  *
994  * Function    :  filter_header
995  *
996  * Description :  Executes all text substitutions from all applying
997  *                +filter actions on the header.
998  *                Most of the code was copied from pcrs_filter_response,
999  *                including the rather short variable names
1000  *
1001  * Parameters  :
1002  *          1  :  csp = Current client state (buffers, headers, etc...)
1003  *          2  :  header = On input, pointer to header to modify.
1004  *                On output, pointer to the modified header, or NULL
1005  *                to remove the header.  This function frees the
1006  *                original string if necessary.
1007  *
1008  * Returns     :  JB_ERR_OK on success and always succeeds
1009  *
1010  *********************************************************************/
1011 jb_err filter_header(struct client_state *csp, char **header)
1012 {
1013    int hits=0;
1014    int matches;
1015    size_t size = strlen(*header);
1016
1017    char *newheader = NULL;
1018    pcrs_job *job;
1019
1020    struct file_list *fl;
1021    struct re_filterfile_spec *b;
1022    struct list_entry *filtername;
1023
1024    int i, found_filters = 0;
1025
1026    log_error(LOG_LEVEL_RE_FILTER, "Entered filter_headers");
1027    /*
1028     * Need to check the set of re_filterfiles...
1029     */
1030    for (i = 0; i < MAX_AF_FILES; i++)
1031    {
1032       fl = csp->rlist[i];
1033       if (NULL != fl)
1034       {
1035          if (NULL != fl->f)
1036          {
1037            found_filters = 1;
1038            break;
1039          }
1040       }
1041    }
1042
1043    if (0 == found_filters)
1044    {
1045       log_error(LOG_LEVEL_ERROR, "Unable to get current state of regexp filtering.");
1046          return(JB_ERR_OK);
1047    }
1048
1049    for (i = 0; i < MAX_AF_FILES; i++)
1050    {
1051       fl = csp->rlist[i];
1052       if ((NULL == fl) || (NULL == fl->f))
1053          break;
1054       /*
1055        * For all applying +filter actions, look if a filter by that
1056        * name exists and if yes, execute its pcrs_joblist on the
1057        * buffer.
1058        */
1059       for (b = fl->f; b; b = b->next)
1060       {
1061          for (filtername = csp->action->multi[ACTION_MULTI_FILTER]->first;
1062               filtername ; filtername = filtername->next)
1063          {
1064             if (strcmp(b->name, filtername->str) == 0)
1065             {
1066                int current_hits = 0;
1067
1068                if ( NULL == b->joblist )
1069                {
1070                   log_error(LOG_LEVEL_RE_FILTER, "Filter %s has empty joblist. Nothing to do.", b->name);
1071                   continue;
1072                }
1073
1074                log_error(LOG_LEVEL_RE_FILTER, "re_filtering %s (size %d) with filter %s...",
1075                          *header, size, b->name);
1076
1077                /* Apply all jobs from the joblist */
1078                for (job = b->joblist; NULL != job; job = job->next)
1079                {
1080                   matches = pcrs_execute(job, *header, size, &newheader, &size);
1081                   if ( 0 < matches )
1082                   {
1083                      current_hits += matches; 
1084                      log_error(LOG_LEVEL_HEADER, "Transforming \"%s\" to \"%s\"", *header, newheader);
1085                      freez(*header);
1086                      *header = newheader;
1087                   }
1088                   else if ( 0 == matches )
1089                   {
1090                      /* Filter doesn't change header */
1091                      freez(newheader);
1092                   }
1093                   else
1094                   {
1095                      /* RegEx failure */
1096                      log_error(LOG_LEVEL_ERROR, "Filtering \'%s\' with \'%s\' didn't work out: %s",
1097                         *header, b->name, pcrs_strerror(matches));
1098                      if( newheader != NULL)
1099                      {
1100                         log_error(LOG_LEVEL_ERROR, "Freeing what's left: %s", newheader);
1101                         freez(newheader);
1102                      }
1103                   }
1104                }
1105                log_error(LOG_LEVEL_RE_FILTER, " ...produced %d hits (new size %d).", current_hits, size);
1106                hits += current_hits;
1107             }
1108          }
1109       }
1110    }
1111
1112    if ( 0 == size )
1113    {
1114            log_error(LOG_LEVEL_HEADER, "Removing empty header %s", *header);
1115       freez(*header);
1116    }
1117    log_error(LOG_LEVEL_RE_FILTER, "Leaving filter headers");
1118    return(JB_ERR_OK);
1119
1120 }
1121
1122
1123 /*********************************************************************
1124  *
1125  * Function    :  crumble
1126  *
1127  * Description :  This is called if a header matches a pattern to "crunch"
1128  *
1129  * Parameters  :
1130  *          1  :  csp = Current client state (buffers, headers, etc...)
1131  *          2  :  header = On input, pointer to header to modify.
1132  *                On output, pointer to the modified header, or NULL
1133  *                to remove the header.  This function frees the
1134  *                original string if necessary.
1135  *
1136  * Returns     :  JB_ERR_OK on success, or
1137  *                JB_ERR_MEMORY on out-of-memory error.
1138  *
1139  *********************************************************************/
1140 jb_err crumble(struct client_state *csp, char **header)
1141 {
1142    log_error(LOG_LEVEL_HEADER, "crumble crunched: %s!", *header);
1143    freez(*header);
1144    return JB_ERR_OK;
1145 }
1146
1147 /*********************************************************************
1148  *
1149  * Function    :  crunch_server_header
1150  *
1151  * Description :  Crunch server header if it matches a string supplied by the
1152  *                user. Called from `sed'.
1153  *
1154  * Parameters  :
1155  *          1  :  csp = Current client state (buffers, headers, etc...)
1156  *          2  :  header = On input, pointer to header to modify.
1157  *                On output, pointer to the modified header, or NULL
1158  *                to remove the header.  This function frees the
1159  *                original string if necessary.
1160  *
1161  * Returns     :  JB_ERR_OK on success and always succeeds
1162  *
1163  *********************************************************************/
1164 jb_err crunch_server_header(struct client_state *csp, char **header)
1165 {
1166    const char *crunch_pattern;
1167    /*Is there a header to crunch*/
1168
1169    if ((csp->action->flags & ACTION_CRUNCH_SERVER_HEADER))
1170    {
1171       crunch_pattern = csp->action->string[ACTION_STRING_SERVER_HEADER];
1172
1173       /*Is the current header the lucky one?*/
1174       if (strstr(*header, crunch_pattern))
1175       {
1176          log_error(LOG_LEVEL_HEADER, "Crunching server header: %s (contains: %s)", *header, crunch_pattern);  
1177          freez(*header);
1178       }
1179    }
1180
1181    return JB_ERR_OK;
1182 }
1183 /*********************************************************************
1184  *
1185  * Function    :  server_content_type
1186  *
1187  * Description :  Set the content-type for filterable types (text/.*,
1188  *                javascript and image/gif) unless filtering has been
1189  *                forbidden (CT_TABOO) while parsing earlier headers.
1190  *                NOTE: Since text/plain is commonly used by web servers
1191  *                      for files whose correct type is unknown, we don't
1192  *                      set CT_TEXT for it.
1193  *
1194  * Parameters  :
1195  *          1  :  csp = Current client state (buffers, headers, etc...)
1196  *          2  :  header = On input, pointer to header to modify.
1197  *                On output, pointer to the modified header, or NULL
1198  *                to remove the header.  This function frees the
1199  *                original string if necessary.
1200  *
1201  * Returns     :  JB_ERR_OK on success, or
1202  *                JB_ERR_MEMORY on out-of-memory error.
1203  *
1204  *********************************************************************/
1205 jb_err server_content_type(struct client_state *csp, char **header)
1206 {
1207    const char *newval;
1208    
1209    newval = csp->action->string[ACTION_STRING_CONTENT_TYPE]; 
1210
1211    if (csp->content_type != CT_TABOO)
1212    {
1213       if ((strstr(*header, " text/") && !strstr(*header, "plain"))
1214        || strstr(*header, "xml")
1215        || strstr(*header, "application/x-javascript"))
1216          csp->content_type = CT_TEXT;
1217       else if (strstr(*header, " image/gif"))
1218          csp->content_type = CT_GIF;
1219       else if (strstr(*header, " image/jpeg"))
1220          csp->content_type = CT_JPEG;
1221       else
1222          csp->content_type = 0;
1223    }
1224    /*
1225     * Are we enabling text mode by force?
1226     */
1227    if (csp->action->flags & ACTION_FORCE_TEXT_MODE)
1228    {
1229      /*
1230       * Do we really have to?
1231       */
1232       if (csp->content_type == CT_TEXT)
1233       {
1234          log_error(LOG_LEVEL_HEADER, "Text mode is already enabled.");   
1235       }
1236       else
1237       {
1238          csp->content_type = CT_TEXT;
1239          log_error(LOG_LEVEL_HEADER, "Text mode enabled by force. Take cover!");   
1240       }
1241    }
1242    /*
1243     * Are we messing with the content type?
1244     */ 
1245    if (csp->action->flags & ACTION_CONTENT_TYPE_OVERWRITE)
1246    { 
1247      /*
1248       * Make sure the user doesn't accidently
1249       * change the content type of binary documents. 
1250       */ 
1251      if (csp->content_type == CT_TEXT)
1252      { 
1253         freez(*header);
1254         *header = strdup("Content-Type: ");
1255         string_append(header, newval);
1256         
1257         if (header == NULL)
1258         { 
1259            log_error(LOG_LEVEL_HEADER, "Insufficient memory. Conten-Type crunched without replacement!");
1260            return JB_ERR_MEMORY;
1261         }
1262         log_error(LOG_LEVEL_HEADER, "Modified: %s!", *header);
1263      }
1264      else
1265      {
1266         log_error(LOG_LEVEL_HEADER, "%s not replaced. It doesn't look like text. "
1267                  "Enable force-text-mode if you know what you're doing.", *header);   
1268      }
1269    }  
1270    return JB_ERR_OK;
1271 }
1272
1273
1274 /*********************************************************************
1275  *
1276  * Function    :  server_transfer_coding
1277  *
1278  * Description :  - Prohibit filtering (CT_TABOO) if transfer coding compresses
1279  *                - Raise the CSP_FLAG_CHUNKED flag if coding is "chunked"
1280  *                - Remove header if body was chunked but has been
1281  *                  de-chunked for filtering.
1282  *
1283  * Parameters  :
1284  *          1  :  csp = Current client state (buffers, headers, etc...)
1285  *          2  :  header = On input, pointer to header to modify.
1286  *                On output, pointer to the modified header, or NULL
1287  *                to remove the header.  This function frees the
1288  *                original string if necessary.
1289  *
1290  * Returns     :  JB_ERR_OK on success, or
1291  *                JB_ERR_MEMORY on out-of-memory error.
1292  *
1293  *********************************************************************/
1294 jb_err server_transfer_coding(struct client_state *csp, char **header)
1295 {
1296    /*
1297     * Turn off pcrs and gif filtering if body compressed
1298     */
1299    if (strstr(*header, "gzip") || strstr(*header, "compress") || strstr(*header, "deflate"))
1300    {
1301       csp->content_type = CT_TABOO;
1302    }
1303
1304    /*
1305     * Raise flag if body chunked
1306     */
1307    if (strstr(*header, "chunked"))
1308    {
1309       csp->flags |= CSP_FLAG_CHUNKED;
1310
1311       /*
1312        * If the body was modified, it has been de-chunked first
1313        * and the header must be removed. 
1314        */
1315       if (csp->flags & CSP_FLAG_MODIFIED)
1316       {
1317          log_error(LOG_LEVEL_HEADER, "Removing: %s", *header);
1318          freez(*header);
1319       }
1320    }
1321
1322    return JB_ERR_OK;
1323 }
1324
1325
1326 /*********************************************************************
1327  *
1328  * Function    :  server_content_encoding
1329  *
1330  * Description :  Prohibit filtering (CT_TABOO) if content encoding compresses
1331  *
1332  * Parameters  :
1333  *          1  :  csp = Current client state (buffers, headers, etc...)
1334  *          2  :  header = On input, pointer to header to modify.
1335  *                On output, pointer to the modified header, or NULL
1336  *                to remove the header.  This function frees the
1337  *                original string if necessary.
1338  *
1339  * Returns     :  JB_ERR_OK on success, or
1340  *                JB_ERR_MEMORY on out-of-memory error.
1341  *
1342  *********************************************************************/
1343 jb_err server_content_encoding(struct client_state *csp, char **header)
1344 {
1345    /*
1346     * Turn off pcrs and gif filtering if body compressed
1347     */
1348    if (strstr(*header, "gzip") || strstr(*header, "compress") || strstr(*header, "deflate"))
1349    {
1350       csp->content_type = CT_TABOO;
1351    }
1352
1353    return JB_ERR_OK;
1354
1355 }
1356
1357
1358 /*********************************************************************
1359  *
1360  * Function    :  server_content_length
1361  *
1362  * Description :  Adjust Content-Length header if we modified
1363  *                the body.
1364  *
1365  * Parameters  :
1366  *          1  :  csp = Current client state (buffers, headers, etc...)
1367  *          2  :  header = On input, pointer to header to modify.
1368  *                On output, pointer to the modified header, or NULL
1369  *                to remove the header.  This function frees the
1370  *                original string if necessary.
1371  *
1372  * Returns     :  JB_ERR_OK on success, or
1373  *                JB_ERR_MEMORY on out-of-memory error.
1374  *
1375  *********************************************************************/
1376 jb_err server_content_length(struct client_state *csp, char **header)
1377 {
1378    if (csp->content_length != 0) /* Content length could have been modified */
1379    {
1380       /*
1381        * XXX: Shouldn't we check if csp->content_length
1382        * is different than the original value?
1383        */
1384       freez(*header);
1385       *header = (char *) zalloc(100);
1386       if (*header == NULL)
1387       {
1388          return JB_ERR_MEMORY;
1389       }
1390
1391       sprintf(*header, "Content-Length: %d", (int) csp->content_length);
1392
1393       log_error(LOG_LEVEL_HEADER, "Adjust Content-Length to %d", (int) csp->content_length);
1394    }
1395
1396    return JB_ERR_OK;
1397 }
1398
1399
1400 /*********************************************************************
1401  *
1402  * Function    :  server_content_md5
1403  *
1404  * Description :  Crumble any Content-MD5 headers if the document was
1405  *                modified. FIXME: Should we re-compute instead?
1406  *
1407  * Parameters  :
1408  *          1  :  csp = Current client state (buffers, headers, etc...)
1409  *          2  :  header = On input, pointer to header to modify.
1410  *                On output, pointer to the modified header, or NULL
1411  *                to remove the header.  This function frees the
1412  *                original string if necessary.
1413  *
1414  * Returns     :  JB_ERR_OK on success, or
1415  *                JB_ERR_MEMORY on out-of-memory error.
1416  *
1417  *********************************************************************/
1418 jb_err server_content_md5(struct client_state *csp, char **header)
1419 {
1420    if (csp->flags & CSP_FLAG_MODIFIED)
1421    {
1422       log_error(LOG_LEVEL_HEADER, "Crunching Content-MD5");
1423       freez(*header);
1424    }
1425
1426    return JB_ERR_OK;
1427 }
1428
1429 /*********************************************************************
1430  *
1431  * Function    :  server_content_disposition
1432  *
1433  * Description :  If enabled, blocks or modifies the "content-disposition" header.
1434  *                Called from `sed'.
1435  *
1436  * Parameters  :
1437  *          1  :  csp = Current client state (buffers, headers, etc...)
1438  *          2  :  header = On input, pointer to header to modify.
1439  *                On output, pointer to the modified header, or NULL
1440  *                to remove the header.  This function frees the
1441  *                original string if necessary.
1442  *
1443  * Returns     :  JB_ERR_OK on success, or
1444  *                JB_ERR_MEMORY on out-of-memory error.
1445  *
1446  *********************************************************************/
1447 jb_err server_content_disposition(struct client_state *csp, char **header)
1448 {
1449    const char *newval;
1450
1451    /*
1452     * Are we messing with the content-disposition header?
1453     */
1454    if ((csp->action->flags & ACTION_HIDE_CONTENT_DISPOSITION) == 0)
1455    {
1456       /*Me tinks not*/
1457       return JB_ERR_OK;
1458    }
1459
1460    newval = csp->action->string[ACTION_STRING_CONTENT_DISPOSITION];
1461
1462    if ((newval == NULL) || (0 == strcmpic(newval, "block")) )
1463    {
1464       /*
1465        * Blocking content-disposition header
1466        */
1467       log_error(LOG_LEVEL_HEADER, "Crunching %s!", *header);
1468       freez(*header);
1469       return JB_ERR_OK;
1470    }
1471    else
1472    {  
1473       /*
1474        * Replacing content-disposition header
1475        */
1476       freez(*header);
1477       *header = strdup("content-disposition: ");
1478       string_append(header, newval);   
1479
1480       if (*header == NULL)
1481       {
1482          log_error(LOG_LEVEL_HEADER, "Insufficent memory. content-disposition header not fully replaced.");  
1483       }
1484       else
1485       {
1486          log_error(LOG_LEVEL_HEADER, "content-disposition header crunched and replaced with: %s", *header);
1487       }
1488    }
1489    return (*header == NULL) ? JB_ERR_MEMORY : JB_ERR_OK;
1490 }
1491
1492 /*********************************************************************
1493  *
1494  * Function    :  server_last_modified
1495  *
1496  * Description :  Changes Last-Modified header to the actual date
1497  *                to help hide-if-modified-since.
1498  *                Called from `sed'.
1499  *
1500  * Parameters  :
1501  *          1  :  csp = Current client state (buffers, headers, etc...)
1502  *          2  :  header = On input, pointer to header to modify.
1503  *                On output, pointer to the modified header, or NULL
1504  *                to remove the header.  This function frees the
1505  *                original string if necessary.
1506  *
1507  * Returns     :  JB_ERR_OK on success, or
1508  *                JB_ERR_MEMORY on out-of-memory error.
1509  *
1510  *********************************************************************/
1511 jb_err server_last_modified(struct client_state *csp, char **header)
1512 {
1513    const char *newval;
1514    char buf[BUFFER_SIZE];
1515
1516    char newheader[50];
1517 #ifdef HAVE_GMTIME_R
1518    struct tm gmt;
1519 #endif
1520    struct tm *timeptr = NULL;
1521    time_t now, last_modified;                  
1522    long int rtime;
1523    long int days, hours, minutes, seconds;
1524    
1525    /*
1526     * Are we messing with the Last-Modified header?
1527     */
1528    if ((csp->action->flags & ACTION_OVERWRITE_LAST_MODIFIED) == 0)
1529    {
1530       /*Nope*/
1531       return JB_ERR_OK;
1532    }
1533
1534    newval = csp->action->string[ACTION_STRING_LAST_MODIFIED];
1535
1536    if (0 == strcmpic(newval, "block") )
1537    {
1538       /*
1539        * Blocking Last-Modified header. Useless but why not.
1540        */
1541       log_error(LOG_LEVEL_HEADER, "Crunching %s!", *header);
1542       freez(*header);
1543       return JB_ERR_OK;
1544    }
1545    else if (0 == strcmpic(newval, "reset-to-request-time"))
1546    {  
1547       /*
1548        * Setting Last-Modified Header to now.
1549        */
1550       get_http_time(0, buf);
1551       freez(*header);
1552       *header = strdup("Last-Modified: ");
1553       string_append(header, buf);   
1554
1555       if (*header == NULL)
1556       {
1557          log_error(LOG_LEVEL_HEADER, "Insufficent memory. Last-Modified header got lost, boohoo.");  
1558       }
1559       else
1560       {
1561          log_error(LOG_LEVEL_HEADER, "Reset to present time: %s", *header);
1562       }
1563    }
1564    else if (0 == strcmpic(newval, "randomize"))
1565    {
1566       log_error(LOG_LEVEL_HEADER, "Randomizing: %s", *header);
1567       now = time(NULL);
1568 #ifdef HAVE_GMTIME_R
1569       timeptr = gmtime_r(&now, &gmt);
1570 #elif OSX_DARWIN
1571       pthread_mutex_lock(&gmtime_mutex);
1572       timeptr = gmtime(&now);
1573       pthread_mutex_unlock(&gmtime_mutex);
1574 #else
1575       timeptr = gmtime(&now);
1576 #endif
1577       if ((timeptr = parse_header_time(*header, &last_modified)) == NULL)
1578       {
1579          log_error(LOG_LEVEL_HEADER, "Couldn't parse: %s (crunching!)", *header);
1580          freez(*header);
1581       }
1582       else
1583       {
1584          rtime = difftime(now, last_modified);
1585          if (rtime)
1586          {
1587             rtime = pick_from_range(rtime);
1588             last_modified += rtime;
1589 #ifdef HAVE_GMTIME_R
1590             timeptr = gmtime_r(&last_modified, &gmt);
1591 #elif OSX_DARWIN
1592             pthread_mutex_lock(&gmtime_mutex);
1593             timeptr = gmtime(&last_modified);
1594             pthread_mutex_unlock(&gmtime_mutex);
1595 #else
1596             timeptr = gmtime(&last_modified);
1597 #endif
1598             strftime(newheader, sizeof(newheader), "%a, %d %b %Y %H:%M:%S GMT", timeptr);
1599             freez(*header);
1600             *header = strdup("Last-Modified: ");
1601             string_append(header, newheader);
1602
1603             if (*header == NULL)
1604             {
1605                log_error(LOG_LEVEL_ERROR, "Insufficent memory, header crunched without replacement.");
1606                return JB_ERR_MEMORY;  
1607             }
1608
1609             if(LOG_LEVEL_HEADER & debug) /* Save cycles if the user isn't interested. */
1610             {
1611                days    = rtime / (3600 * 24);
1612                hours   = rtime / 3600 % 24;
1613                minutes = rtime / 60 % 60;
1614                seconds = rtime % 60;            
1615
1616                log_error(LOG_LEVEL_HEADER, "Randomized:  %s (added %d da%s %d hou%s %d minut%s %d second%s",
1617                   *header, days, (days == 1) ? "y" : "ys", hours, (hours == 1) ? "r" : "rs",
1618                   minutes, (minutes == 1) ? "e" : "es", seconds, (seconds == 1) ? ")" : "s)");
1619             }
1620          }
1621          else
1622          {
1623             log_error(LOG_LEVEL_HEADER, "Randomized ... or not. No time difference to work with.");
1624          }
1625       }
1626    }
1627
1628    return JB_ERR_OK;
1629 }
1630
1631 /*********************************************************************
1632  *
1633  * Function    :  client_accept_encoding
1634  *
1635  * Description :  Rewrite the client's Accept-Encoding header so that
1636  *                if doesn't allow compression, if the action applies.
1637  *                Note: For HTTP/1.0 the absence of the header is enough.
1638  *
1639  * Parameters  :
1640  *          1  :  csp = Current client state (buffers, headers, etc...)
1641  *          2  :  header = On input, pointer to header to modify.
1642  *                On output, pointer to the modified header, or NULL
1643  *                to remove the header.  This function frees the
1644  *                original string if necessary.
1645  *
1646  * Returns     :  JB_ERR_OK on success, or
1647  *                JB_ERR_MEMORY on out-of-memory error.
1648  *
1649  *********************************************************************/
1650 jb_err client_accept_encoding(struct client_state *csp, char **header)
1651 {
1652    if ((csp->action->flags & ACTION_NO_COMPRESSION) != 0)
1653    {
1654       log_error(LOG_LEVEL_HEADER, "Suppressed offer to compress content");
1655
1656       freez(*header);
1657
1658       /* Temporarily disable the correct behaviour to
1659        * work around a PHP bug. 
1660        *
1661        * if (!strcmpic(csp->http->ver, "HTTP/1.1"))
1662        * {
1663        *    *header = strdup("Accept-Encoding: identity;q=1.0, *;q=0");
1664        *    if (*header == NULL)
1665        *    {
1666        *       return JB_ERR_MEMORY;
1667        *    }
1668        * }
1669        * 
1670        */
1671    }
1672
1673    return JB_ERR_OK;
1674 }
1675
1676
1677 /*********************************************************************
1678  *
1679  * Function    :  client_te
1680  *
1681  * Description :  Rewrite the client's TE header so that
1682  *                if doesn't allow compression, if the action applies.
1683  *
1684  * Parameters  :
1685  *          1  :  csp = Current client state (buffers, headers, etc...)
1686  *          2  :  header = On input, pointer to header to modify.
1687  *                On output, pointer to the modified header, or NULL
1688  *                to remove the header.  This function frees the
1689  *                original string if necessary.
1690  *
1691  * Returns     :  JB_ERR_OK on success, or
1692  *                JB_ERR_MEMORY on out-of-memory error.
1693  *
1694  *********************************************************************/
1695 jb_err client_te(struct client_state *csp, char **header)
1696 {
1697    if ((csp->action->flags & ACTION_NO_COMPRESSION) != 0)
1698    {
1699       freez(*header);
1700       log_error(LOG_LEVEL_HEADER, "Suppressed offer to compress transfer");
1701    }
1702
1703    return JB_ERR_OK;
1704 }
1705
1706 /*********************************************************************
1707  *
1708  * Function    :  client_referrer
1709  *
1710  * Description :  Handle the "referer" config setting properly.
1711  *                Called from `sed'.
1712  *
1713  * Parameters  :
1714  *          1  :  csp = Current client state (buffers, headers, etc...)
1715  *          2  :  header = On input, pointer to header to modify.
1716  *                On output, pointer to the modified header, or NULL
1717  *                to remove the header.  This function frees the
1718  *                original string if necessary.
1719  *
1720  * Returns     :  JB_ERR_OK on success, or
1721  *                JB_ERR_MEMORY on out-of-memory error.
1722  *
1723  *********************************************************************/
1724 jb_err client_referrer(struct client_state *csp, char **header)
1725 {
1726    const char *newval;
1727    const char *host;
1728    char *referer;
1729    size_t hostlenght;
1730  
1731 #ifdef FEATURE_FORCE_LOAD
1732    /* Since the referrer can include the prefix even
1733     * if the request itself is non-forced, we must
1734     * clean it unconditionally
1735     */
1736    strclean(*header, FORCE_PREFIX);
1737 #endif /* def FEATURE_FORCE_LOAD */
1738
1739    /*
1740     * Are we sending referer?
1741     */
1742    if ((csp->action->flags & ACTION_HIDE_REFERER) == 0)
1743    {
1744       return JB_ERR_OK;
1745    }
1746
1747    newval = csp->action->string[ACTION_STRING_REFERER];
1748
1749    if ((0 != strcmpic(newval, "conditional-block")))
1750    {  
1751       freez(*header);
1752    }
1753    if ((newval == NULL) || (0 == strcmpic(newval, "block")) )
1754    {
1755       /*
1756        * Blocking referer
1757        */
1758       log_error(LOG_LEVEL_HEADER, "Referer crunched!");
1759       return JB_ERR_OK;
1760    }
1761    else if (0 == strcmpic(newval, "conditional-block"))
1762    {
1763       /*
1764        * Block referer if host has changed.
1765        */
1766
1767       if (NULL == (host = strdup(csp->http->hostport)))
1768       {
1769          freez(*header);
1770          log_error(LOG_LEVEL_HEADER, "Referer crunched! Couldn't allocate memory for temporary host copy.");
1771          return JB_ERR_MEMORY;
1772       }
1773       if (NULL == (referer = strdup(*header)))
1774       {
1775          freez(*header);
1776          freez(host);
1777          log_error(LOG_LEVEL_HEADER, "Referer crunched! Couldn't allocate memory for temporary referer copy.");
1778          return JB_ERR_MEMORY;
1779       }
1780       hostlenght = strlen(host);
1781       if ( hostlenght < (strlen(referer)-17) ) /*referer begins with 'Referer: http[s]://'*/
1782       {
1783          /*Shorten referer to make sure the referer is blocked
1784           *if www.example.org/www.example.com-shall-see-the-referer/
1785           *links to www.example.com/
1786           */
1787          referer[hostlenght+17] = '\0';
1788       }
1789       if ( 0 == strstr(referer, host)) /*Host has changed*/
1790       {
1791          log_error(LOG_LEVEL_HEADER, "New host is: %s. Crunching %s!", host, *header);
1792          freez(*header);
1793       }
1794       else
1795       {
1796          log_error(LOG_LEVEL_HEADER, "%s (not modified, still on %s)", *header, host);
1797       }
1798       freez(referer);
1799       freez(host);
1800       return JB_ERR_OK;    
1801    }
1802    else if (0 != strcmpic(newval, "forge"))
1803    {
1804       /*
1805        * We have a specific (fixed) referer we want to send.
1806        */
1807       if ((0 != strncmpic(newval, "http://", 7)) && (0 != strncmpic(newval, "https://", 8)))
1808       {
1809          log_error(LOG_LEVEL_HEADER, "Parameter: +referrer{%s} is a bad idea, but I don't care.", newval);
1810       }
1811       *header = strdup("Referer: ");
1812       string_append(header, newval);
1813       log_error(LOG_LEVEL_HEADER, "Referer overwritten with: %s", *header);
1814
1815       return (*header == NULL) ? JB_ERR_MEMORY : JB_ERR_OK;
1816    }
1817    else
1818    {
1819       /*
1820        * Forge a referer as http://[hostname:port of REQUEST]/
1821        * to fool stupid checks for in-site links
1822        */
1823
1824       *header = strdup("Referer: http://");
1825       string_append(header, csp->http->hostport);
1826       string_append(header, "/");
1827       log_error(LOG_LEVEL_HEADER, "Referer forged to: %s", *header);
1828       
1829       return (*header == NULL) ? JB_ERR_MEMORY : JB_ERR_OK;
1830    }
1831 }
1832
1833 /*********************************************************************
1834  *
1835  * Function    :  client_accept_language
1836  *
1837  * Description :  Handle the "Accept-Language" config setting properly.
1838  *                Called from `sed'.
1839  *
1840  * Parameters  :
1841  *          1  :  csp = Current client state (buffers, headers, etc...)
1842  *          2  :  header = On input, pointer to header to modify.
1843  *                On output, pointer to the modified header, or NULL
1844  *                to remove the header.  This function frees the
1845  *                original string if necessary.
1846  *
1847  * Returns     :  JB_ERR_OK on success, or
1848  *                JB_ERR_MEMORY on out-of-memory error.
1849  *
1850  *********************************************************************/
1851 jb_err client_accept_language(struct client_state *csp, char **header)
1852 {
1853    const char *newval;
1854
1855    /*
1856     * Are we messing with the Accept-Language?
1857     */
1858    if ((csp->action->flags & ACTION_HIDE_ACCEPT_LANGUAGE) == 0)
1859    {
1860       /*I don't think so*/
1861       return JB_ERR_OK;
1862    }
1863
1864    newval = csp->action->string[ACTION_STRING_LANGUAGE];
1865
1866    if ((newval == NULL) || (0 == strcmpic(newval, "block")) )
1867    {
1868       /*
1869        * Blocking Accept-Language header
1870        */
1871       log_error(LOG_LEVEL_HEADER, "Crunching Accept-Language!");
1872       freez(*header);
1873       return JB_ERR_OK;
1874    }
1875    else
1876    {  
1877       /*
1878        * Replacing Accept-Language header
1879        */
1880       freez(*header);
1881       *header = strdup("Accept-Language: ");
1882       string_append(header, newval);   
1883
1884       if (*header == NULL)
1885       {
1886          log_error(LOG_LEVEL_ERROR, " Insufficent memory. Accept-Language header crunched without replacement.");  
1887       }
1888       else
1889       {
1890          log_error(LOG_LEVEL_HEADER, "Accept-Language header crunched and replaced with: %s", *header);
1891       }
1892    }
1893    return (*header == NULL) ? JB_ERR_MEMORY : JB_ERR_OK;
1894 }
1895
1896 /*********************************************************************
1897  *
1898  * Function    :  crunch_client_header
1899  *
1900  * Description :  Crunch client header if it matches a string supplied by the
1901  *                user. Called from `sed'.
1902  *
1903  * Parameters  :
1904  *          1  :  csp = Current client state (buffers, headers, etc...)
1905  *          2  :  header = On input, pointer to header to modify.
1906  *                On output, pointer to the modified header, or NULL
1907  *                to remove the header.  This function frees the
1908  *                original string if necessary.
1909  *
1910  * Returns     :  JB_ERR_OK on success and always succeeds
1911  *
1912  *********************************************************************/
1913 jb_err crunch_client_header(struct client_state *csp, char **header)
1914 {
1915    const char *crunch_pattern;
1916    /*Is there a header to crunch*/
1917    
1918    if ((csp->action->flags & ACTION_CRUNCH_CLIENT_HEADER))
1919    {
1920       crunch_pattern = csp->action->string[ACTION_STRING_CLIENT_HEADER];
1921
1922       /*Is the current header the lucky one?*/
1923       if (strstr(*header, crunch_pattern))
1924       {
1925          log_error(LOG_LEVEL_HEADER, "Crunching client header: %s (contains: %s)", *header, crunch_pattern);  
1926          freez(*header);
1927       }
1928    }
1929    return JB_ERR_OK;
1930 }
1931
1932
1933 /*********************************************************************
1934  *
1935  * Function    :  client_uagent
1936  *
1937  * Description :  Handle the "user-agent" config setting properly
1938  *                and remember its original value to enable browser
1939  *                bug workarounds. Called from `sed'.
1940  *
1941  * Parameters  :
1942  *          1  :  csp = Current client state (buffers, headers, etc...)
1943  *          2  :  header = On input, pointer to header to modify.
1944  *                On output, pointer to the modified header, or NULL
1945  *                to remove the header.  This function frees the
1946  *                original string if necessary.
1947  *
1948  * Returns     :  JB_ERR_OK on success, or
1949  *                JB_ERR_MEMORY on out-of-memory error.
1950  *
1951  *********************************************************************/
1952 jb_err client_uagent(struct client_state *csp, char **header)
1953 {
1954    const char *newval;
1955
1956    if ((csp->action->flags & ACTION_HIDE_USER_AGENT) == 0)
1957    {
1958       return JB_ERR_OK;
1959    }
1960
1961    newval = csp->action->string[ACTION_STRING_USER_AGENT];
1962    if (newval == NULL)
1963    {
1964       return JB_ERR_OK;
1965    }
1966
1967    freez(*header);
1968    *header = strdup("User-Agent: ");
1969    string_append(header, newval);
1970
1971    log_error(LOG_LEVEL_HEADER, "Modified: %s", *header);
1972
1973    return (*header == NULL) ? JB_ERR_MEMORY : JB_ERR_OK;
1974 }
1975
1976 /*********************************************************************
1977  *
1978  * Function    :  client_ua
1979  *
1980  * Description :  Handle "ua-" headers properly.  Called from `sed'.
1981  *
1982  * Parameters  :
1983  *          1  :  csp = Current client state (buffers, headers, etc...)
1984  *          2  :  header = On input, pointer to header to modify.
1985  *                On output, pointer to the modified header, or NULL
1986  *                to remove the header.  This function frees the
1987  *                original string if necessary.
1988  *
1989  * Returns     :  JB_ERR_OK on success, or
1990  *                JB_ERR_MEMORY on out-of-memory error.
1991  *
1992  *********************************************************************/
1993 jb_err client_ua(struct client_state *csp, char **header)
1994 {
1995    if ((csp->action->flags & ACTION_HIDE_USER_AGENT) != 0)
1996    {
1997       log_error(LOG_LEVEL_HEADER, "crunched User-Agent!");
1998       freez(*header);
1999    }
2000
2001    return JB_ERR_OK;
2002 }
2003
2004
2005 /*********************************************************************
2006  *
2007  * Function    :  client_from
2008  *
2009  * Description :  Handle the "from" config setting properly.
2010  *                Called from `sed'.
2011  *
2012  * Parameters  :
2013  *          1  :  csp = Current client state (buffers, headers, etc...)
2014  *          2  :  header = On input, pointer to header to modify.
2015  *                On output, pointer to the modified header, or NULL
2016  *                to remove the header.  This function frees the
2017  *                original string if necessary.
2018  *
2019  * Returns     :  JB_ERR_OK on success, or
2020  *                JB_ERR_MEMORY on out-of-memory error.
2021  *
2022  *********************************************************************/
2023 jb_err client_from(struct client_state *csp, char **header)
2024 {
2025    const char *newval;
2026
2027    if ((csp->action->flags & ACTION_HIDE_FROM) == 0)
2028    {
2029       return JB_ERR_OK;
2030    }
2031
2032    freez(*header);
2033
2034    newval = csp->action->string[ACTION_STRING_FROM];
2035
2036    /*
2037     * Are we blocking the e-mail address?
2038     */
2039    if ((newval == NULL) || (0 == strcmpic(newval, "block")) )
2040    {
2041       log_error(LOG_LEVEL_HEADER, "crunched From!");
2042       return JB_ERR_OK;
2043    }
2044
2045    log_error(LOG_LEVEL_HEADER, " modified");
2046
2047    *header = strdup("From: ");
2048    string_append(header, newval);
2049
2050    return (*header == NULL) ? JB_ERR_MEMORY : JB_ERR_OK;
2051 }
2052
2053
2054 /*********************************************************************
2055  *
2056  * Function    :  client_send_cookie
2057  *
2058  * Description :  Handle the "cookie" header properly.  Called from `sed'.
2059  *                If cookie is accepted, add it to the cookie_list,
2060  *                else we crunch it.  Mmmmmmmmmmm ... cookie ......
2061  *
2062  * Parameters  :
2063  *          1  :  csp = Current client state (buffers, headers, etc...)
2064  *          2  :  header = On input, pointer to header to modify.
2065  *                On output, pointer to the modified header, or NULL
2066  *                to remove the header.  This function frees the
2067  *                original string if necessary.
2068  *
2069  * Returns     :  JB_ERR_OK on success, or
2070  *                JB_ERR_MEMORY on out-of-memory error.
2071  *
2072  *********************************************************************/
2073 jb_err client_send_cookie(struct client_state *csp, char **header)
2074 {
2075    jb_err result = JB_ERR_OK;
2076
2077    if ((csp->action->flags & ACTION_NO_COOKIE_READ) == 0)
2078    {
2079       /* strlen("cookie: ") == 8 */
2080       result = enlist(csp->cookie_list, *header + 8);
2081    }
2082    else
2083    {
2084       log_error(LOG_LEVEL_HEADER, "Crunched outgoing cookie -- yum!");
2085    }
2086
2087    /*
2088     * Always remove the cookie here.  The cookie header
2089     * will be sent at the end of the header.
2090     */
2091    freez(*header);
2092
2093    return result;
2094 }
2095
2096
2097 /*********************************************************************
2098  *
2099  * Function    :  client_x_forwarded
2100  *
2101  * Description :  Handle the "x-forwarded-for" config setting properly,
2102  *                also used in the add_client_headers list.  Called from `sed'.
2103  *
2104  * Parameters  :
2105  *          1  :  csp = Current client state (buffers, headers, etc...)
2106  *          2  :  header = On input, pointer to header to modify.
2107  *                On output, pointer to the modified header, or NULL
2108  *                to remove the header.  This function frees the
2109  *                original string if necessary.
2110  *
2111  * Returns     :  JB_ERR_OK on success, or
2112  *                JB_ERR_MEMORY on out-of-memory error.
2113  *
2114  *********************************************************************/
2115 jb_err client_x_forwarded(struct client_state *csp, char **header)
2116 {
2117    if ((csp->action->flags & ACTION_HIDE_FORWARDED) == 0)
2118    {
2119       /* Save it so we can re-add it later */
2120       freez(csp->x_forwarded);
2121       csp->x_forwarded = *header;
2122
2123       /*
2124        * Always set *header = NULL, since this information
2125        * will be sent at the end of the header.
2126        */
2127       *header = NULL;
2128    }
2129    else
2130    {
2131       freez(*header);
2132       log_error(LOG_LEVEL_HEADER, "crunched x-forwarded-for!");
2133    }
2134
2135    return JB_ERR_OK;
2136 }
2137
2138
2139 /*********************************************************************
2140  *
2141  * Function    :  client_max_forwards
2142  *
2143  * Description :  If the HTTP method is OPTIONS or TRACE, subtract one
2144  *                from the value of the Max-Forwards header field.
2145  *
2146  * Parameters  :
2147  *          1  :  csp = Current client state (buffers, headers, etc...)
2148  *          2  :  header = On input, pointer to header to modify.
2149  *                On output, pointer to the modified header, or NULL
2150  *                to remove the header.  This function frees the
2151  *                original string if necessary.
2152  *
2153  * Returns     :  JB_ERR_OK on success, or
2154  *                JB_ERR_MEMORY on out-of-memory error.
2155  *
2156  *********************************************************************/
2157 jb_err client_max_forwards(struct client_state *csp, char **header)
2158 {
2159    unsigned int max_forwards;
2160
2161    if ((0 == strcmpic(csp->http->gpc, "trace"))
2162       || (0 == strcmpic(csp->http->gpc, "options")))
2163    {
2164       if (1 == sscanf(*header, "Max-Forwards: %u", &max_forwards))
2165       {
2166          if (max_forwards-- >= 1)
2167          {
2168             sprintf(*header, "Max-Forwards: %u", max_forwards);
2169             log_error(LOG_LEVEL_HEADER, "Max forwards of %s request now %d", csp->http->gpc, max_forwards);
2170          }
2171          else
2172          {
2173             log_error(LOG_LEVEL_ERROR, "Non-intercepted %s request with Max-Forwards zero!", csp->http->gpc);
2174          }
2175       }
2176    }
2177
2178    return JB_ERR_OK;
2179 }
2180
2181
2182 /*********************************************************************
2183  *
2184  * Function    :  client_host
2185  *
2186  * Description :  If the request URI did not contain host and
2187  *                port information, parse and evaluate the Host
2188  *                header field.
2189  *
2190  *                Also, kill ill-formed HOST: headers as sent by
2191  *                Apple's iTunes software when used with a proxy.
2192  *
2193  * Parameters  :
2194  *          1  :  csp = Current client state (buffers, headers, etc...)
2195  *          2  :  header = On input, pointer to header to modify.
2196  *                On output, pointer to the modified header, or NULL
2197  *                to remove the header.  This function frees the
2198  *                original string if necessary.
2199  *
2200  * Returns     :  JB_ERR_OK on success, or
2201  *                JB_ERR_MEMORY on out-of-memory error.
2202  *
2203  *********************************************************************/
2204 jb_err client_host(struct client_state *csp, char **header)
2205 {
2206    char *p, *q;
2207
2208    /*
2209     * If the header field name is all upper-case, chances are that it's
2210     * an ill-formed one from iTunes. BTW, killing innocent headers here is
2211     * not a problem -- they are regenerated later.
2212     */
2213    if ((*header)[1] == 'O')
2214    {
2215       log_error(LOG_LEVEL_HEADER, "Killed all-caps Host header line: %s", *header);
2216       freez(*header);
2217       return JB_ERR_OK;
2218    }
2219
2220    if (!csp->http->hostport || (*csp->http->hostport == '*') ||  
2221        *csp->http->hostport == ' ' || *csp->http->hostport == '\0')
2222    {
2223       
2224       if (NULL == (p = strdup((*header)+6)))
2225       {
2226          return JB_ERR_MEMORY;
2227       }
2228       chomp(p);
2229       if (NULL == (q = strdup(p)))
2230       {
2231          freez(p);
2232          return JB_ERR_MEMORY;
2233       }
2234
2235       freez(csp->http->hostport);
2236       csp->http->hostport = p;
2237       freez(csp->http->host);
2238       csp->http->host = q;
2239       q = strchr(csp->http->host, ':');
2240       if (q != NULL)
2241       {
2242          /* Terminate hostname and evaluate port string */
2243          *q++ = '\0';
2244          csp->http->port = atoi(q);
2245       }
2246       else
2247       {
2248          csp->http->port = csp->http->ssl ? 443 : 80;
2249       }
2250
2251       log_error(LOG_LEVEL_HEADER, "New host and port from Host field: %s = %s:%d",
2252                 csp->http->hostport, csp->http->host, csp->http->port);
2253    }
2254
2255    return JB_ERR_OK;
2256 }
2257
2258 /*********************************************************************
2259  *
2260  * Function    :  client_if_modified_since
2261  *
2262  * Description :  Remove or modify the If-Modified-Since header.
2263  *
2264  * Parameters  :
2265  *          1  :  csp = Current client state (buffers, headers, etc...)
2266  *          2  :  header = On input, pointer to header to modify.
2267  *                On output, pointer to the modified header, or NULL
2268  *                to remove the header.  This function frees the
2269  *                original string if necessary.
2270  *
2271  * Returns     :  JB_ERR_OK on success, or
2272  *                JB_ERR_MEMORY on out-of-memory error.
2273  *
2274  *********************************************************************/
2275 jb_err client_if_modified_since(struct client_state *csp, char **header)
2276 {
2277    char newheader[50];
2278 #ifdef HAVE_GMTIME_R
2279    struct tm gmt;
2280 #endif
2281    struct tm *timeptr = NULL;
2282    time_t tm = 0;                  
2283    const char *newval;
2284    long int rtime;
2285    long int hours, minutes, seconds;
2286    int negative = 0;
2287    char * endptr;
2288    
2289    if ( 0 == strcmpic(*header, "If-Modified-Since: Wed, 08 Jun 1955 12:00:00 GMT"))
2290    {
2291       /* 
2292        * The client got an error message because of a temporary problem,
2293        * the problem is gone and the client now tries to revalidate our
2294        * error message on the real server. The revalidation would always
2295        * end with the transmission of the whole document and there is
2296        * no need to expose the bogus If-Modified-Since header.
2297        */
2298       log_error(LOG_LEVEL_HEADER, "Crunching useless If-Modified-Since header.");
2299       freez(*header);
2300    }
2301    else if (csp->action->flags & ACTION_HIDE_IF_MODIFIED_SINCE)
2302    {
2303       newval = csp->action->string[ACTION_STRING_IF_MODIFIED_SINCE];
2304
2305       if ((0 == strcmpic(newval, "block")))
2306       {
2307          log_error(LOG_LEVEL_HEADER, "Crunching %s", *header);
2308          freez(*header);
2309       }
2310       else /* add random value */
2311       {
2312          if ((timeptr = parse_header_time(*header, &tm)) == NULL)
2313          {
2314             log_error(LOG_LEVEL_HEADER, "Couldn't parse: %s (crunching!)", *header);
2315             freez(*header);
2316          }
2317          else
2318          {
2319             rtime = strtol(newval, &endptr, 0);
2320             if(rtime)
2321             {
2322                log_error(LOG_LEVEL_HEADER, "Randomizing: %s (random range: %d minut%s)",
2323                   *header, rtime, (rtime == 1 || rtime == -1) ? "e": "es");
2324                if(rtime < 0)
2325                {
2326                   rtime *= -1; 
2327                   negative = 1;
2328                }
2329                rtime *= 60;
2330                rtime = pick_from_range(rtime);
2331             }
2332             else
2333             {
2334                log_error(LOG_LEVEL_ERROR, "Random range is 0. Assuming time transformation test.",
2335                   *header);
2336             }
2337             tm += rtime * (negative ? -1 : 1);
2338 #ifdef HAVE_GMTIME_R
2339             timeptr = gmtime_r(&tm, &gmt);
2340 #elif OSX_DARWIN
2341             pthread_mutex_lock(&gmtime_mutex);
2342             timeptr = gmtime(&tm);
2343             pthread_mutex_unlock(&gmtime_mutex);
2344 #else
2345             timeptr = gmtime(&tm);
2346 #endif
2347             strftime(newheader, sizeof(newheader), "%a, %d %b %Y %H:%M:%S GMT", timeptr);
2348
2349             freez(*header);
2350             *header = strdup("If-Modified-Since: ");
2351             string_append(header, newheader);
2352
2353             if (*header == NULL)
2354             {
2355                log_error(LOG_LEVEL_HEADER, "Insufficent memory, header crunched without replacement.");
2356                return JB_ERR_MEMORY;  
2357             }
2358
2359             if(LOG_LEVEL_HEADER & debug) /* Save cycles if the user isn't interested. */
2360             {
2361                hours   = rtime / 3600;
2362                minutes = rtime / 60 % 60;
2363                seconds = rtime % 60;            
2364
2365                log_error(LOG_LEVEL_HEADER, "Randomized:  %s (%s %d hou%s %d minut%s %d second%s",
2366                   *header, (negative) ? "subtracted" : "added", hours, (hours == 1) ? "r" : "rs",
2367                   minutes, (minutes == 1) ? "e" : "es", seconds, (seconds == 1) ? ")" : "s)");
2368             }
2369          }
2370       }
2371    }
2372
2373    return JB_ERR_OK;
2374 }
2375
2376 /*********************************************************************
2377  *
2378  * Function    :  client_if_none_match
2379  *
2380  * Description :  Remove the If-None-Match header.
2381  *
2382  * Parameters  :
2383  *          1  :  csp = Current client state (buffers, headers, etc...)
2384  *          2  :  header = On input, pointer to header to modify.
2385  *                On output, pointer to the modified header, or NULL
2386  *                to remove the header.  This function frees the
2387  *                original string if necessary.
2388  *
2389  * Returns     :  JB_ERR_OK on success, or
2390  *                JB_ERR_MEMORY on out-of-memory error.
2391  *
2392  *********************************************************************/
2393 jb_err client_if_none_match(struct client_state *csp, char **header)
2394 {
2395    if (csp->action->flags & ACTION_CRUNCH_IF_NONE_MATCH)
2396    {  
2397       log_error(LOG_LEVEL_HEADER, "Crunching %s", *header);
2398       freez(*header);
2399    }
2400
2401    return JB_ERR_OK;
2402 }
2403
2404 /*********************************************************************
2405  *
2406  * Function    :  client_x_filter
2407  *
2408  * Description :  Disables filtering if the client set "X-Filter: No".
2409  *                Called from `sed'.
2410  *
2411  * Parameters  :
2412  *          1  :  csp = Current client state (buffers, headers, etc...)
2413  *          2  :  header = On input, pointer to header to modify.
2414  *                On output, pointer to the modified header, or NULL
2415  *                to remove the header.  This function frees the
2416  *                original string if necessary.
2417  *
2418  * Returns     :  JB_ERR_OK on success
2419  *
2420  *********************************************************************/
2421 jb_err client_x_filter(struct client_state *csp, char **header)
2422 {
2423    if ( 0 == strcmpic(*header, "X-Filter: No"))
2424    {
2425       if (!(csp->config->feature_flags & RUNTIME_FEATURE_HTTP_TOGGLE))
2426       {
2427          log_error(LOG_LEVEL_INFO, "Ignored the client's request to fetch without filtering.");
2428       }
2429                 else
2430       {
2431          if (csp->action->flags & ACTION_FORCE_TEXT_MODE)
2432          {
2433             log_error(LOG_LEVEL_HEADER, "force-text-mode overruled the client's request to fetch without filtering!");
2434          }
2435          else
2436          {  
2437             csp->content_type = CT_TABOO;
2438             log_error(LOG_LEVEL_HEADER, "Accepted the client's request to fetch without filtering.");
2439          }
2440          log_error(LOG_LEVEL_HEADER, "Crunching %s", *header);
2441          freez(*header);
2442       }
2443    }
2444    return JB_ERR_OK; 
2445 }
2446
2447 /* the following functions add headers directly to the header list */
2448
2449 /*********************************************************************
2450  *
2451  * Function    :  client_host_adder
2452  *
2453  * Description :  Adds the Host: header field if it is missing.
2454  *                Called from `sed'.
2455  *
2456  * Parameters  :
2457  *          1  :  csp = Current client state (buffers, headers, etc...)
2458  *
2459  * Returns     :  JB_ERR_OK on success, or
2460  *                JB_ERR_MEMORY on out-of-memory error.
2461  *
2462  *********************************************************************/
2463 jb_err client_host_adder(struct client_state *csp)
2464 {
2465    char *p;
2466    jb_err err;
2467
2468    if ( !csp->http->hostport || !*(csp->http->hostport))
2469    {
2470       return JB_ERR_OK;
2471    }
2472
2473    /*
2474     * remove 'user:pass@' from 'proto://user:pass@host'
2475     */
2476    if ( (p = strchr( csp->http->hostport, '@')) != NULL )
2477    {
2478       p++;
2479    }
2480    else
2481    {
2482       p = csp->http->hostport;
2483    }
2484
2485    log_error(LOG_LEVEL_HEADER, "addh-unique: Host: %s", p);
2486    err = enlist_unique_header(csp->headers, "Host", p);
2487    return err;
2488
2489 }
2490
2491
2492 /*********************************************************************
2493  *
2494  * Function    :  client_cookie_adder
2495  *
2496  * Description :  Used in the add_client_headers list.  Called from `sed'.
2497  *
2498  * Parameters  :
2499  *          1  :  csp = Current client state (buffers, headers, etc...)
2500  *
2501  * Returns     :  JB_ERR_OK on success, or
2502  *                JB_ERR_MEMORY on out-of-memory error.
2503  *
2504  *********************************************************************/
2505 jb_err client_cookie_adder(struct client_state *csp)
2506 {
2507    struct list_entry *lst;
2508    char *tmp;
2509    struct list_entry *list1 = csp->cookie_list->first;
2510    struct list_entry *list2 = csp->action->multi[ACTION_MULTI_WAFER]->first;
2511    int first_cookie = 1;
2512    jb_err err;
2513
2514    if ((list1 == NULL) && (list2 == NULL))
2515    {
2516       /* Nothing to do */
2517       return JB_ERR_OK;
2518    }
2519
2520    tmp = strdup("Cookie: ");
2521
2522    for (lst = list1; lst ; lst = lst->next)
2523    {
2524       if (first_cookie)
2525       {
2526          first_cookie = 0;
2527       }
2528       else
2529       {
2530          string_append(&tmp, "; ");
2531       }
2532       string_append(&tmp, lst->str);
2533    }
2534
2535    for (lst = list2;  lst ; lst = lst->next)
2536    {
2537       if (first_cookie)
2538       {
2539          first_cookie = 0;
2540       }
2541       else
2542       {
2543          string_append(&tmp, "; ");
2544       }
2545       string_join(&tmp, cookie_encode(lst->str));
2546    }
2547
2548    if (tmp == NULL)
2549    {
2550       return JB_ERR_MEMORY;
2551    }
2552
2553    log_error(LOG_LEVEL_HEADER, "addh: %s", tmp);
2554    err = enlist(csp->headers, tmp);
2555    free(tmp);
2556    return err;
2557 }
2558
2559
2560 /*********************************************************************
2561  *
2562  * Function    :  client_accept_encoding_adder
2563  *
2564  * Description :  Add an Accept-Encoding header to the client's request
2565  *                that disables compression if the action applies, and
2566  *                the header is not already there. Called from `sed'.
2567  *                Note: For HTTP/1.0, the absence of the header is enough.
2568  *
2569  * Parameters  :
2570  *          1  :  csp = Current client state (buffers, headers, etc...)
2571  *
2572  * Returns     :  JB_ERR_OK on success, or
2573  *                JB_ERR_MEMORY on out-of-memory error.
2574  *
2575  *********************************************************************/
2576 jb_err client_accept_encoding_adder(struct client_state *csp)
2577 {
2578    if (   ((csp->action->flags & ACTION_NO_COMPRESSION) != 0)
2579        && (!strcmpic(csp->http->ver, "HTTP/1.1")) )
2580    {
2581       return enlist_unique(csp->headers, "Accept-Encoding: identity;q=1.0, *;q=0", 16);
2582    }
2583
2584    return JB_ERR_OK;
2585 }
2586
2587
2588 /*********************************************************************
2589  *
2590  * Function    :  client_xtra_adder
2591  *
2592  * Description :  Used in the add_client_headers list.  Called from `sed'.
2593  *
2594  * Parameters  :
2595  *          1  :  csp = Current client state (buffers, headers, etc...)
2596  *
2597  * Returns     :  JB_ERR_OK on success, or
2598  *                JB_ERR_MEMORY on out-of-memory error.
2599  *
2600  *********************************************************************/
2601 jb_err client_xtra_adder(struct client_state *csp)
2602 {
2603    struct list_entry *lst;
2604    jb_err err;
2605
2606    for (lst = csp->action->multi[ACTION_MULTI_ADD_HEADER]->first;
2607         lst ; lst = lst->next)
2608    {
2609       log_error(LOG_LEVEL_HEADER, "addh: %s", lst->str);
2610       err = enlist(csp->headers, lst->str);
2611       if (err)
2612       {
2613          return err;
2614       }
2615
2616    }
2617
2618    return JB_ERR_OK;
2619 }
2620
2621
2622 /*********************************************************************
2623  *
2624  * Function    :  client_x_forwarded_adder
2625  *
2626  * Description :  Used in the add_client_headers list.  Called from `sed'.
2627  *
2628  * Parameters  :
2629  *          1  :  csp = Current client state (buffers, headers, etc...)
2630  *
2631  * Returns     :  JB_ERR_OK on success, or
2632  *                JB_ERR_MEMORY on out-of-memory error.
2633  *
2634  *********************************************************************/
2635 jb_err client_x_forwarded_adder(struct client_state *csp)
2636 {
2637    char *p = NULL;
2638    jb_err err;
2639
2640    if ((csp->action->flags & ACTION_HIDE_FORWARDED) != 0)
2641    {
2642       return JB_ERR_OK;
2643    }
2644
2645    if (csp->x_forwarded)
2646    {
2647       p = strdup(csp->x_forwarded);
2648       string_append(&p, ", ");
2649    }
2650    else
2651    {
2652       p = strdup("X-Forwarded-For: ");
2653    }
2654    string_append(&p, csp->ip_addr_str);
2655
2656    if (p == NULL)
2657    {
2658       return JB_ERR_MEMORY;
2659    }
2660
2661    log_error(LOG_LEVEL_HEADER, "addh: %s", p);
2662    err = enlist(csp->headers, p);
2663    free(p);
2664
2665    return err;
2666 }
2667
2668
2669 /*********************************************************************
2670  *
2671  * Function    :  connection_close_adder
2672  *
2673  * Description :  Adds a "Connection: close" header to csp->headers
2674  *                as a temporary fix for the needed but missing HTTP/1.1
2675  *                support. Called from `sed'.
2676  *                FIXME: This whole function shouldn't be neccessary!
2677  *
2678  * Parameters  :
2679  *          1  :  csp = Current client state (buffers, headers, etc...)
2680  *
2681  * Returns     :  JB_ERR_OK on success, or
2682  *                JB_ERR_MEMORY on out-of-memory error.
2683  *
2684  *********************************************************************/
2685 jb_err connection_close_adder(struct client_state *csp)
2686 {
2687    log_error(LOG_LEVEL_HEADER, "Adding: Connection: close");
2688    return enlist(csp->headers, "Connection: close");
2689 }
2690
2691
2692 /*********************************************************************
2693  *
2694  * Function    :  server_http
2695  *
2696  * Description :  - Save the HTTP Status into csp->http->status
2697  *                - Set CT_TABOO to prevent filtering if the answer
2698  *                  is a partial range (HTTP status 206)
2699  *                - Rewrite HTTP/1.1 answers to HTTP/1.0 if +downgrade
2700  *                  action applies.
2701  *
2702  * Parameters  :
2703  *          1  :  csp = Current client state (buffers, headers, etc...)
2704  *          2  :  header = On input, pointer to header to modify.
2705  *                On output, pointer to the modified header, or NULL
2706  *                to remove the header.  This function frees the
2707  *                original string if necessary.
2708  *
2709  * Returns     :  JB_ERR_OK on success, or
2710  *                JB_ERR_MEMORY on out-of-memory error.
2711  *
2712  *********************************************************************/
2713 jb_err server_http(struct client_state *csp, char **header)
2714 {
2715    sscanf(*header, "HTTP/%*d.%*d %d", &(csp->http->status));
2716    if (csp->http->status == 206)
2717    {
2718       csp->content_type = CT_TABOO;
2719    }
2720
2721    if ((csp->action->flags & ACTION_DOWNGRADE) != 0)
2722    {
2723       (*header)[7] = '0';
2724       log_error(LOG_LEVEL_HEADER, "Downgraded answer to HTTP/1.0");
2725    }
2726
2727    return JB_ERR_OK;
2728 }
2729
2730
2731 /*********************************************************************
2732  *
2733  * Function    :  server_set_cookie
2734  *
2735  * Description :  Handle the server "cookie" header properly.
2736  *                Log cookie to the jar file.  Then "crunch" it,
2737  *                or accept it.  Called from `sed'.
2738  *
2739  * Parameters  :
2740  *          1  :  csp = Current client state (buffers, headers, etc...)
2741  *          2  :  header = On input, pointer to header to modify.
2742  *                On output, pointer to the modified header, or NULL
2743  *                to remove the header.  This function frees the
2744  *                original string if necessary.
2745  *
2746  * Returns     :  JB_ERR_OK on success, or
2747  *                JB_ERR_MEMORY on out-of-memory error.
2748  *
2749  *********************************************************************/
2750 jb_err server_set_cookie(struct client_state *csp, char **header)
2751 {
2752 #ifdef FEATURE_COOKIE_JAR
2753    if (csp->config->jar)
2754    {
2755       /*
2756        * Write timestamp into outbuf.
2757        *
2758        * Complex because not all OSs have tm_gmtoff or
2759        * the %z field in strftime()
2760        */
2761       char tempbuf[ BUFFER_SIZE ];
2762       time_t now; 
2763       struct tm tm_now; 
2764       time (&now); 
2765 #ifdef HAVE_LOCALTIME_R
2766       tm_now = *localtime_r(&now, &tm_now);
2767 #elif OSX_DARWIN
2768       pthread_mutex_lock(&localtime_mutex);
2769       tm_now = *localtime (&now); 
2770       pthread_mutex_unlock(&localtime_mutex);
2771 #else
2772       tm_now = *localtime (&now); 
2773 #endif
2774       strftime(tempbuf, BUFFER_SIZE-6, "%b %d %H:%M:%S ", &tm_now); 
2775
2776       /* strlen("set-cookie: ") = 12 */
2777       fprintf(csp->config->jar, "%s %s\t%s\n", tempbuf, csp->http->host, *header + 12);
2778    }
2779 #endif /* def FEATURE_COOKIE_JAR */
2780
2781    if ((csp->action->flags & ACTION_NO_COOKIE_SET) != 0)
2782    {
2783       log_error(LOG_LEVEL_HEADER, "Crunched incoming cookie -- yum!");
2784       return crumble(csp, header);
2785    }
2786    else if ((csp->action->flags & ACTION_NO_COOKIE_KEEP) != 0)
2787    {
2788       /* Flag whether or not to log a message */
2789       int changed = 0;
2790
2791       /* A variable to store the tag we're working on */
2792       char *cur_tag;
2793
2794       /* Skip "Set-Cookie:" (11 characters) in header */
2795       cur_tag = *header + 11;
2796
2797       /* skip whitespace between "Set-Cookie:" and value */
2798       while (*cur_tag && ijb_isspace(*cur_tag))
2799       {
2800          cur_tag++;
2801       }
2802
2803       /* Loop through each tag in the cookie */
2804       while (*cur_tag)
2805       {
2806          /* Find next tag */
2807          char *next_tag = strchr(cur_tag, ';');
2808          if (next_tag != NULL)
2809          {
2810             /* Skip the ';' character itself */
2811             next_tag++;
2812
2813             /* skip whitespace ";" and start of tag */
2814             while (*next_tag && ijb_isspace(*next_tag))
2815             {
2816                next_tag++;
2817             }
2818          }
2819          else
2820          {
2821             /* "Next tag" is the end of the string */
2822             next_tag = cur_tag + strlen(cur_tag);
2823          }
2824
2825          /* Is this the "Expires" tag? */
2826          if (strncmpic(cur_tag, "expires=", 8) == 0)
2827          {
2828             /* Delete the tag by copying the rest of the string over it.
2829              * (Note that we cannot just use "strcpy(cur_tag, next_tag)",
2830              * since the behaviour of strcpy is undefined for overlapping
2831              * strings.)
2832              */
2833             memmove(cur_tag, next_tag, strlen(next_tag) + 1);
2834
2835             /* That changed the header, need to issue a log message */
2836             changed = 1;
2837
2838             /* Note that the next tag has now been moved to *cur_tag,
2839              * so we do not need to update the cur_tag pointer.
2840              */
2841          }
2842          else
2843          {
2844             /* Move on to next cookie tag */
2845             cur_tag = next_tag;
2846          }
2847       }
2848
2849       if (changed)
2850       {
2851          log_error(LOG_LEVEL_HEADER, "Changed cookie to a temporary one.");
2852       }
2853    }
2854
2855    return JB_ERR_OK;
2856 }
2857
2858
2859 #ifdef FEATURE_FORCE_LOAD
2860 /*********************************************************************
2861  *
2862  * Function    :  strclean
2863  *
2864  * Description :  In-Situ-Eliminate all occurances of substring in
2865  *                string
2866  *
2867  * Parameters  :
2868  *          1  :  string = string to clean
2869  *          2  :  substring = substring to eliminate
2870  *
2871  * Returns     :  Number of eliminations
2872  *
2873  *********************************************************************/
2874 int strclean(const char *string, const char *substring)
2875 {
2876    int hits = 0, len = strlen(substring);
2877    char *pos, *p;
2878
2879    while((pos = strstr(string, substring)) != NULL)
2880    {
2881       p = pos + len;
2882       do
2883       {
2884          *(p - len) = *p;
2885       }
2886       while (*p++ != '\0');
2887
2888       hits++;
2889    }
2890
2891    return(hits);
2892 }
2893 #endif /* def FEATURE_FORCE_LOAD */
2894
2895 /*********************************************************************
2896  *
2897  * Function    :  parse_header_time
2898  *
2899  * Description :  Transforms time inside a HTTP header into
2900  *                the usual time format.
2901  *
2902  * Parameters  :
2903  *          1  :  header = header to parse
2904  *          2  :  tm = storage for the resulting time in seconds 
2905  *
2906  * Returns     :  Time struct containing the header time, or
2907  *                NULL in case of a parsing problem.
2908  *
2909  *********************************************************************/
2910 struct tm *parse_header_time(char *header, time_t *tm) {
2911
2912    char * timestring;
2913    struct tm gmt;
2914    struct tm * timeptr;
2915
2916    /* Skipping header name */
2917    timestring = strstr(header, ": ");
2918    if (strptime(timestring, ": %a, %d %b %Y %H:%M:%S", &gmt) == NULL)
2919    {
2920       timeptr = NULL;
2921    }
2922    else
2923    {
2924       *tm = timegm(&gmt);
2925       timeptr=&gmt;
2926    }
2927    return(timeptr);
2928 }
2929
2930
2931 /*
2932   Local Variables:
2933   tab-width: 3
2934   end:
2935 */