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