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