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