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