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