*** empty log message ***
[privoxy.git] / parsers.c
1 const char parsers_rcs[] = "$Id: parsers.c,v 1.1 2001/05/13 21:57:06 administrator Exp $";
2 /*********************************************************************
3  *
4  * File        :  $Source: /home/administrator/cvs/ijb/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', `match', `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  *
45  *********************************************************************/
46 \f
47
48 #include "config.h"
49
50 #include <stdio.h>
51 #include <sys/types.h>
52 #include <stdlib.h>
53 #include <ctype.h>
54 #include <string.h>
55
56 #ifndef _WIN32
57 #include <unistd.h>
58 #endif
59
60 #include "project.h"
61 #include "parsers.h"
62 #include "encode.h"
63 #include "filters.h"
64 #include "loaders.h"
65 #include "showargs.h"
66 #include "jcc.h"
67 #include "ssplit.h"
68 #include "errlog.h"
69 #include "jbsockets.h"
70 #include "miscutil.h"
71
72 const char parsers_h_rcs[] = PARSERS_H_VERSION;
73
74 /* Fix a problem with Solaris.  There should be no effect on other
75  * platforms.
76  * Solaris's isspace() is a macro which uses it's argument directly
77  * as an array index.  Therefore we need to make sure that high-bit
78  * characters generate +ve values, and ideally we also want to make
79  * the argument match the declared parameter type of "int".
80  * 
81  * Why did they write a character function that can't take a simple 
82  * "char" argument?  Doh!
83  */
84 #define ijb_isupper(__X) isupper((int)(unsigned char)(__X))
85 #define ijb_tolower(__X) tolower((int)(unsigned char)(__X))
86
87
88 const struct parsers client_patterns[] = {
89    { "referer:",                 8,    client_referrer },
90    { "user-agent:",              11,   client_uagent },
91    { "ua-",                      3,    client_ua },
92    { "from:",                    5,    client_from },
93    { "cookie:",                  7,    client_send_cookie },
94    { "x-forwarded-for:",         16,   client_x_forwarded },
95    { "proxy-connection:",        17,   crumble },
96 #ifdef DENY_GZIP
97    { "Accept-Encoding: gzip",    21,   crumble },
98 #endif /* def DENY_GZIP */
99 #if defined(DETECT_MSIE_IMAGES)
100    { "Accept:",                   7,   client_accept },
101 #endif /* defined(DETECT_MSIE_IMAGES) */
102 #ifdef FORCE_LOAD
103    { "Host:",                     5,   client_host },
104 #endif /* def FORCE_LOAD */
105 /* { "if-modified-since:",       18,   crumble }, */
106    { NULL,                       0,    NULL }
107 };
108
109 const struct interceptors intercept_patterns[] = {
110    { "show-proxy-args",    14, show_proxy_args },
111 #ifdef TRUST_FILES
112    { "ij-untrusted-url",   14, ij_untrusted_url },
113 #endif /* def TRUST_FILES */
114    { NULL, 0, NULL }
115 };
116
117 const struct parsers server_patterns[] = {
118    { "set-cookie:",        11, server_set_cookie },
119    { "connection:",        11, crumble },
120 #ifdef PCRS
121    { "Content-Type:",      13, content_type },
122    { "Content-Length:",    15, crumble },
123 #endif /* def PCRS */
124    { NULL, 0, NULL }
125 };
126
127
128 void (* const add_client_headers[])(struct client_state *) = {
129    client_cookie_adder,
130    client_x_forwarded_adder,
131    client_xtra_adder,
132    NULL
133 };
134
135
136 void (* const add_server_headers[])(struct client_state *) = {
137    NULL
138 };
139
140
141 /*********************************************************************
142  *
143  * Function    :  match
144  *
145  * Description :  Do a `strncmpic' on every pattern in pats.
146  *
147  * Parameters  :
148  *          1  :  buf = a string to match to a list of patterns
149  *          2  :  pats = list of strings to compare against buf.
150  *
151  * Returns     :  Return the matching "struct parsers *",
152  *                or NULL if no pattern matches.
153  *
154  *********************************************************************/
155 static const struct parsers *match(char *buf, const struct parsers *pats)
156 {
157    const struct parsers *v;
158
159    if (buf == NULL)
160    {
161       /* hit me */
162       log_error(LOG_LEVEL_ERROR, "NULL parameter to match()");
163       return(NULL);
164    }
165
166    for (v = pats; v->str ; v++)
167    {
168       if (strncmpic(buf, v->str, v->len) == 0)
169       {
170          return(v);
171       }
172    }
173    return(NULL);
174
175 }
176
177
178 /*********************************************************************
179  *
180  * Function    :  flush_socket
181  *
182  * Description :  Write any pending "buffered" content.
183  *
184  * Parameters  :
185  *          1  :  fd = file descriptor of the socket to read
186  *          2  :  csp = Current client state (buffers, headers, etc...)
187  *
188  * Returns     :  On success, the number of bytes written are returned (zero
189  *                indicates nothing was written).  On error, -1 is returned,
190  *                and errno is set appropriately.  If count is zero and the
191  *                file descriptor refers to a regular file, 0 will be
192  *                returned without causing any other effect.  For a special
193  *                file, the results are not portable.
194  *
195  *********************************************************************/
196 int flush_socket(int fd, struct client_state *csp)
197 {
198    struct iob *iob = csp->iob;
199    int n = iob->eod - iob->cur;
200
201    if (n <= 0)
202    {
203       return(0);
204    }
205
206    n = write_socket(fd, iob->cur, n);
207    iob->eod = iob->cur = iob->buf;
208    return(n);
209
210 }
211
212
213 /*********************************************************************
214  *
215  * Function    :  add_to_iob
216  *
217  * Description :  Add content to the buffered page.
218  *
219  * Parameters  :
220  *          1  :  csp = Current client state (buffers, headers, etc...)
221  *          2  :  buf = holds the content to be added to the page
222  *          3  :  n = number of bytes to be added
223  *
224  * Returns     :  Number of bytes in the content buffer.
225  *
226  *********************************************************************/
227 int add_to_iob(struct client_state *csp, char *buf, int n)
228 {
229    struct iob *iob = csp->iob;
230    int have, need;
231    char *p;
232
233    have = iob->eod - iob->cur;
234
235    if (n <= 0)
236    {
237       return(have);
238    }
239
240    need = have + n;
241
242    if ((p = (char *)malloc(need + 1)) == NULL)
243    {
244       log_error(LOG_LEVEL_ERROR, "malloc() iob failed: %E");
245       return(-1);
246    }
247
248    if (have)
249    {
250       /* there is something in the buffer - save it */
251       memcpy(p, iob->cur, have);
252
253       /* replace the buffer with the new space */
254       freez(iob->buf);
255       iob->buf = p;
256
257       /* point to the end of the data */
258       p += have;
259    }
260    else
261    {
262       /* the buffer is empty, free it and reinitialize */
263       freez(iob->buf);
264       iob->buf = p;
265    }
266
267    /* copy the new data into the iob buffer */
268    memcpy(p, buf, n);
269
270    /* point to the end of the data */
271    p += n;
272
273    /* null terminate == cheap insurance */
274    *p = '\0';
275
276    /* set the pointers to the new values */
277    iob->cur = iob->buf;
278    iob->eod = p;
279
280    return(need);
281
282 }
283
284
285 /*********************************************************************
286  *
287  * Function    :  get_header
288  *
289  * Description :  This (odd) routine will parse the csp->iob
290  *
291  * Parameters  :
292  *          1  :  csp = Current client state (buffers, headers, etc...)
293  *
294  * Returns     :  Any one of the following:
295  *
296  * 1) a pointer to a dynamically allocated string that contains a header line
297  * 2) NULL  indicating that the end of the header was reached
298  * 3) ""    indicating that the end of the iob was reached before finding
299  *          a complete header line.
300  *
301  *********************************************************************/
302 char *get_header(struct client_state *csp)
303 {
304    struct iob *iob;
305    char *p, *q, *ret;
306    iob = csp->iob;
307
308    if ((iob->cur == NULL)
309       || ((p = strchr(iob->cur, '\n')) == NULL))
310    {
311       return(""); /* couldn't find a complete header */
312    }
313
314    *p = '\0';
315
316    ret = strdup(iob->cur);
317
318    iob->cur = p+1;
319
320    if ((q = strchr(ret, '\r'))) *q = '\0';
321
322    /* is this a blank linke (i.e. the end of the header) ? */
323    if (*ret == '\0')
324    {
325       freez(ret);
326       return(NULL);
327    }
328
329    return(ret);
330
331 }
332
333
334 /*********************************************************************
335  *
336  * Function    :  enlist
337  *
338  * Description :  Append a string into a specified string list.
339  *
340  * Parameters  :
341  *          1  :  h = pointer to list 'dummy' header
342  *          2  :  s = string to add to the list
343  *
344  * Returns     :  N/A
345  *
346  *********************************************************************/
347 void enlist(struct list *h, const char *s)
348 {
349    struct list *n = (struct list *)malloc(sizeof(*n));
350    struct list *l;
351
352    if (n)
353    {
354       n->str  = strdup(s);
355       n->next = NULL;
356
357       if ((l = h->last))
358       {
359          l->next = n;
360       }
361       else
362       {
363          h->next = n;
364       }
365
366       h->last = n;
367    }
368
369 }
370
371
372 /*********************************************************************
373  *
374  * Function    :  destroy_list
375  *
376  * Description :  Destroy a string list (opposite of enlist)
377  *
378  * Parameters  :
379  *          1  :  h = pointer to list 'dummy' header
380  *
381  * Returns     :  N/A
382  *
383  *********************************************************************/
384 void destroy_list(struct list *h)
385 {
386    struct list *p, *n;
387
388    for (p = h->next; p ; p = n)
389    {
390       n = p->next;
391       freez(p->str);
392       freez(p);
393    }
394
395    memset(h, '\0', sizeof(*h));
396
397 }
398
399
400 /*********************************************************************
401  *
402  * Function    :  list_to_text
403  *
404  * Description :  "Flaten" a string list into 1 long \r\n delimited string.
405  *
406  * Parameters  :
407  *          1  :  h = pointer to list 'dummy' header
408  *
409  * Returns     :  NULL on malloc error, else new long string.
410  *
411  *********************************************************************/
412 static char *list_to_text(struct list *h)
413 {
414    struct list *p;
415    char *ret = NULL;
416    char *s;
417    int size;
418
419    size = 0;
420
421    for (p = h->next; p ; p = p->next)
422    {
423       if (p->str)
424       {
425          size += strlen(p->str) + 2;
426       }
427    }
428
429    if ((ret = (char *)malloc(size + 1)) == NULL)
430    {
431       return(NULL);
432    }
433
434    ret[size] = '\0';
435
436    s = ret;
437
438    for (p = h->next; p ; p = p->next)
439    {
440       if (p->str)
441       {
442          strcpy(s, p->str);
443          s += strlen(s);
444          *s++ = '\r'; *s++ = '\n';
445       }
446    }
447
448    return(ret);
449
450 }
451
452
453 /*********************************************************************
454  *
455  * Function    :  sed
456  *
457  * Description :  add, delete or modify lines in the HTTP header streams.
458  *                On entry, it receives a linked list of headers space
459  *                that was allocated dynamically (both the list nodes
460  *                and the header contents).
461  *
462  *                As a side effect it frees the space used by the original
463  *                header lines.
464  *
465  * Parameters  :
466  *          1  :  pats = list of patterns to match against headers
467  *          2  :  more_headers = list of functions to add more
468  *                headers (client or server)
469  *          3  :  csp = Current client state (buffers, headers, etc...)
470  *
471  * Returns     :  Single pointer to a fully formed header.
472  *
473  *********************************************************************/
474 char *sed(const struct parsers pats[], void (* const more_headers[])(struct client_state *), struct client_state *csp)
475 {
476    struct list *p;
477    const struct parsers *v;
478    char *hdr;
479    void (* const *f)();
480
481    for (p = csp->headers->next; p ; p = p->next)
482    {
483       log_error(LOG_LEVEL_HEADER, "scan: %s", p->str);
484
485       if ((v = match(p->str, pats)))
486       {
487          hdr = v->parser(v, p->str, csp);
488          freez(p->str);
489          p->str = hdr;
490       }
491
492    }
493
494    /* place any additional headers on the csp->headers list */
495    for (f = more_headers; *f ; f++)
496    {
497       (*f)(csp);
498    }
499
500    /* add the blank line at the end of the header */
501    enlist(csp->headers, "");
502
503    hdr = list_to_text(csp->headers);
504
505    return(hdr);
506
507 }
508
509
510 /*********************************************************************
511  *
512  * Function    :  free_http_request
513  *
514  * Description :  Freez a http_request structure
515  *
516  * Parameters  :
517  *          1  :  http = points to a http_request structure to free
518  *
519  * Returns     :  N/A
520  *
521  *********************************************************************/
522 void free_http_request(struct http_request *http)
523 {
524    freez(http->cmd);
525    freez(http->gpc);
526    freez(http->host);
527    freez(http->hostport);
528    freez(http->path);
529    freez(http->ver);
530
531 }
532
533
534 /*********************************************************************
535  *
536  * Function    :  parse_http_request
537  *
538  * Description :  Parse out the host and port from the URL.  Find the
539  *                hostname & path, port (if ':'), and/or password (if '@')
540  *
541  * Parameters  :
542  *          1  :  req = URL (or is it URI?) to break down
543  *          2  :  http = pointer to the http structure to hold elements
544  *          3  :  csp = Current client state (buffers, headers, etc...)
545  *
546  * Returns     :  N/A
547  *
548  *********************************************************************/
549 void parse_http_request(char *req, struct http_request *http, struct client_state *csp)
550 {
551    char *buf, *v[10], *url, *p;
552    int n;
553
554    memset(http, '\0', sizeof(*http));
555
556    http->cmd = strdup(req);
557
558    buf = strdup(req);
559
560    n = ssplit(buf, " \r\n", v, SZ(v), 1, 1);
561
562    if (n == 3)
563    {
564       /* this could be a CONNECT request */
565       if (strcmpic(v[0], "connect") == 0)
566       {
567          http->ssl      = 1;
568          http->gpc      = strdup(v[0]);
569          http->hostport = strdup(v[1]);
570          http->ver      = strdup(v[2]);
571       }
572
573 #ifdef WEBDAV
574
575 /* This next line is a little ugly, but it simplifies the if statement below. */
576 /* Basically if using webDAV, we want the OR condition to use these too.      */
577
578 /*
579  * by haroon
580  * These are the headers as defined in RFC2518 to add webDAV support
581  */
582
583 #define OR_WEBDAV || \
584          (0 == strcmpic(v[0], "propfind")) || \
585          (0 == strcmpic(v[0], "proppatch")) || \
586          (0 == strcmpic(v[0], "move")) || \
587          (0 == strcmpic(v[0], "copy")) || \
588          (0 == strcmpic(v[0], "mkcol")) || \
589          (0 == strcmpic(v[0], "lock")) || \
590          (0 == strcmpic(v[0], "unlock"))
591
592 #else /* No webDAV support is enabled.  Provide an empty OR_WEBDAV macro. */
593
594 #define OR_WEBDAV
595
596 #endif
597
598       /* or it could be a GET or a POST (possibly webDAV too) */
599       if ((strcmpic(v[0], "get")  == 0) ||
600           (strcmpic(v[0], "head") == 0) OR_WEBDAV ||
601           (strcmpic(v[0], "post") == 0))
602       {
603          http->ssl      = 0;
604          http->gpc      = strdup(v[0]);
605          url            = v[1];
606          http->ver      = strdup(v[2]);
607
608          if (strncmpic(url, "http://",  7) == 0)
609          {
610             url += 7;
611          }
612          else if (strncmpic(url, "https://", 8) == 0)
613          {
614             url += 8;
615          }
616          else
617          {
618             url = NULL;
619          }
620
621          if (url && (p = strchr(url, '/')))
622          {
623             http->path = strdup(p);
624             *p = '\0';
625             http->hostport = strdup(url);
626          }
627       }
628    }
629
630    freez(buf);
631
632
633    if (http->hostport == NULL)
634    {
635       free_http_request(http);
636       return;
637    }
638
639    buf = strdup(http->hostport);
640
641
642    /* check if url contains password */
643    n = ssplit(buf, "@", v, SZ(v), 1, 1);
644    if (n == 2)
645    {
646       char * newbuf = NULL;
647       newbuf = strdup(v[1]);
648       freez(buf);
649       buf = newbuf;
650    }
651
652    n = ssplit(buf, ":", v, SZ(v), 1, 1);
653
654    if (n == 1)
655    {
656       http->host = strdup(v[0]);
657       http->port = 80;
658    }
659
660    if (n == 2)
661    {
662       http->host = strdup(v[0]);
663       http->port = atoi(v[1]);
664    }
665
666    freez(buf);
667
668    if (http->host == NULL)
669    {
670       free_http_request(http);
671    }
672
673    if (http->path == NULL)
674    {
675       http->path = strdup("");
676    }
677
678 }
679
680
681 /* here begins the family of parser functions that reformat header lines */
682
683
684 /*********************************************************************
685  *
686  * Function    :  crumble
687  *
688  * Description :  This is called if a header matches a pattern to "crunch"
689  *
690  * Parameters  :
691  *          1  :  v = Pointer to parsers structure, which basically holds
692  *                headers (client or server) that we want to "crunch"
693  *          2  :  s = header (from sed) to "crunch"
694  *          3  :  csp = Current client state (buffers, headers, etc...)
695  *
696  * Returns     :  Always NULL.
697  *
698  *********************************************************************/
699 char *crumble(const struct parsers *v, char *s, struct client_state *csp)
700 {
701    log_error(LOG_LEVEL_HEADER, "crunch!");
702    return(NULL);
703
704 }
705
706
707 #ifdef PCRS
708
709 /*********************************************************************
710  *
711  * Function    :  content_type
712  *
713  * Description :  Is this a text/* or javascript MIME Type?
714  *
715  * Parameters  :
716  *          1  :  v = ignored
717  *          2  :  s = header string we are "considering"
718  *          3  :  csp = Current client state (buffers, headers, etc...)
719  *
720  * Returns     :  A duplicate string pointer to this header (ie. pass thru)
721  *
722  *********************************************************************/
723 char *content_type(const struct parsers *v, char *s, struct client_state *csp)
724 {
725    if (strstr (s, " text/") || strstr (s, "application/x-javascript"))
726       csp->is_text = 1;
727    else
728       csp->is_text = 0;
729
730    return(strdup(s));
731
732 }
733
734 #endif /* def PCRS */
735
736
737 /*********************************************************************
738  *
739  * Function    :  client_referrer
740  *
741  * Description :  Handle the "referer" config setting properly.
742  *                Called from `sed'.
743  *
744  * Parameters  :
745  *          1  :  v = ignored
746  *          2  :  s = header (from sed) to "crunch"
747  *          3  :  csp = Current client state (buffers, headers, etc...)
748  *
749  * Returns     :  NULL if crunched, or a malloc'ed string with the original
750  *                or modified header
751  *
752  *********************************************************************/
753 char *client_referrer(const struct parsers *v, char *s, struct client_state *csp)
754 {
755 #ifdef FORCE_LOAD
756    /* Since the referrer can include the prefix even
757     * even if the request itself is non-forced, we must
758     * clean it unconditionally 
759     */
760    strclean(s, FORCE_PREFIX);
761 #endif /* def FORCE_LOAD */
762
763    csp->referrer = strdup(s);
764
765    if (referrer == NULL)
766    {
767       log_error(LOG_LEVEL_HEADER, "crunch!");
768       return(NULL);
769    }
770
771    if (*referrer == '.')
772    {
773       return(strdup(s));
774    }
775
776    if (*referrer == '@')
777    {
778       if (csp->send_user_cookie)
779       {
780          return(strdup(s));
781       }
782       else
783       {
784          log_error(LOG_LEVEL_HEADER, "crunch!");
785          return(NULL);
786       }
787    }
788
789    /*
790     * New option §: Forge a referer as http://[hostname:port of REQUEST]/
791     * to fool stupid checks for in-site links
792     */
793
794    if (*referrer == '§')
795    {
796       if (csp->send_user_cookie)
797       {
798          return(strdup(s));
799       }
800       else
801       {
802          log_error(LOG_LEVEL_HEADER, "crunch+forge!");
803          s = strsav(NULL, "Referer: ");
804          s = strsav(s, "http://");
805          s = strsav(s, csp->http->hostport);
806          s = strsav(s, "/");
807          return(s);
808       }
809    }
810
811    log_error(LOG_LEVEL_HEADER, "modified");
812
813    s = strsav( NULL, "Referer: " );
814    s = strsav( s, referrer );
815    return(s);
816
817 }
818
819
820 /*********************************************************************
821  *
822  * Function    :  client_uagent
823  *
824  * Description :  Handle the "user-agent" config setting properly.
825  *                Called from `sed'.
826  *
827  * Parameters  :
828  *          1  :  v = ignored
829  *          2  :  s = header (from sed) to "crunch"
830  *          3  :  csp = Current client state (buffers, headers, etc...)
831  *
832  * Returns     :  A malloc'ed pointer to the default agent, or
833  *                a malloc'ed string pointer to this header (ie. pass thru).
834  *
835  *********************************************************************/
836 char *client_uagent(const struct parsers *v, char *s, struct client_state *csp)
837 {
838 #ifdef DETECT_MSIE_IMAGES
839    if (strstr (s, "MSIE "))
840    {
841       /* This is Microsoft Internet Explorer.
842        * Enable auto-detect.
843        */
844       csp->accept_types |= ACCEPT_TYPE_IS_MSIE;
845    }
846 #endif /* def DETECT_MSIE_IMAGES */
847
848    if (uagent == NULL)
849    {
850       log_error(LOG_LEVEL_HEADER, "default");
851       return(strdup(DEFAULT_USER_AGENT));
852    }
853
854    if (*uagent == '.')
855    {
856       return(strdup(s));
857    }
858
859    if (*uagent == '@')
860    {
861       if (csp->send_user_cookie)
862       {
863          return(strdup(s));
864       }
865       else
866       {
867          log_error(LOG_LEVEL_HEADER, "default");
868          return(strdup(DEFAULT_USER_AGENT));
869       }
870    }
871
872    log_error(LOG_LEVEL_HEADER, "modified");
873
874    s = strsav( NULL, "User-Agent: " );
875    s = strsav( s, uagent );
876    return(s);
877
878 }
879
880
881 /*********************************************************************
882  *
883  * Function    :  client_ua
884  *
885  * Description :  Handle "ua-" headers properly.  Called from `sed'.
886  *
887  * Parameters  :
888  *          1  :  v = ignored
889  *          2  :  s = header (from sed) to "crunch"
890  *          3  :  csp = Current client state (buffers, headers, etc...)
891  *
892  * Returns     :  NULL if crunched, or a malloc'ed string to original header
893  *
894  *********************************************************************/
895 char *client_ua(const struct parsers *v, char *s, struct client_state *csp)
896 {
897    if (uagent == NULL)
898    {
899       log_error(LOG_LEVEL_HEADER, "crunch!");
900       return(NULL);
901    }
902
903    if (*uagent == '.')
904    {
905       return(strdup(s));
906    }
907
908    if (*uagent == '@')
909    {
910       if (csp->send_user_cookie)
911       {
912          return(strdup(s));
913       }
914       else
915       {
916          log_error(LOG_LEVEL_HEADER, "crunch!");
917          return(NULL);
918       }
919    }
920
921    log_error(LOG_LEVEL_HEADER, "crunch!");
922    return(NULL);
923
924 }
925
926
927 /*********************************************************************
928  *
929  * Function    :  client_from
930  *
931  * Description :  Handle the "from" config setting properly.
932  *                Called from `sed'.
933  *
934  * Parameters  :
935  *          1  :  v = ignored
936  *          2  :  s = header (from sed) to "crunch"
937  *          3  :  csp = Current client state (buffers, headers, etc...)
938  *
939  * Returns     :  NULL if crunched, or a malloc'ed string to
940  *                modified/original header.
941  *
942  *********************************************************************/
943 char *client_from(const struct parsers *v, char *s, struct client_state *csp)
944 {
945    /* if not set, zap it */
946    if (from == NULL)
947    {
948       log_error(LOG_LEVEL_HEADER, "crunch!");
949       return(NULL);
950    }
951
952    if (*from == '.')
953    {
954       return(strdup(s));
955    }
956
957    log_error(LOG_LEVEL_HEADER, " modified");
958
959    s = strsav( NULL, "From: " );
960    s = strsav( s, from );
961    return(s);
962
963 }
964
965
966 /*********************************************************************
967  *
968  * Function    :  client_send_cookie
969  *
970  * Description :  Handle the "cookie" header properly.  Called from `sed'.
971  *                If cookie is accepted, add it to the cookie_list,
972  *                else we crunch it.  Mmmmmmmmmmm ... cookie ......
973  *
974  * Parameters  :
975  *          1  :  v = pattern of cookie `sed' found matching
976  *          2  :  s = header (from sed) to "crunch"
977  *          3  :  csp = Current client state (buffers, headers, etc...)
978  *
979  * Returns     :  Always NULL.
980  *
981  *********************************************************************/
982 char *client_send_cookie(const struct parsers *v, char *s, struct client_state *csp)
983 {
984    if (csp->send_user_cookie)
985    {
986       enlist(csp->cookie_list, s + v->len + 1);
987    }
988    else
989    {
990       log_error(LOG_LEVEL_HEADER, " crunch!");
991    }
992
993    /*
994     * Always return NULL here.  The cookie header
995     * will be sent at the end of the header.
996     */
997    return(NULL);
998
999 }
1000
1001
1002 /*********************************************************************
1003  *
1004  * Function    :  client_x_forwarded
1005  *
1006  * Description :  Handle the "x-forwarded-for" config setting properly,
1007  *                also used in the add_client_headers list.  Called from `sed'.
1008  *
1009  * Parameters  :
1010  *          1  :  v = ignored
1011  *          2  :  s = header (from sed) to "crunch"
1012  *          3  :  csp = Current client state (buffers, headers, etc...)
1013  *
1014  * Returns     :  Always NULL.
1015  *
1016  *********************************************************************/
1017 char *client_x_forwarded(const struct parsers *v, char *s, struct client_state *csp)
1018 {
1019    if (add_forwarded)
1020    {
1021       csp->x_forwarded = strdup(s);
1022    }
1023
1024    /*
1025     * Always return NULL, since this information
1026     * will be sent at the end of the header.
1027     */
1028
1029    return(NULL);
1030
1031 }
1032
1033 #if defined(DETECT_MSIE_IMAGES)
1034 /*********************************************************************
1035  *
1036  * Function    :  client_accept
1037  *
1038  * Description :  Detect whether the client wants HTML or an image.
1039  *                Clients do not always make this information available
1040  *                in a sane way.  Always passes the header through
1041  *                the proxy unchanged.
1042  *
1043  * Parameters  :
1044  *          1  :  v = Ignored.
1045  *          2  :  s = Header string.  Null terminated.
1046  *          3  :  csp = Current client state (buffers, headers, etc...)
1047  *
1048  * Returns     :  Duplicate of argument s.
1049  *
1050  *********************************************************************/
1051 char *client_accept(const struct parsers *v, char *s, struct client_state *csp)
1052 {
1053 #ifdef DETECT_MSIE_IMAGES
1054    if (strstr (s, "image/gif"))
1055    {
1056       /* Client will accept HTML.  If this seems counterintuitive,
1057        * blame Microsoft. 
1058        */
1059       csp->accept_types |= ACCEPT_TYPE_MSIE_HTML;
1060    }
1061    else
1062    {
1063       csp->accept_types |= ACCEPT_TYPE_MSIE_IMAGE;
1064    }
1065 #endif /* def DETECT_MSIE_IMAGES */
1066
1067    return(strdup(s));
1068
1069 }
1070 #endif /* defined(DETECT_MSIE_IMAGES) */
1071
1072
1073
1074 /* the following functions add headers directly to the header list */
1075
1076
1077 /*********************************************************************
1078  *
1079  * Function    :  client_cookie_adder
1080  *
1081  * Description :  Used in the add_client_headers list.  Called from `sed'.
1082  *
1083  * Parameters  :
1084  *          1  :  csp = Current client state (buffers, headers, etc...)
1085  *
1086  * Returns     :  N/A
1087  *
1088  *********************************************************************/
1089 void client_cookie_adder(struct client_state *csp)
1090 {
1091    struct list *l;
1092    char *tmp = NULL;
1093    char *e;
1094
1095    for (l = csp->cookie_list->next; l ; l = l->next)
1096    {
1097       if (tmp)
1098       {
1099          tmp = strsav(tmp, "; ");
1100       }
1101       tmp = strsav(tmp, l->str);
1102    }
1103
1104    for (l = wafer_list->next;  l ; l = l->next)
1105    {
1106       if (tmp)
1107       {
1108          tmp = strsav(tmp, "; ");
1109       }
1110
1111       if ((e = cookie_encode(l->str)))
1112       {
1113          tmp = strsav(tmp, e);
1114          freez(e);
1115       }
1116    }
1117
1118    if (tmp)
1119    {
1120       char *ret;
1121
1122       ret = strdup("Cookie: ");
1123       ret = strsav(ret, tmp);
1124       log_error(LOG_LEVEL_HEADER, "addh: %s", ret);
1125       enlist(csp->headers, ret);
1126       freez(tmp);
1127       freez(ret);
1128    }
1129
1130 }
1131
1132
1133 /*********************************************************************
1134  *
1135  * Function    :  client_xtra_adder
1136  *
1137  * Description :  Used in the add_client_headers list.  Called from `sed'.
1138  *
1139  * Parameters  :
1140  *          1  :  csp = Current client state (buffers, headers, etc...)
1141  *
1142  * Returns     :  N/A
1143  *
1144  *********************************************************************/
1145 void client_xtra_adder(struct client_state *csp)
1146 {
1147    struct list *l;
1148
1149    for (l = xtra_list->next; l ; l = l->next)
1150    {
1151       log_error(LOG_LEVEL_HEADER, "addh: %s", l->str);
1152       enlist(csp->headers, l->str);
1153    }
1154
1155 }
1156
1157
1158 /*********************************************************************
1159  *
1160  * Function    :  client_x_forwarded_adder
1161  *
1162  * Description :  Used in the add_client_headers list.  Called from `sed'.
1163  *
1164  * Parameters  :
1165  *          1  :  csp = Current client state (buffers, headers, etc...)
1166  *
1167  * Returns     :  N/A
1168  *
1169  *********************************************************************/
1170 void client_x_forwarded_adder(struct client_state *csp)
1171 {
1172    char *p = NULL;
1173
1174    if (add_forwarded == 0) return;
1175
1176    if (csp->x_forwarded)
1177    {
1178       p = strsav(p, csp->x_forwarded);
1179       p = strsav(p, ", ");
1180       p = strsav(p, csp->ip_addr_str);
1181    }
1182    else
1183    {
1184       p = strsav(p, "X-Forwarded-For: ");
1185       p = strsav(p, csp->ip_addr_str);
1186    }
1187
1188    log_error(LOG_LEVEL_HEADER, "addh: %s", p);
1189    enlist(csp->headers, p);
1190
1191 }
1192
1193
1194 /*********************************************************************
1195  *
1196  * Function    :  server_set_cookie
1197  *
1198  * Description :  Handle the server "cookie" header properly.
1199  *                Log cookie to the jar file.  Then "crunch" it,
1200  *                or accept it.  Called from `sed'.
1201  *
1202  * Parameters  :
1203  *          1  :  v = parser pattern that matched this header
1204  *          2  :  s = header that matched this pattern
1205  *          3  :  csp = Current client state (buffers, headers, etc...)
1206  *
1207  * Returns     :  `crumble' or a newly malloc'ed string.
1208  *
1209  *********************************************************************/
1210 char *server_set_cookie(const struct parsers *v, char *s, struct client_state *csp)
1211 {
1212 #ifdef JAR_FILES
1213    if (jar)
1214    {
1215       fprintf(jar, "%s\t%s\n", csp->http->host, (s + v->len + 1));
1216    }
1217 #endif /* def JAR_FILES */
1218
1219    if (csp->accept_server_cookie == 0)
1220    {
1221       return(crumble(v, s, csp));
1222    }
1223
1224    return(strdup(s));
1225
1226 }
1227
1228
1229 #ifdef FORCE_LOAD
1230 /*********************************************************************
1231  *
1232  * Function    :  client_host
1233  *
1234  * Description :  Clean the FORCE_PREFIX out of the 'host' http
1235  *                header, if we use force
1236  *
1237  * Parameters  :
1238  *          1  :  v = ignored
1239  *          2  :  s = header (from sed) to clean
1240  *          3  :  csp = Current client state (buffers, headers, etc...)
1241  *
1242  * Returns     :  A malloc'ed pointer to the cleaned host header 
1243  *
1244  *********************************************************************/
1245 char *client_host(const struct parsers *v, char *s, struct client_state *csp)
1246 {
1247    char *cleanhost = strdup(s);
1248  
1249    if(csp->force)
1250       strclean(cleanhost, FORCE_PREFIX);
1251  
1252    return(cleanhost);
1253 }
1254 #endif /* def FORCE_LOAD */
1255  
1256  
1257 #ifdef FORCE_LOAD 
1258 /*********************************************************************
1259  *
1260  * Function    :  strclean
1261  *
1262  * Description :  In-Situ-Eliminate all occurances of substring in 
1263  *                string
1264  *
1265  * Parameters  :
1266  *          1  :  string = string to clean
1267  *          2  :  substring = substring to eliminate
1268  *
1269  * Returns     :  Number of eliminations
1270  *
1271  *********************************************************************/
1272 int strclean(const char *string, const char *substring)
1273 {
1274    int hits = 0, len = strlen(substring);
1275    char *pos, *p;
1276
1277    while((pos = strstr(string, substring)))
1278    {
1279       p = pos + len;
1280       do
1281       {
1282          *(p - len) = *p; 
1283       }
1284       while (*p++ != '\0');
1285
1286       hits++;
1287    }
1288
1289    return(hits);
1290 }
1291 #endif /* def FORCE_LOAD */
1292
1293
1294 /*
1295   Local Variables:
1296   tab-width: 3
1297   end:
1298 */