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