Remove 'user:pass@' from 'proto://user:pass@host' for the
[privoxy.git] / parsers.c
1 const char parsers_rcs[] = "$Id: parsers.c,v 1.43 2001/11/23 00:26:38 jongfoster 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', `free_http_request', `get_header',
14  *                   `list_to_text', `parse_http_request', `sed',
15  *                   and `server_set_cookie'.
16  *
17  * Copyright   :  Written by and Copyright (C) 2001 the SourceForge
18  *                IJBSWA team.  http://ijbswa.sourceforge.net
19  *
20  *                Based on the Internet Junkbuster originally written
21  *                by and Copyright (C) 1997 Anonymous Coders and
22  *                Junkbusters Corporation.  http://www.junkbusters.com
23  *
24  *                This program is free software; you can redistribute it
25  *                and/or modify it under the terms of the GNU General
26  *                Public License as published by the Free Software
27  *                Foundation; either version 2 of the License, or (at
28  *                your option) any later version.
29  *
30  *                This program is distributed in the hope that it will
31  *                be useful, but WITHOUT ANY WARRANTY; without even the
32  *                implied warranty of MERCHANTABILITY or FITNESS FOR A
33  *                PARTICULAR PURPOSE.  See the GNU General Public
34  *                License for more details.
35  *
36  *                The GNU General Public License should be included with
37  *                this file.  If not, you can view it at
38  *                http://www.gnu.org/copyleft/gpl.html
39  *                or write to the Free Software Foundation, Inc., 59
40  *                Temple Place - Suite 330, Boston, MA  02111-1307, USA.
41  *
42  * Revisions   :
43  *    $Log: parsers.c,v $
44  *    Revision 1.43  2001/11/23 00:26:38  jongfoster
45  *    Fixing two really stupid errors in my previous commit
46  *
47  *    Revision 1.42  2001/11/22 21:59:30  jongfoster
48  *    Adding code to handle +no-cookies-keep
49  *
50  *    Revision 1.41  2001/11/05 23:43:05  steudten
51  *    Add time+date to log files.
52  *
53  *    Revision 1.40  2001/10/26 20:13:09  jongfoster
54  *    ctype.h is needed in Windows, too.
55  *
56  *    Revision 1.39  2001/10/26 17:40:04  oes
57  *    Introduced get_header_value()
58  *    Removed http->user_agent, csp->referrer and csp->accept_types
59  *    Removed client_accept()
60  *
61  *    Revision 1.38  2001/10/25 03:40:48  david__schmidt
62  *    Change in porting tactics: OS/2's EMX porting layer doesn't allow multiple
63  *    threads to call select() simultaneously.  So, it's time to do a real, live,
64  *    native OS/2 port.  See defines for __EMX__ (the porting layer) vs. __OS2__
65  *    (native). Both versions will work, but using __OS2__ offers multi-threading.
66  *
67  *    Revision 1.37  2001/10/23 21:36:02  jongfoster
68  *    Documenting sed()'s error behaviou (doc change only)
69  *
70  *    Revision 1.36  2001/10/13 12:51:51  joergs
71  *    Removed client_host, (was only required for the old 2.0.2-11 http://noijb.
72  *    force-load), instead crumble Host: and add it (again) in client_host_adder
73  *    (in case we get a HTTP/1.0 request without Host: header and forward it to
74  *    a HTTP/1.1 server/proxy).
75  *
76  *    Revision 1.35  2001/10/09 22:39:21  jongfoster
77  *    assert.h is also required under Win32, so moving out of #ifndef _WIN32
78  *    block.
79  *
80  *    Revision 1.34  2001/10/07 18:50:55  oes
81  *    Added server_content_encoding, renamed server_transfer_encoding
82  *
83  *    Revision 1.33  2001/10/07 18:04:49  oes
84  *    Changed server_http11 to server_http and its pattern to "HTTP".
85  *      Additional functionality: it now saves the HTTP status into
86  *      csp->http->status and sets CT_TABOO for Status 206 (partial range)
87  *
88  *    Revision 1.32  2001/10/07 15:43:28  oes
89  *    Removed FEATURE_DENY_GZIP and replaced it with client_accept_encoding,
90  *       client_te and client_accept_encoding_adder, triggered by the new
91  *       +no-compression action. For HTTP/1.1 the Accept-Encoding header is
92  *       changed to allow only identity and chunked, and the TE header is
93  *       crunched. For HTTP/1.0, Accept-Encoding is crunched.
94  *
95  *    parse_http_request no longer does anything than parsing. The rewriting
96  *      of http->cmd and version mangling are gone. It now also recognizes
97  *      the put and delete methods and saves the url in http->url. Removed
98  *      unused variable.
99  *
100  *    renamed content_type and content_length to have the server_ prefix
101  *
102  *    server_content_type now only works if csp->content_type != CT_TABOO
103  *
104  *    added server_transfer_encoding, which
105  *      - Sets CT_TABOO to prohibit filtering if encoding compresses
106  *      - Raises the CSP_FLAG_CHUNKED flag if Encoding is "chunked"
107  *      - Change from "chunked" to "identity" if body was chunked
108  *        but has been de-chunked for filtering.
109  *
110  *    added server_content_md5 which crunches any Content-MD5 headers
111  *      if the body was modified.
112  *
113  *    made server_http11 conditional on +downgrade action
114  *
115  *    Replaced 6 boolean members of csp with one bitmap (csp->flags)
116  *
117  *    Revision 1.31  2001/10/05 14:25:02  oes
118  *    Crumble Keep-Alive from Server
119  *
120  *    Revision 1.30  2001/09/29 12:56:03  joergs
121  *    IJB now changes HTTP/1.1 to HTTP/1.0 in requests and answers.
122  *
123  *    Revision 1.29  2001/09/24 21:09:24  jongfoster
124  *    Fixing 2 memory leaks that Guy spotted, where the paramater to
125  *    enlist() was not being free()d.
126  *
127  *    Revision 1.28  2001/09/22 16:32:28  jongfoster
128  *    Removing unused #includes.
129  *
130  *    Revision 1.27  2001/09/20 15:45:25  steudten
131  *
132  *    add casting from size_t to int for printf()
133  *    remove local variable shadow s2
134  *
135  *    Revision 1.26  2001/09/16 17:05:14  jongfoster
136  *    Removing unused #include showarg.h
137  *
138  *    Revision 1.25  2001/09/16 13:21:27  jongfoster
139  *    Changes to use new list functions.
140  *
141  *    Revision 1.24  2001/09/13 23:05:50  jongfoster
142  *    Changing the string paramater to the header parsers a "const".
143  *
144  *    Revision 1.23  2001/09/12 18:08:19  steudten
145  *
146  *    In parse_http_request() header rewriting miss the host value, so
147  *    from http://www.mydomain.com the result was just " / " not
148  *    http://www.mydomain.com/ in case we forward.
149  *
150  *    Revision 1.22  2001/09/10 10:58:53  oes
151  *    Silenced compiler warnings
152  *
153  *    Revision 1.21  2001/07/31 14:46:00  oes
154  *     - Persistant connections now suppressed
155  *     - sed() no longer appends empty header to csp->headers
156  *
157  *    Revision 1.20  2001/07/30 22:08:36  jongfoster
158  *    Tidying up #defines:
159  *    - All feature #defines are now of the form FEATURE_xxx
160  *    - Permanently turned off WIN_GUI_EDIT
161  *    - Permanently turned on WEBDAV and SPLIT_PROXY_ARGS
162  *
163  *    Revision 1.19  2001/07/25 17:21:54  oes
164  *    client_uagent now saves copy of User-Agent: header value
165  *
166  *    Revision 1.18  2001/07/13 14:02:46  oes
167  *     - Included fix to repair broken HTTP requests that
168  *       don't contain a path, not even '/'.
169  *     - Removed all #ifdef PCRS
170  *     - content_type now always inspected and classified as
171  *       text, gif or other.
172  *     - formatting / comments
173  *
174  *    Revision 1.17  2001/06/29 21:45:41  oes
175  *    Indentation, CRLF->LF, Tab-> Space
176  *
177  *    Revision 1.16  2001/06/29 13:32:42  oes
178  *    - Fixed a comment
179  *    - Adapted free_http_request
180  *    - Removed logentry from cancelled commit
181  *
182  *    Revision 1.15  2001/06/03 19:12:38  oes
183  *    deleted const struct interceptors
184  *
185  *    Revision 1.14  2001/06/01 18:49:17  jongfoster
186  *    Replaced "list_share" with "list" - the tiny memory gain was not
187  *    worth the extra complexity.
188  *
189  *    Revision 1.13  2001/05/31 21:30:33  jongfoster
190  *    Removed list code - it's now in list.[ch]
191  *    Renamed "permission" to "action", and changed many features
192  *    to use the actions file rather than the global config.
193  *
194  *    Revision 1.12  2001/05/31 17:33:13  oes
195  *
196  *    CRLF -> LF
197  *
198  *    Revision 1.11  2001/05/29 20:11:19  joergs
199  *    '/ * inside comment' warning removed.
200  *
201  *    Revision 1.10  2001/05/29 09:50:24  jongfoster
202  *    Unified blocklist/imagelist/permissionslist.
203  *    File format is still under discussion, but the internal changes
204  *    are (mostly) done.
205  *
206  *    Also modified interceptor behaviour:
207  *    - We now intercept all URLs beginning with one of the following
208  *      prefixes (and *only* these prefixes):
209  *        * http://i.j.b/
210  *        * http://ijbswa.sf.net/config/
211  *        * http://ijbswa.sourceforge.net/config/
212  *    - New interceptors "home page" - go to http://i.j.b/ to see it.
213  *    - Internal changes so that intercepted and fast redirect pages
214  *      are not replaced with an image.
215  *    - Interceptors now have the option to send a binary page direct
216  *      to the client. (i.e. ijb-send-banner uses this)
217  *    - Implemented show-url-info interceptor.  (Which is why I needed
218  *      the above interceptors changes - a typical URL is
219  *      "http://i.j.b/show-url-info?url=www.somesite.com/banner.gif".
220  *      The previous mechanism would not have intercepted that, and
221  *      if it had been intercepted then it then it would have replaced
222  *      it with an image.)
223  *
224  *    Revision 1.9  2001/05/28 17:26:33  jongfoster
225  *    Fixing segfault if last header was crunched.
226  *    Fixing Windows build (snprintf() is _snprintf() under Win32, but we
227  *    can use the cross-platform sprintf() instead.)
228  *
229  *    Revision 1.8  2001/05/27 22:17:04  oes
230  *
231  *    - re_process_buffer no longer writes the modified buffer
232  *      to the client, which was very ugly. It now returns the
233  *      buffer, which it is then written by chat.
234  *
235  *    - content_length now adjusts the Content-Length: header
236  *      for modified documents rather than crunch()ing it.
237  *      (Length info in csp->content_length, which is 0 for
238  *      unmodified documents)
239  *
240  *    - For this to work, sed() is called twice when filtering.
241  *
242  *    Revision 1.7  2001/05/27 13:19:06  oes
243  *    Patched Joergs solution for the content-length in.
244  *
245  *    Revision 1.6  2001/05/26 13:39:32  jongfoster
246  *    Only crunches Content-Length header if applying RE filtering.
247  *    Without this fix, Microsoft Windows Update wouldn't work.
248  *
249  *    Revision 1.5  2001/05/26 00:28:36  jongfoster
250  *    Automatic reloading of config file.
251  *    Removed obsolete SIGHUP support (Unix) and Reload menu option (Win32).
252  *    Most of the global variables have been moved to a new
253  *    struct configuration_spec, accessed through csp->config->globalname
254  *    Most of the globals remaining are used by the Win32 GUI.
255  *
256  *    Revision 1.4  2001/05/22 18:46:04  oes
257  *
258  *    - Enabled filtering banners by size rather than URL
259  *      by adding patterns that replace all standard banner
260  *      sizes with the "Junkbuster" gif to the re_filterfile
261  *
262  *    - Enabled filtering WebBugs by providing a pattern
263  *      which kills all 1x1 images
264  *
265  *    - Added support for PCRE_UNGREEDY behaviour to pcrs,
266  *      which is selected by the (nonstandard and therefore
267  *      capital) letter 'U' in the option string.
268  *      It causes the quantifiers to be ungreedy by default.
269  *      Appending a ? turns back to greedy (!).
270  *
271  *    - Added a new interceptor ijb-send-banner, which
272  *      sends back the "Junkbuster" gif. Without imagelist or
273  *      MSIE detection support, or if tinygif = 1, or the
274  *      URL isn't recognized as an imageurl, a lame HTML
275  *      explanation is sent instead.
276  *
277  *    - Added new feature, which permits blocking remote
278  *      script redirects and firing back a local redirect
279  *      to the browser.
280  *      The feature is conditionally compiled, i.e. it
281  *      can be disabled with --disable-fast-redirects,
282  *      plus it must be activated by a "fast-redirects"
283  *      line in the config file, has its own log level
284  *      and of course wants to be displayed by show-proxy-args
285  *      Note: Boy, all the #ifdefs in 1001 locations and
286  *      all the fumbling with configure.in and acconfig.h
287  *      were *way* more work than the feature itself :-(
288  *
289  *    - Because a generic redirect template was needed for
290  *      this, tinygif = 3 now uses the same.
291  *
292  *    - Moved GIFs, and other static HTTP response templates
293  *      to project.h
294  *
295  *    - Some minor fixes
296  *
297  *    - Removed some >400 CRs again (Jon, you really worked
298  *      a lot! ;-)
299  *
300  *    Revision 1.3  2001/05/20 01:21:20  jongfoster
301  *    Version 2.9.4 checkin.
302  *    - Merged popupfile and cookiefile, and added control over PCRS
303  *      filtering, in new "permissionsfile".
304  *    - Implemented LOG_LEVEL_FATAL, so that if there is a configuration
305  *      file error you now get a message box (in the Win32 GUI) rather
306  *      than the program exiting with no explanation.
307  *    - Made killpopup use the PCRS MIME-type checking and HTTP-header
308  *      skipping.
309  *    - Removed tabs from "config"
310  *    - Moved duplicated url parsing code in "loaders.c" to a new funcition.
311  *    - Bumped up version number.
312  *
313  *    Revision 1.2  2001/05/17 23:02:36  oes
314  *     - Made referrer option accept 'L' as a substitute for '§'
315  *
316  *    Revision 1.1.1.1  2001/05/15 13:59:01  oes
317  *    Initial import of version 2.9.3 source tree
318  *
319  *
320  *********************************************************************/
321 \f
322
323 #include "config.h"
324
325 #ifndef _WIN32
326 #include <stdio.h>
327 #include <sys/types.h>
328 #endif
329
330 #include <stdlib.h>
331 #include <ctype.h>
332 #include <assert.h>
333 #include <string.h>
334
335 #if !defined(_WIN32) && !defined(__OS2__)
336 #include <unistd.h>
337 #endif
338
339 #include "project.h"
340 #include "list.h"
341 #include "parsers.h"
342 #include "encode.h"
343 #include "ssplit.h"
344 #include "errlog.h"
345 #include "jbsockets.h"
346 #include "miscutil.h"
347 #include "list.h"
348
349 const char parsers_h_rcs[] = PARSERS_H_VERSION;
350
351 /* Fix a problem with Solaris.  There should be no effect on other
352  * platforms.
353  * Solaris's isspace() is a macro which uses it's argument directly
354  * as an array index.  Therefore we need to make sure that high-bit
355  * characters generate +ve values, and ideally we also want to make
356  * the argument match the declared parameter type of "int".
357  *
358  * Why did they write a character function that can't take a simple
359  * "char" argument?  Doh!
360  */
361 #define ijb_isupper(__X) isupper((int)(unsigned char)(__X))
362 #define ijb_tolower(__X) tolower((int)(unsigned char)(__X))
363
364
365 const struct parsers client_patterns[] = {
366    { "referer:",                 8,    client_referrer },
367    { "user-agent:",              11,   client_uagent },
368    { "ua-",                      3,    client_ua },
369    { "from:",                    5,    client_from },
370    { "cookie:",                  7,    client_send_cookie },
371    { "x-forwarded-for:",         16,   client_x_forwarded },
372    { "Accept-Encoding:",         16,   client_accept_encoding },
373    { "TE:",                      3,    client_te },
374    { "Host:",                     5,   crumble },
375 /* { "if-modified-since:",       18,   crumble }, */
376    { "Keep-Alive:",              11,   crumble },
377    { "connection:",              11,   crumble },
378    { "proxy-connection:",        17,   crumble },
379    { NULL,                       0,    NULL }
380 };
381
382
383 const struct parsers server_patterns[] = {
384    { "HTTP",                4, server_http },
385    { "set-cookie:",        11, server_set_cookie },
386    { "connection:",        11, crumble },
387    { "Content-Type:",      13, server_content_type },
388    { "Content-Length:",    15, server_content_length },
389    { "Content-MD5:",       12, server_content_md5 },
390    { "Content-Encoding:",  17, server_content_encoding },
391    { "Transfer-Encoding:", 18, server_transfer_coding },
392    { "Keep-Alive:",        11, crumble },
393    { NULL, 0, NULL }
394 };
395
396
397 void (* const add_client_headers[])(struct client_state *) = {
398    client_host_adder,
399    client_cookie_adder,
400    client_x_forwarded_adder,
401    client_xtra_adder,
402    client_accept_encoding_adder,
403    connection_close_adder,
404    NULL
405 };
406
407
408 void (* const add_server_headers[])(struct client_state *) = {
409    connection_close_adder,
410    NULL
411 };
412
413
414 /*********************************************************************
415  *
416  * Function    :  flush_socket
417  *
418  * Description :  Write any pending "buffered" content.
419  *
420  * Parameters  :
421  *          1  :  fd = file descriptor of the socket to read
422  *          2  :  csp = Current client state (buffers, headers, etc...)
423  *
424  * Returns     :  On success, the number of bytes written are returned (zero
425  *                indicates nothing was written).  On error, -1 is returned,
426  *                and errno is set appropriately.  If count is zero and the
427  *                file descriptor refers to a regular file, 0 will be
428  *                returned without causing any other effect.  For a special
429  *                file, the results are not portable.
430  *
431  *********************************************************************/
432 int flush_socket(int fd, struct client_state *csp)
433 {
434    struct iob *iob = csp->iob;
435    int n = iob->eod - iob->cur;
436
437    if (n <= 0)
438    {
439       return(0);
440    }
441
442    n = write_socket(fd, iob->cur, n);
443    iob->eod = iob->cur = iob->buf;
444    return(n);
445
446 }
447
448
449 /*********************************************************************
450  *
451  * Function    :  add_to_iob
452  *
453  * Description :  Add content to the buffered page.
454  *
455  * Parameters  :
456  *          1  :  csp = Current client state (buffers, headers, etc...)
457  *          2  :  buf = holds the content to be added to the page
458  *          3  :  n = number of bytes to be added
459  *
460  * Returns     :  Number of bytes in the content buffer.
461  *
462  *********************************************************************/
463 int add_to_iob(struct client_state *csp, char *buf, int n)
464 {
465    struct iob *iob = csp->iob;
466    int have, need;
467    char *p;
468
469    have = iob->eod - iob->cur;
470
471    if (n <= 0)
472    {
473       return(have);
474    }
475
476    need = have + n;
477
478    if ((p = (char *)malloc(need + 1)) == NULL)
479    {
480       log_error(LOG_LEVEL_ERROR, "malloc() iob failed: %E");
481       return(-1);
482    }
483
484    if (have)
485    {
486       /* there is something in the buffer - save it */
487       memcpy(p, iob->cur, have);
488
489       /* replace the buffer with the new space */
490       freez(iob->buf);
491       iob->buf = p;
492
493       /* point to the end of the data */
494       p += have;
495    }
496    else
497    {
498       /* the buffer is empty, free it and reinitialize */
499       freez(iob->buf);
500       iob->buf = p;
501    }
502
503    /* copy the new data into the iob buffer */
504    memcpy(p, buf, n);
505
506    /* point to the end of the data */
507    p += n;
508
509    /* null terminate == cheap insurance */
510    *p = '\0';
511
512    /* set the pointers to the new values */
513    iob->cur = iob->buf;
514    iob->eod = p;
515
516    return(need);
517
518 }
519
520
521 /*********************************************************************
522  *
523  * Function    :  get_header
524  *
525  * Description :  This (odd) routine will parse the csp->iob
526  *
527  * Parameters  :
528  *          1  :  csp = Current client state (buffers, headers, etc...)
529  *
530  * Returns     :  Any one of the following:
531  *
532  * 1) a pointer to a dynamically allocated string that contains a header line
533  * 2) NULL  indicating that the end of the header was reached
534  * 3) ""    indicating that the end of the iob was reached before finding
535  *          a complete header line.
536  *
537  *********************************************************************/
538 char *get_header(struct client_state *csp)
539 {
540    struct iob *iob;
541    char *p, *q, *ret;
542    iob = csp->iob;
543
544    if ((iob->cur == NULL)
545       || ((p = strchr(iob->cur, '\n')) == NULL))
546    {
547       return(""); /* couldn't find a complete header */
548    }
549
550    *p = '\0';
551
552    ret = strdup(iob->cur);
553
554    iob->cur = p+1;
555
556    if ((q = strchr(ret, '\r'))) *q = '\0';
557
558    /* is this a blank linke (i.e. the end of the header) ? */
559    if (*ret == '\0')
560    {
561       freez(ret);
562       return(NULL);
563    }
564
565    return(ret);
566
567 }
568
569
570 /*********************************************************************
571  *
572  * Function    :  get_header_value
573  *
574  * Description :  Get the value of a given header from a chained list
575  *                of header lines or return NULL if no such header is
576  *                present in the list.
577  *
578  * Parameters  :
579  *          1  :  header_list = pointer to list
580  *          2  :  header_name = string with name of header to look for.
581  *                              Trailing colon required, capitalization
582  *                              doesn't matter.
583  *
584  * Returns     :  NULL if not found, else value of header
585  *
586  *********************************************************************/
587 char *get_header_value(const struct list *header_list, const char *header_name)
588 {
589    struct list_entry *cur_entry;
590    char *ret = NULL;
591    size_t length = 0;
592
593    assert(header_list);
594    assert(header_name);
595    length = strlen(header_name);
596
597    for (cur_entry = header_list->first; cur_entry ; cur_entry = cur_entry->next)
598    {
599       if (cur_entry->str)
600       {
601          if (!strncmpic(cur_entry->str, header_name, length))
602          {
603             /*
604              * Found: return pointer to start of value
605              */
606             ret = (char *) (cur_entry->str + length);
607             while (*ret && ijb_isspace(*ret)) ret++;
608             return(ret);
609          }
610       }
611    }
612
613    /* 
614     * Not found
615     */
616    return NULL;
617
618 }
619
620 /*********************************************************************
621  *
622  * Function    :  sed
623  *
624  * Description :  add, delete or modify lines in the HTTP header streams.
625  *                On entry, it receives a linked list of headers space
626  *                that was allocated dynamically (both the list nodes
627  *                and the header contents).
628  *
629  *                As a side effect it frees the space used by the original
630  *                header lines.
631  *
632  * Parameters  :
633  *          1  :  pats = list of patterns to match against headers
634  *          2  :  more_headers = list of functions to add more
635  *                headers (client or server)
636  *          3  :  csp = Current client state (buffers, headers, etc...)
637  *
638  * Returns     :  Single pointer to a fully formed header, or NULL
639  *                on out-of-memory error.
640  *
641  *********************************************************************/
642 char *sed(const struct parsers pats[], void (* const more_headers[])(struct client_state *), struct client_state *csp)
643 {
644    struct list_entry *p;
645    const struct parsers *v;
646    char *hdr;
647    void (* const *f)();
648
649    for (v = pats; v->str ; v++)
650    {
651       for (p = csp->headers->first; p ; p = p->next)
652       {
653          /* Header crunch()ed in previous run? -> ignore */
654          if (p->str == NULL) continue;
655
656          if (v == pats) log_error(LOG_LEVEL_HEADER, "scan: %s", p->str);
657
658          if (strncmpic(p->str, v->str, v->len) == 0)
659          {
660             hdr = v->parser(v, p->str, csp);
661             freez(p->str); /* FIXME: Yuck! patching a list...*/
662             p->str = hdr;
663          }
664       }
665    }
666
667    /* place any additional headers on the csp->headers list */
668    for (f = more_headers; *f ; f++)
669    {
670       (*f)(csp);
671    }
672
673    hdr = list_to_text(csp->headers);
674
675    return hdr;
676
677 }
678
679
680 /*********************************************************************
681  *
682  * Function    :  free_http_request
683  *
684  * Description :  Freez a http_request structure
685  *
686  * Parameters  :
687  *          1  :  http = points to a http_request structure to free
688  *
689  * Returns     :  N/A
690  *
691  *********************************************************************/
692 void free_http_request(struct http_request *http)
693 {
694    assert(http);
695
696    freez(http->cmd);
697    freez(http->gpc);
698    freez(http->host);
699    freez(http->url);
700    freez(http->hostport);
701    freez(http->path);
702    freez(http->ver);
703    freez(http->host_ip_addr_str);
704
705 }
706
707
708 /*********************************************************************
709  *
710  * Function    :  parse_http_request
711  *
712  * Description :  Parse out the host and port from the URL.  Find the
713  *                hostname & path, port (if ':'), and/or password (if '@')
714  *
715  * Parameters  :
716  *          1  :  req = URL (or is it URI?) to break down
717  *          2  :  http = pointer to the http structure to hold elements
718  *          3  :  csp = Current client state (buffers, headers, etc...)
719  *
720  * Returns     :  N/A
721  *
722  *********************************************************************/
723 void parse_http_request(char *req, struct http_request *http, struct client_state *csp)
724 {
725    char *buf, *v[10], *url, *p;
726    int n;
727
728    memset(http, '\0', sizeof(*http));
729    http->cmd = strdup(req);
730
731    buf = strdup(req);
732    n = ssplit(buf, " \r\n", v, SZ(v), 1, 1);
733
734    if (n == 3)
735    {
736       /* this could be a CONNECT request */
737       if (strcmpic(v[0], "connect") == 0)
738       {
739          http->ssl      = 1;
740          http->gpc      = strdup(v[0]);
741          http->hostport = strdup(v[1]);
742          http->ver      = strdup(v[2]);
743       }
744
745       /* or it could be any other basic HTTP request type */
746       if ((0 == strcmpic(v[0], "get"))
747        || (0 == strcmpic(v[0], "head"))
748        || (0 == strcmpic(v[0], "post"))
749        || (0 == strcmpic(v[0], "put"))
750        || (0 == strcmpic(v[0], "delete"))
751
752        /* or a webDAV extension (RFC2518) */
753        || (0 == strcmpic(v[0], "propfind"))
754        || (0 == strcmpic(v[0], "proppatch"))
755        || (0 == strcmpic(v[0], "move"))
756        || (0 == strcmpic(v[0], "copy"))
757        || (0 == strcmpic(v[0], "mkcol"))
758        || (0 == strcmpic(v[0], "lock"))
759        || (0 == strcmpic(v[0], "unlock"))
760        )
761       {
762          http->ssl    = 0;
763          http->gpc    = strdup(v[0]);
764          http->url    = strdup(v[1]);
765          http->ver    = strdup(v[2]);
766
767          url = v[1];
768          if (strncmpic(url, "http://",  7) == 0)
769          {
770             url += 7;
771          }
772          else if (strncmpic(url, "https://", 8) == 0)
773          {
774             url += 8;
775          }
776          else
777          {
778             url = NULL;
779          }
780
781          if (url)
782          {
783             if ((p = strchr(url, '/')))
784             {
785                http->path = strdup(p);
786                *p = '\0';
787                http->hostport = strdup(url);
788             }
789             /*
790              * Repair broken HTTP requests that don't contain a path
791              */
792             else
793             {
794                http->path = strdup("/");
795                http->hostport = strdup(url);
796             }
797          }
798       }
799    }
800
801    freez(buf);
802
803
804    if (http->hostport == NULL)
805    {
806       free_http_request(http);
807       return;
808    }
809
810    buf = strdup(http->hostport);
811
812
813    /* check if url contains password */
814    n = ssplit(buf, "@", v, SZ(v), 1, 1);
815    if (n == 2)
816    {
817       char * newbuf = NULL;
818       newbuf = strdup(v[1]);
819       freez(buf);
820       buf = newbuf;
821    }
822
823    n = ssplit(buf, ":", v, SZ(v), 1, 1);
824
825    if (n == 1)
826    {
827       http->host = strdup(v[0]);
828       http->port = 80;
829    }
830
831    if (n == 2)
832    {
833       http->host = strdup(v[0]);
834       http->port = atoi(v[1]);
835    }
836
837    freez(buf);
838
839    if (http->host == NULL)
840    {
841       free_http_request(http);
842    }
843
844    if (http->path == NULL)
845    {
846       http->path = strdup("/");
847    }
848
849 }
850
851
852 /* here begins the family of parser functions that reformat header lines */
853
854
855 /*********************************************************************
856  *
857  * Function    :  crumble
858  *
859  * Description :  This is called if a header matches a pattern to "crunch"
860  *
861  * Parameters  :
862  *          1  :  v = Pointer to parsers structure, which basically holds
863  *                headers (client or server) that we want to "crunch"
864  *          2  :  s = header (from sed) to "crunch"
865  *          3  :  csp = Current client state (buffers, headers, etc...)
866  *
867  * Returns     :  Always NULL.
868  *
869  *********************************************************************/
870 char *crumble(const struct parsers *v, const char *s, struct client_state *csp)
871 {
872    log_error(LOG_LEVEL_HEADER, "crunch!");
873    return(NULL);
874
875 }
876
877
878 /*********************************************************************
879  *
880  * Function    :  server_content_type
881  *
882  * Description :  Set the content-type for filterable types (text/.*,
883  *                javascript and image/gif) unless filtering has been
884  *                forbidden (CT_TABOO) while parsing earlier headers.
885  *
886  * Parameters  :
887  *          1  :  v = ignored
888  *          2  :  s = header string we are "considering"
889  *          3  :  csp = Current client state (buffers, headers, etc...)
890  *
891  * Returns     :  A duplicate string pointer to this header (ie. pass thru)
892  *
893  *********************************************************************/
894 char *server_content_type(const struct parsers *v, const char *s, struct client_state *csp)
895 {
896    if (csp->content_type != CT_TABOO)
897    {
898       if (strstr(s, " text/") || strstr(s, "application/x-javascript"))
899          csp->content_type = CT_TEXT;
900       else if (strstr(s, " image/gif"))
901          csp->content_type = CT_GIF;
902       else
903          csp->content_type = 0;
904    }
905
906    return(strdup(s));
907
908 }
909
910
911 /*********************************************************************
912  *
913  * Function    :  server_transfer_coding
914  *
915  * Description :  - Prohibit filtering (CT_TABOO) if transfer coding compresses
916  *                - Raise the CSP_FLAG_CHUNKED flag if coding is "chunked"
917  *                - Change from "chunked" to "identity" if body was chunked
918  *                  but has been de-chunked for filtering.
919  *
920  * Parameters  :
921  *          1  :  v = ignored
922  *          2  :  s = header string we are "considering"
923  *          3  :  csp = Current client state (buffers, headers, etc...)
924  *
925  * Returns     :  A duplicate string pointer to this header (ie. pass thru)
926  *
927  *********************************************************************/
928 char *server_transfer_coding(const struct parsers *v, const char *s, struct client_state *csp)
929 {
930    /*
931     * Turn off pcrs and gif filtering if body compressed
932     */
933    if (strstr(s, "gzip") || strstr(s, "compress") || strstr(s, "deflate"))
934    {
935       csp->content_type = CT_TABOO;
936    }
937
938    /*
939     * Raise flag if body chunked
940     */
941    if (strstr(s, "chunked"))
942    {
943       csp->flags |= CSP_FLAG_CHUNKED;
944
945       /*
946        * If the body was modified, it has been
947        * de-chunked first, so adjust the header:
948        */
949       if (csp->flags & CSP_FLAG_MODIFIED)
950       {
951          return(strdup("Transfer-Encoding: identity"));
952       }
953    }
954
955    return(strdup(s));
956
957 }
958
959
960 /*********************************************************************
961  *
962  * Function    :  server_content_encoding
963  *
964  * Description :  Prohibit filtering (CT_TABOO) if content encoding compresses
965  *
966  * Parameters  :
967  *          1  :  v = ignored
968  *          2  :  s = header string we are "considering"
969  *          3  :  csp = Current client state (buffers, headers, etc...)
970  *
971  * Returns     :  A duplicate string pointer to this header (ie. pass thru)
972  *
973  *********************************************************************/
974 char *server_content_encoding(const struct parsers *v, const char *s, struct client_state *csp)
975 {
976    /*
977     * Turn off pcrs and gif filtering if body compressed
978     */
979    if (strstr(s, "gzip") || strstr(s, "compress") || strstr(s, "deflate"))
980    {
981       csp->content_type = CT_TABOO;
982    }
983
984    return(strdup(s));
985
986 }
987
988
989 /*********************************************************************
990  *
991  * Function    :  server_content_length
992  *
993  * Description :  Adjust Content-Length header if we modified
994  *                the body.
995  *
996  * Parameters  :
997  *          1  :  v = ignored
998  *          2  :  s = header string we are "considering"
999  *          3  :  csp = Current client state (buffers, headers, etc...)
1000  *
1001  * Returns     :  A duplicate string pointer to this header (ie. pass thru)
1002  *
1003  *********************************************************************/
1004 char *server_content_length(const struct parsers *v, const char *s, struct client_state *csp)
1005 {
1006    if (csp->content_length != 0) /* Content length has been modified */
1007    {
1008       char * s2 = (char *) zalloc(100);
1009       sprintf(s2, "Content-Length: %d", (int) csp->content_length);
1010
1011       log_error(LOG_LEVEL_HEADER, "Adjust Content-Length to %d", (int) csp->content_length);
1012       return(s2);
1013    }
1014    else
1015    {
1016       return(strdup(s));
1017    }
1018
1019 }
1020
1021
1022 /*********************************************************************
1023  *
1024  * Function    :  server_content_md5
1025  *
1026  * Description :  Crumble any Content-MD5 headers if the document was
1027  *                modified. FIXME: Should we re-compute instead?
1028  *
1029  * Parameters  :
1030  *          1  :  v = ignored
1031  *          2  :  s = header string we are "considering"
1032  *          3  :  csp = Current client state (buffers, headers, etc...)
1033  *
1034  * Returns     :  A duplicate string pointer to this header (ie. pass thru)
1035  *
1036  *********************************************************************/
1037 char *server_content_md5(const struct parsers *v, const char *s, struct client_state *csp)
1038 {
1039    if (csp->flags & CSP_FLAG_MODIFIED)
1040    {
1041       log_error(LOG_LEVEL_HEADER, "Crunching Content-MD5");
1042       return(NULL);
1043    }
1044    else
1045    {
1046       return(strdup(s));
1047    }
1048
1049 }
1050
1051
1052 /*********************************************************************
1053  *
1054  * Function    :  client_accept_encoding
1055  *
1056  * Description :  Rewrite the client's Accept-Encoding header so that
1057  *                if doesn't allow compression, if the action applies.
1058  *                Note: For HTTP/1.0 the absence of the header is enough.
1059  *
1060  * Parameters  :
1061  *          1  :  v = ignored
1062  *          2  :  s = header string we are "considering"
1063  *          3  :  csp = Current client state (buffers, headers, etc...)
1064  *
1065  * Returns     :  A copy of the client's original or the modified header.
1066  *
1067  *********************************************************************/
1068 char *client_accept_encoding(const struct parsers *v, const char *s, struct client_state *csp)
1069 {
1070    if ((csp->action->flags & ACTION_NO_COMPRESSION) == 0)
1071    {
1072       return(strdup(s));
1073    }
1074    else
1075    {
1076       log_error(LOG_LEVEL_HEADER, "Supressed offer to compress content");
1077
1078       if (!strcmpic(csp->http->ver, "HTTP/1.1"))
1079       {
1080          return(strdup("Accept-Encoding: identity;q=1.0, *;q=0"));
1081       }
1082       else
1083       {
1084          return(NULL);
1085       }
1086    }
1087
1088 }
1089
1090
1091 /*********************************************************************
1092  *
1093  * Function    :  client_te
1094  *
1095  * Description :  Rewrite the client's TE header so that
1096  *                if doesn't allow compression, if the action applies.
1097  *
1098  * Parameters  :
1099  *          1  :  v = ignored
1100  *          2  :  s = header string we are "considering"
1101  *          3  :  csp = Current client state (buffers, headers, etc...)
1102  *
1103  * Returns     :  A copy of the client's original or the modified header.
1104  *
1105  *********************************************************************/
1106 char *client_te(const struct parsers *v, const char *s, struct client_state *csp)
1107 {
1108    if ((csp->action->flags & ACTION_NO_COMPRESSION) == 0)
1109    {
1110       return(strdup(s));
1111    }
1112    else
1113    {
1114       log_error(LOG_LEVEL_HEADER, "Supressed offer to compress transfer");
1115       return(NULL);
1116    }
1117
1118 }
1119
1120 /*********************************************************************
1121  *
1122  * Function    :  client_referrer
1123  *
1124  * Description :  Handle the "referer" config setting properly.
1125  *                Called from `sed'.
1126  *
1127  * Parameters  :
1128  *          1  :  v = ignored
1129  *          2  :  s = header (from sed) to "crunch"
1130  *          3  :  csp = Current client state (buffers, headers, etc...)
1131  *
1132  * Returns     :  NULL if crunched, or a malloc'ed string with the original
1133  *                or modified header
1134  *
1135  *********************************************************************/
1136 char *client_referrer(const struct parsers *v, const char *s, struct client_state *csp)
1137 {
1138    const char * newval;
1139    char * s2;
1140 #ifdef FEATURE_FORCE_LOAD
1141    /* Since the referrer can include the prefix even
1142     * even if the request itself is non-forced, we must
1143     * clean it unconditionally
1144     */
1145    strclean(s, FORCE_PREFIX);
1146 #endif /* def FEATURE_FORCE_LOAD */
1147
1148    /*
1149     * Are we sending referer?
1150     */
1151    if ((csp->action->flags & ACTION_HIDE_REFERER) == 0)
1152    {
1153       return(strdup(s));
1154    }
1155
1156    newval = csp->action->string[ACTION_STRING_REFERER];
1157
1158    /*
1159     * Are we blocking referer?
1160     */
1161    if ((newval == NULL) || (0 == strcmpic(newval, "block")) )
1162    {
1163       log_error(LOG_LEVEL_HEADER, "crunch!");
1164       return(NULL);
1165    }
1166
1167    /*
1168     * Are we forging referer?
1169     */
1170    if (0 == strcmpic(newval, "forge"))
1171    {
1172       /*
1173        * Forge a referer as http://[hostname:port of REQUEST]/
1174        * to fool stupid checks for in-site links
1175        */
1176       log_error(LOG_LEVEL_HEADER, "crunch+forge!");
1177       s2 = strsav(NULL, "Referer: ");
1178       s2 = strsav(s2, "http://");
1179       s2 = strsav(s2, csp->http->hostport);
1180       s2 = strsav(s2, "/");
1181       return(s2);
1182    }
1183
1184    /*
1185     * Have we got a fixed referer?
1186     */
1187    if (0 == strncmpic(newval, "http://", 7))
1188    {
1189       /*
1190        * We have a specific (fixed) referer we want to send.
1191        */
1192       char * s3;
1193
1194       log_error(LOG_LEVEL_HEADER, "modified");
1195
1196       s3 = strsav( NULL, "Referer: " );
1197       s3 = strsav( s3, newval );
1198       return(s3);
1199    }
1200
1201    /* Should never get here! */
1202    log_error(LOG_LEVEL_ERROR, "Bad parameter: +referer{%s}", newval);
1203
1204    /*
1205     * Forge is probably the best default.
1206     *
1207     * Forge a referer as http://[hostname:port of REQUEST]/
1208     * to fool stupid checks for in-site links
1209     */
1210    log_error(LOG_LEVEL_HEADER, "crunch+forge!");
1211    s2 = strsav(NULL, "Referer: ");
1212    s2 = strsav(s2, "http://");
1213    s2 = strsav(s2, csp->http->hostport);
1214    s2 = strsav(s2, "/");
1215    return(s2);
1216 }
1217
1218
1219 /*********************************************************************
1220  *
1221  * Function    :  client_uagent
1222  *
1223  * Description :  Handle the "user-agent" config setting properly
1224  *                and remember its original value to enable browser
1225  *                bug workarounds. Called from `sed'.
1226  *
1227  * Parameters  :
1228  *          1  :  v = ignored
1229  *          2  :  s = header (from sed) to "crunch"
1230  *          3  :  csp = Current client state (buffers, headers, etc...)
1231  *
1232  * Returns     :  A malloc'ed pointer to the default agent, or
1233  *                a malloc'ed string pointer to this header (ie. pass thru).
1234  *
1235  *********************************************************************/
1236 char *client_uagent(const struct parsers *v, const char *s, struct client_state *csp)
1237 {
1238    const char * newval;
1239    char * s2;
1240
1241    if ((csp->action->flags & ACTION_HIDE_USER_AGENT) == 0)
1242    {
1243       return(strdup(s));
1244    }
1245
1246    newval = csp->action->string[ACTION_STRING_USER_AGENT];
1247    if (newval == NULL)
1248    {
1249       return(strdup(s));
1250    }
1251
1252    log_error(LOG_LEVEL_HEADER, "modified");
1253
1254    s2 = strsav( NULL, "User-Agent: " );
1255    s2 = strsav( s2, newval );
1256    return(s2);
1257
1258 }
1259
1260
1261 /*********************************************************************
1262  *
1263  * Function    :  client_ua
1264  *
1265  * Description :  Handle "ua-" headers properly.  Called from `sed'.
1266  *
1267  * Parameters  :
1268  *          1  :  v = ignored
1269  *          2  :  s = header (from sed) to "crunch"
1270  *          3  :  csp = Current client state (buffers, headers, etc...)
1271  *
1272  * Returns     :  NULL if crunched, or a malloc'ed string to original header
1273  *
1274  *********************************************************************/
1275 char *client_ua(const struct parsers *v, const char *s, struct client_state *csp)
1276 {
1277    if ((csp->action->flags & ACTION_HIDE_USER_AGENT) == 0)
1278    {
1279       return(strdup(s));
1280    }
1281    else
1282    {
1283       log_error(LOG_LEVEL_HEADER, "crunch!");
1284       return(NULL);
1285    }
1286 }
1287
1288
1289 /*********************************************************************
1290  *
1291  * Function    :  client_from
1292  *
1293  * Description :  Handle the "from" config setting properly.
1294  *                Called from `sed'.
1295  *
1296  * Parameters  :
1297  *          1  :  v = ignored
1298  *          2  :  s = header (from sed) to "crunch"
1299  *          3  :  csp = Current client state (buffers, headers, etc...)
1300  *
1301  * Returns     :  NULL if crunched, or a malloc'ed string to
1302  *                modified/original header.
1303  *
1304  *********************************************************************/
1305 char *client_from(const struct parsers *v, const char *s, struct client_state *csp)
1306 {
1307    const char * newval;
1308    char * s2;
1309
1310    if ((csp->action->flags & ACTION_HIDE_FROM) == 0)
1311    {
1312       return(strdup(s));
1313    }
1314
1315    newval = csp->action->string[ACTION_STRING_FROM];
1316
1317    /*
1318     * Are we blocking referer?
1319     */
1320    if ((newval == NULL) || (0 == strcmpic(newval, "block")) )
1321    {
1322       log_error(LOG_LEVEL_HEADER, "crunch!");
1323       return(NULL);
1324    }
1325
1326    log_error(LOG_LEVEL_HEADER, " modified");
1327
1328    s2 = strsav( NULL, "From: " );
1329    s2 = strsav( s2, newval );
1330    return(s2);
1331
1332 }
1333
1334
1335 /*********************************************************************
1336  *
1337  * Function    :  client_send_cookie
1338  *
1339  * Description :  Handle the "cookie" header properly.  Called from `sed'.
1340  *                If cookie is accepted, add it to the cookie_list,
1341  *                else we crunch it.  Mmmmmmmmmmm ... cookie ......
1342  *
1343  * Parameters  :
1344  *          1  :  v = pattern of cookie `sed' found matching
1345  *          2  :  s = header (from sed) to "crunch"
1346  *          3  :  csp = Current client state (buffers, headers, etc...)
1347  *
1348  * Returns     :  Always NULL.
1349  *
1350  *********************************************************************/
1351 char *client_send_cookie(const struct parsers *v, const char *s, struct client_state *csp)
1352 {
1353    if ((csp->action->flags & ACTION_NO_COOKIE_READ) == 0)
1354    {
1355       enlist(csp->cookie_list, s + v->len + 1);
1356    }
1357    else
1358    {
1359       log_error(LOG_LEVEL_HEADER, " crunch!");
1360    }
1361
1362    /*
1363     * Always return NULL here.  The cookie header
1364     * will be sent at the end of the header.
1365     */
1366    return(NULL);
1367
1368 }
1369
1370
1371 /*********************************************************************
1372  *
1373  * Function    :  client_x_forwarded
1374  *
1375  * Description :  Handle the "x-forwarded-for" config setting properly,
1376  *                also used in the add_client_headers list.  Called from `sed'.
1377  *
1378  * Parameters  :
1379  *          1  :  v = ignored
1380  *          2  :  s = header (from sed) to "crunch"
1381  *          3  :  csp = Current client state (buffers, headers, etc...)
1382  *
1383  * Returns     :  Always NULL.
1384  *
1385  *********************************************************************/
1386 char *client_x_forwarded(const struct parsers *v, const char *s, struct client_state *csp)
1387 {
1388    if ((csp->action->flags & ACTION_HIDE_FORWARDED) == 0)
1389    {
1390       /* Save it so we can re-add it later */
1391       csp->x_forwarded = strdup(s);
1392    }
1393
1394    /*
1395     * Always return NULL, since this information
1396     * will be sent at the end of the header.
1397     */
1398
1399    return(NULL);
1400
1401 }
1402
1403 /* the following functions add headers directly to the header list */
1404
1405 /*********************************************************************
1406  *
1407  * Function    :  client_host_adder
1408  *
1409  * Description :  (re)adds the host header. Called from `sed'.
1410  *
1411  * Parameters  :
1412  *          1  :  csp = Current client state (buffers, headers, etc...)
1413  *
1414  * Returns     :  N/A
1415  *
1416  *********************************************************************/
1417 void client_host_adder(struct client_state *csp)
1418 {
1419    char *p = NULL,
1420         *pos = NULL;
1421
1422    if ( !csp->http->hostport || !*(csp->http->hostport)) return;
1423    p = strsav(p, "Host: ");
1424    /*
1425    ** remove 'user:pass@' from 'proto://user:pass@host'
1426    */
1427    if ( (pos = strchr( csp->http->hostport, '@')) != NULL )
1428    {
1429        p = strsav(p, pos+1);
1430    }
1431    else
1432    {
1433       p = strsav(p, csp->http->hostport);
1434    }
1435    log_error(LOG_LEVEL_HEADER, "addh: %s", p);
1436    enlist(csp->headers, p);
1437
1438    freez(p);
1439 }
1440
1441
1442 /*********************************************************************
1443  *
1444  * Function    :  client_cookie_adder
1445  *
1446  * Description :  Used in the add_client_headers list.  Called from `sed'.
1447  *
1448  * Parameters  :
1449  *          1  :  csp = Current client state (buffers, headers, etc...)
1450  *
1451  * Returns     :  N/A
1452  *
1453  *********************************************************************/
1454 void client_cookie_adder(struct client_state *csp)
1455 {
1456    struct list_entry *lst;
1457    char *tmp = NULL;
1458    char *e;
1459
1460    for (lst = csp->cookie_list->first; lst ; lst = lst->next)
1461    {
1462       if (tmp)
1463       {
1464          tmp = strsav(tmp, "; ");
1465       }
1466       tmp = strsav(tmp, lst->str);
1467    }
1468
1469    for (lst = csp->action->multi[ACTION_MULTI_WAFER]->first;  lst ; lst = lst->next)
1470    {
1471       if (tmp)
1472       {
1473          tmp = strsav(tmp, "; ");
1474       }
1475
1476       if ((e = cookie_encode(lst->str)))
1477       {
1478          tmp = strsav(tmp, e);
1479          freez(e);
1480       }
1481    }
1482
1483    if (tmp)
1484    {
1485       char *ret;
1486
1487       ret = strdup("Cookie: ");
1488       ret = strsav(ret, tmp);
1489       log_error(LOG_LEVEL_HEADER, "addh: %s", ret);
1490       enlist(csp->headers, ret);
1491       freez(tmp);
1492       freez(ret);
1493    }
1494
1495 }
1496
1497
1498 /*********************************************************************
1499  *
1500  * Function    :  client_accept_encoding_adder
1501  *
1502  * Description :  Add an Accept-Encoding header to the client's request
1503  *                that disables compression if the action applies, and
1504  *                the header is not already there. Called from `sed'.
1505  *                Note: For HTTP/1.0, the absence of the header is enough.
1506  *
1507  * Parameters  :
1508  *          1  :  csp = Current client state (buffers, headers, etc...)
1509  *
1510  * Returns     :  N/A
1511  *
1512  *********************************************************************/
1513 void client_accept_encoding_adder(struct client_state *csp)
1514 {
1515    if (   ((csp->action->flags & ACTION_NO_COMPRESSION) != 0)
1516        && (!strcmpic(csp->http->ver, "HTTP/1.1")) )
1517    {
1518       enlist_unique(csp->headers, "Accept-Encoding: identity;q=1.0, *;q=0", 16);
1519    }
1520
1521 }
1522
1523
1524 /*********************************************************************
1525  *
1526  * Function    :  client_xtra_adder
1527  *
1528  * Description :  Used in the add_client_headers list.  Called from `sed'.
1529  *
1530  * Parameters  :
1531  *          1  :  csp = Current client state (buffers, headers, etc...)
1532  *
1533  * Returns     :  N/A
1534  *
1535  *********************************************************************/
1536 void client_xtra_adder(struct client_state *csp)
1537 {
1538    struct list_entry *lst;
1539
1540    for (lst = csp->action->multi[ACTION_MULTI_ADD_HEADER]->first;
1541         lst ; lst = lst->next)
1542    {
1543       log_error(LOG_LEVEL_HEADER, "addh: %s", lst->str);
1544       enlist(csp->headers, lst->str);
1545    }
1546
1547 }
1548
1549
1550 /*********************************************************************
1551  *
1552  * Function    :  client_x_forwarded_adder
1553  *
1554  * Description :  Used in the add_client_headers list.  Called from `sed'.
1555  *
1556  * Parameters  :
1557  *          1  :  csp = Current client state (buffers, headers, etc...)
1558  *
1559  * Returns     :  N/A
1560  *
1561  *********************************************************************/
1562 void client_x_forwarded_adder(struct client_state *csp)
1563 {
1564    char *p = NULL;
1565
1566    if ((csp->action->flags & ACTION_HIDE_FORWARDED) != 0)
1567    {
1568       return;
1569    }
1570
1571    if (csp->x_forwarded)
1572    {
1573       p = strsav(p, csp->x_forwarded);
1574       p = strsav(p, ", ");
1575       p = strsav(p, csp->ip_addr_str);
1576    }
1577    else
1578    {
1579       p = strsav(p, "X-Forwarded-For: ");
1580       p = strsav(p, csp->ip_addr_str);
1581    }
1582
1583    log_error(LOG_LEVEL_HEADER, "addh: %s", p);
1584    enlist(csp->headers, p);
1585
1586    freez(p);
1587 }
1588
1589
1590 /*********************************************************************
1591  *
1592  * Function    :  connection_close_adder
1593  *
1594  * Description :  Adds a "Connection: close" header to csp->headers
1595  *                as a temporary fix for the needed but missing HTTP/1.1
1596  *                support. Called from `sed'.
1597  *                FIXME: This whole function shouldn't be neccessary!
1598  *
1599  * Parameters  :
1600  *          1  :  csp = Current client state (buffers, headers, etc...)
1601  *
1602  * Returns     :  N/A
1603  *
1604  *********************************************************************/
1605 void connection_close_adder(struct client_state *csp)
1606 {
1607    enlist(csp->headers, "Connection: close");
1608 }
1609
1610
1611 /*********************************************************************
1612  *
1613  * Function    :  server_http
1614  *
1615  * Description :  - Save the HTTP Status into csp->http->status
1616  *                - Set CT_TABOO to prevent filtering if the answer
1617  *                  is a partial range (HTTP status 206)
1618  *                - Rewrite HTTP/1.1 answers to HTTP/1.0 if +downgrade
1619  *                  action applies.
1620  *
1621  * Parameters  :
1622  *          1  :  v = parser pattern that matched this header
1623  *          2  :  s = header that matched this pattern
1624  *          3  :  csp = Current client state (buffers, headers, etc...)
1625  *
1626  * Returns     :  Copy of changed  or original answer.
1627  *
1628  *********************************************************************/
1629 char *server_http(const struct parsers *v, const char *s, struct client_state *csp)
1630 {
1631    char *ret = strdup(s);
1632
1633    sscanf(ret, "HTTP/%*d.%*d %d", &(csp->http->status));
1634    if (csp->http->status == 206)
1635    {
1636       csp->content_type = CT_TABOO;
1637    }
1638
1639    if ((csp->action->flags & ACTION_DOWNGRADE) != 0)
1640    {
1641       ret[7] = '0';
1642       log_error(LOG_LEVEL_HEADER, "Downgraded answer to HTTP/1.0");
1643    }
1644    return(ret);
1645
1646 }
1647
1648
1649 /*********************************************************************
1650  *
1651  * Function    :  server_set_cookie
1652  *
1653  * Description :  Handle the server "cookie" header properly.
1654  *                Log cookie to the jar file.  Then "crunch" it,
1655  *                or accept it.  Called from `sed'.
1656  *
1657  * Parameters  :
1658  *          1  :  v = parser pattern that matched this header
1659  *          2  :  s = header that matched this pattern
1660  *          3  :  csp = Current client state (buffers, headers, etc...)
1661  *
1662  * Returns     :  `crumble' or a newly malloc'ed string.
1663  *
1664  *********************************************************************/
1665 char *server_set_cookie(const struct parsers *v, const char *s, struct client_state *csp)
1666 {
1667 #ifdef FEATURE_COOKIE_JAR
1668    if (csp->config->jar)
1669    {
1670       /*
1671        * Write timestamp into outbuf.
1672        *
1673        * Complex because not all OSs have tm_gmtoff or
1674        * the %z field in strftime()
1675        */
1676       char tempbuf[ BUFFER_SIZE ];
1677       time_t now; 
1678       struct tm *tm_now; 
1679       time (&now); 
1680       tm_now = localtime (&now); 
1681       strftime (tempbuf, BUFFER_SIZE-6, "%b %d %H:%M:%S ", tm_now); 
1682
1683       fprintf(csp->config->jar, "%s %s\t%s\n", tempbuf, csp->http->host, (s + v->len + 1));
1684    }
1685 #endif /* def FEATURE_COOKIE_JAR */
1686
1687    if ((csp->action->flags & ACTION_NO_COOKIE_SET) != 0)
1688    {
1689       return(crumble(v, s, csp));
1690    }
1691    else if ((csp->action->flags & ACTION_NO_COOKIE_KEEP) != 0)
1692    {
1693       /* Flag whether or not to log a message */
1694       int changed = 0;
1695
1696       /* A variable to store the tag we're working on */
1697       char * cur_tag;
1698
1699       /* Make a copy of the header we can write to */
1700       char * result = strdup(s);
1701       if (result == NULL)
1702       {
1703          /* FIXME: This error handling is incorrect */
1704          return NULL;
1705       }
1706
1707       /* Skip "Set-Cookie:" (11 characters) in header */
1708       cur_tag = result + 11;
1709
1710       /* skip whitespace between "Set-Cookie:" and value */
1711       while (*cur_tag && ijb_isspace(*cur_tag))
1712       {
1713          cur_tag++;
1714       }
1715
1716       /* Loop through each tag in the cookie */
1717       while (*cur_tag)
1718       {
1719          /* Find next tag */
1720          char * next_tag = strchr(cur_tag, ';');
1721          if (next_tag != NULL)
1722          {
1723             /* Skip the ';' character itself */
1724             next_tag++;
1725
1726             /* skip whitespace ";" and start of tag */
1727             while (*next_tag && ijb_isspace(*next_tag))
1728             {
1729                next_tag++;
1730             }
1731          }
1732          else
1733          {
1734             /* "Next tag" is the end of the string */
1735             next_tag = cur_tag + strlen(cur_tag);
1736          }
1737
1738          /* Is this the "Expires" tag? */
1739          if (strncmpic(cur_tag, "expires=", 8) == 0)
1740          {
1741             /* Delete the tag by copying the rest of the string over it.
1742              * (Note that we cannot just use "strcpy(cur_tag, next_tag)",
1743              * since the behaviour of strcpy is undefined for overlapping
1744              * strings.)
1745              */
1746             memmove(cur_tag, next_tag, strlen(next_tag) + 1);
1747
1748             /* That changed the header, need to issue a log message */
1749             changed = 1;
1750
1751             /* Note that the next tag has now been moved to *cur_tag,
1752              * so we do not need to update the cur_tag pointer.
1753              */
1754          }
1755          else
1756          {
1757             /* Move on to next cookie tag */
1758             cur_tag = next_tag;
1759          }
1760       }
1761
1762       if (changed)
1763       {
1764          log_error(LOG_LEVEL_HEADER, "Changed cookie to a temporary one.");
1765       }
1766
1767       return result;
1768    }
1769
1770    return(strdup(s));
1771 }
1772
1773
1774 #ifdef FEATURE_FORCE_LOAD
1775 /*********************************************************************
1776  *
1777  * Function    :  strclean
1778  *
1779  * Description :  In-Situ-Eliminate all occurances of substring in
1780  *                string
1781  *
1782  * Parameters  :
1783  *          1  :  string = string to clean
1784  *          2  :  substring = substring to eliminate
1785  *
1786  * Returns     :  Number of eliminations
1787  *
1788  *********************************************************************/
1789 int strclean(const char *string, const char *substring)
1790 {
1791    int hits = 0, len = strlen(substring);
1792    char *pos, *p;
1793
1794    while((pos = strstr(string, substring)))
1795    {
1796       p = pos + len;
1797       do
1798       {
1799          *(p - len) = *p;
1800       }
1801       while (*p++ != '\0');
1802
1803       hits++;
1804    }
1805
1806    return(hits);
1807 }
1808 #endif /* def FEATURE_FORCE_LOAD */
1809
1810
1811 /*
1812   Local Variables:
1813   tab-width: 3
1814   end:
1815 */