- Moved strsav() from showargs to miscutil
[privoxy.git] / loaders.c
1 const char loaders_rcs[] = "$Id: loaders.c,v 1.9 2001/05/26 17:12:07 jongfoster Exp $";
2 /*********************************************************************
3  *
4  * File        :  $Source: /cvsroot/ijbswa/current/loaders.c,v $
5  *
6  * Purpose     :  Functions to load and unload the various
7  *                configuration files.  Also contains code to manage
8  *                the list of active loaders, and to automatically 
9  *                unload files that are no longer in use.
10  *
11  * Copyright   :  Written by and Copyright (C) 2001 the SourceForge
12  *                IJBSWA team.  http://ijbswa.sourceforge.net
13  *
14  *                Based on the Internet Junkbuster originally written
15  *                by and Copyright (C) 1997 Anonymous Coders and 
16  *                Junkbusters Corporation.  http://www.junkbusters.com
17  *
18  *                This program is free software; you can redistribute it 
19  *                and/or modify it under the terms of the GNU General
20  *                Public License as published by the Free Software
21  *                Foundation; either version 2 of the License, or (at
22  *                your option) any later version.
23  *
24  *                This program is distributed in the hope that it will
25  *                be useful, but WITHOUT ANY WARRANTY; without even the
26  *                implied warranty of MERCHANTABILITY or FITNESS FOR A
27  *                PARTICULAR PURPOSE.  See the GNU General Public
28  *                License for more details.
29  *
30  *                The GNU General Public License should be included with
31  *                this file.  If not, you can view it at
32  *                http://www.gnu.org/copyleft/gpl.html
33  *                or write to the Free Software Foundation, Inc., 59
34  *                Temple Place - Suite 330, Boston, MA  02111-1307, USA.
35  *
36  * Revisions   :
37  *    $Log: loaders.c,v $
38  *    Revision 1.9  2001/05/26 17:12:07  jongfoster
39  *    Fatal errors loading configuration files now give better error messages.
40  *
41  *    Revision 1.8  2001/05/26 00:55:20  jongfoster
42  *    Removing duplicated code.  load_forwardfile() now uses create_url_spec()
43  *
44  *    Revision 1.7  2001/05/26 00:28:36  jongfoster
45  *    Automatic reloading of config file.
46  *    Removed obsolete SIGHUP support (Unix) and Reload menu option (Win32).
47  *    Most of the global variables have been moved to a new
48  *    struct configuration_spec, accessed through csp->config->globalname
49  *    Most of the globals remaining are used by the Win32 GUI.
50  *
51  *    Revision 1.6  2001/05/23 12:27:33  oes
52  *
53  *    Fixed ugly indentation of my last changes
54  *
55  *    Revision 1.5  2001/05/23 10:39:05  oes
56  *    - Added support for escaping the comment character
57  *      in config files by a backslash
58  *    - Added support for line continuation in config
59  *      files
60  *    - Fixed a buffer overflow bug with long config lines
61  *
62  *    Revision 1.4  2001/05/22 18:56:28  oes
63  *    CRLF -> LF
64  *
65  *    Revision 1.3  2001/05/20 01:21:20  jongfoster
66  *    Version 2.9.4 checkin.
67  *    - Merged popupfile and cookiefile, and added control over PCRS
68  *      filtering, in new "permissionsfile".
69  *    - Implemented LOG_LEVEL_FATAL, so that if there is a configuration
70  *      file error you now get a message box (in the Win32 GUI) rather
71  *      than the program exiting with no explanation.
72  *    - Made killpopup use the PCRS MIME-type checking and HTTP-header
73  *      skipping.
74  *    - Removed tabs from "config"
75  *    - Moved duplicated url parsing code in "loaders.c" to a new funcition.
76  *    - Bumped up version number.
77  *
78  *    Revision 1.2  2001/05/17 23:01:01  oes
79  *     - Cleaned CRLF's from the sources and related files
80  *
81  *    Revision 1.1.1.1  2001/05/15 13:58:59  oes
82  *    Initial import of version 2.9.3 source tree
83  *
84  *
85  *********************************************************************/
86 \f
87
88 #include "config.h"
89
90 #include <stdio.h>
91 #include <stdlib.h>
92 #include <sys/types.h>
93 #include <string.h>
94 #include <malloc.h>
95 #include <errno.h>
96 #include <sys/stat.h>
97 #include <ctype.h>
98
99 #ifndef _WIN32
100 #include <unistd.h>
101 #endif
102
103 #include "project.h"
104 #include "loaders.h"
105 #include "encode.h"
106 #include "filters.h"
107 #include "parsers.h"
108 #include "jcc.h"
109 #include "ssplit.h"
110 #include "miscutil.h"
111 #include "errlog.h"
112 #include "gateway.h"
113
114 #ifndef SPLIT_PROXY_ARGS
115 /* For strsav */
116 #include "showargs.h"
117 #endif /* ndef SPLIT_PROXY_ARGS */
118
119 const char loaders_h_rcs[] = LOADERS_H_VERSION;
120
121 /* Fix a problem with Solaris.  There should be no effect on other
122  * platforms.
123  * Solaris's isspace() is a macro which uses it's argument directly
124  * as an array index.  Therefore we need to make sure that high-bit
125  * characters generate +ve values, and ideally we also want to make
126  * the argument match the declared parameter type of "int".
127  */
128 #define ijb_isspace(__X) isspace((int)(unsigned char)(__X))
129
130
131 /*
132  * Currently active files.
133  * These are also entered in the main linked list of files.
134  */
135 static struct file_list *current_permissions_file  = NULL;
136 static struct file_list *current_forwardfile    = NULL;
137
138 #ifdef ACL_FILES
139 static struct file_list *current_aclfile        = NULL;
140 #endif /* def ACL_FILES */
141
142 #ifdef TRUST_FILES
143 static struct file_list *current_trustfile      = NULL;
144 #endif /* def TRUST_FILES */
145
146 #ifdef PCRS
147 static struct file_list *current_re_filterfile  = NULL;
148 #endif /* def PCRS */
149
150
151 static int create_url_spec(struct url_spec * url, char * buf);
152
153
154 /*********************************************************************
155  *
156  * Function    :  sweep
157  *
158  * Description :  Basically a mark and sweep garbage collector, it is run
159  *                (by the parent thread) every once in a while to reclaim memory.
160  *
161  * It uses a mark and sweep strategy:
162  *   1) mark all files as inactive
163  *
164  *   2) check with each client:
165  *       if it is active,   mark its files as active
166  *       if it is inactive, free its resources
167  *
168  *   3) free the resources of all of the files that
169  *      are still marked as inactive (and are obsolete).
170  *
171  *   N.B. files that are not obsolete don't have an unloader defined.
172  *
173  * Parameters  :  None
174  *
175  * Returns     :  N/A
176  *
177  *********************************************************************/
178 void sweep(void)
179 {
180    struct file_list *fl, *nfl;
181    struct client_state *csp, *ncsp;
182
183    /* clear all of the file's active flags */
184    for ( fl = files->next; NULL != fl; fl = fl->next )
185    {
186       fl->active = 0;
187    }
188
189    for (csp = clients; csp && (ncsp = csp->next) ; csp = csp->next)
190    {
191       if (ncsp->active)
192       {
193          /* mark this client's files as active */
194
195          /*
196           * Always have a configuration file.
197           * (Also note the slightly non-standard extra
198           * indirection here.)
199           */
200          ncsp->config->config_file_list->active = 1;
201
202          if (ncsp->permissions_list)     /* permissions files */
203          {
204             ncsp->permissions_list->active = 1;
205          }
206
207          if (ncsp->flist)     /* forward files */
208          {
209             ncsp->flist->active = 1;
210          }
211
212 #ifdef ACL_FILES
213          if (ncsp->alist)     /* acl files */
214          {
215             ncsp->alist->active = 1;
216          }
217 #endif /* def ACL_FILES */
218
219 #ifdef PCRS
220          if (ncsp->rlist)     /* perl re files */
221          {
222             ncsp->rlist->active = 1;
223          }
224 #endif /* def PCRS */
225
226 #ifdef TRUST_FILES
227          if (ncsp->tlist)     /* trust files */
228          {
229             ncsp->tlist->active = 1;
230          }
231 #endif /* def TRUST_FILES */
232
233       }
234       else
235       {
236          /* this client one is not active, release its resources */
237          csp->next = ncsp->next;
238
239          freez(ncsp->ip_addr_str);
240 #ifdef TRUST_FILES
241          freez(ncsp->referrer);
242 #endif /* def TRUST_FILES */
243          freez(ncsp->x_forwarded);
244          freez(ncsp->iob->buf);
245
246          free_http_request(ncsp->http);
247
248          destroy_list(ncsp->headers);
249          destroy_list(ncsp->cookie_list);
250
251 #ifdef STATISTICS
252          urls_read++;
253          if (ncsp->rejected)
254          {
255             urls_rejected++;
256          }
257 #endif /* def STATISTICS */
258
259          freez(ncsp);
260       }
261    }
262
263    for (fl = files; fl && (nfl = fl->next) ; fl = fl->next)
264    {
265       if ( ( 0 == nfl->active ) && ( NULL != nfl->unloader ) )
266       {
267          fl->next = nfl->next;
268
269          (nfl->unloader)(nfl->f);
270
271 #ifndef SPLIT_PROXY_ARGS
272          freez(nfl->proxy_args);
273 #endif /* ndef SPLIT_PROXY_ARGS */
274
275          freez(nfl->filename);
276
277          freez(nfl);
278       }
279    }
280
281 }
282
283
284 /*********************************************************************
285  *
286  * Function    :  create_url_spec
287  *
288  * Description :  Creates a "url_spec" structure from a string.
289  *                When finished, free with unload_url().
290  *
291  * Parameters  :
292  *          1  :  url = Target url_spec to be filled in.  Must be
293  *                      zeroed out before the call (e.g. using zalloc).
294  *          2  :  buf = Source pattern, null terminated.  NOTE: The
295  *                      contents of this buffer are destroyed by this
296  *                      function.  If this function succeeds, the
297  *                      buffer is copied to url->spec.  If this
298  *                      function fails, the contents of the buffer
299  *                      are lost forever.
300  *
301  * Returns     :  0 => Ok, everything else is an error.
302  *
303  *********************************************************************/
304 static int create_url_spec(struct url_spec * url, char * buf)
305 {
306    char *p;
307    struct url_spec tmp_url[1];
308
309    /* paranoia - should never happen. */
310    if ((url == NULL) || (buf == NULL))
311    {
312       return 1;
313    }
314
315    /* save a copy of the orignal specification */
316    if ((url->spec = strdup(buf)) == NULL)
317    {
318       return 1;
319    }
320
321    if ((p = strchr(buf, '/')))
322    {
323       if (NULL == (url->path = strdup(p)))
324       {
325          freez(url->spec);
326          return 1;
327       }
328       url->pathlen = strlen(url->path);
329       *p = '\0';
330    }
331    else
332    {
333       url->path    = NULL;
334       url->pathlen = 0;
335    }
336 #ifdef REGEX
337    if (url->path)
338    {
339       int errcode;
340       char rebuf[BUFSIZ];
341
342       if (NULL == (url->preg = zalloc(sizeof(*url->preg))))
343       {
344          freez(url->spec);
345          freez(url->path);
346          return 1;
347       }
348
349       sprintf(rebuf, "^(%s)", url->path);
350
351       errcode = regcomp(url->preg, rebuf,
352             (REG_EXTENDED|REG_NOSUB|REG_ICASE));
353       if (errcode)
354       {
355          size_t errlen =
356             regerror(errcode,
357                url->preg, buf, sizeof(buf));
358
359          buf[errlen] = '\0';
360
361          log_error(LOG_LEVEL_ERROR, "error compiling %s: %s",
362                  url->spec, buf);
363
364          freez(url->spec);
365          freez(url->path);
366          freez(url->preg);
367
368          return 1;
369       }
370    }
371 #endif
372    if ((p = strchr(buf, ':')) == NULL)
373    {
374       url->port = 0;
375    }
376    else
377    {
378       *p++ = '\0';
379       url->port = atoi(p);
380    }
381
382    if ((url->domain = strdup(buf)) == NULL)
383    {
384       freez(url->spec);
385       freez(url->path);
386 #ifdef REGEX
387       freez(url->preg);
388 #endif /* def REGEX */
389       return 1;
390    }
391
392    /* split domain into components */
393
394    *tmp_url = dsplit(url->domain);
395    url->dbuf = tmp_url->dbuf;
396    url->dcnt = tmp_url->dcnt;
397    url->dvec = tmp_url->dvec;
398
399    return 0; /* OK */
400 }
401
402
403 /*********************************************************************
404  *
405  * Function    :  unload_url
406  *
407  * Description :  Called from the "unloaders".  Freez the url
408  *                structure elements.
409  *
410  * Parameters  :
411  *          1  :  url = pointer to a url_spec structure.
412  *
413  * Returns     :  N/A
414  *
415  *********************************************************************/
416 static void unload_url(struct url_spec *url)
417 {
418    if (url == NULL) return;
419
420    freez(url->spec);
421    freez(url->domain);
422    freez(url->dbuf);
423    freez(url->dvec);
424    freez(url->path);
425 #ifdef REGEX
426    if (url->preg)
427    {
428       regfree(url->preg);
429       freez(url->preg);
430    }
431 #endif
432
433 }
434
435
436 #ifdef ACL_FILES
437 /*********************************************************************
438  *
439  * Function    :  unload_aclfile
440  *
441  * Description :  Unloads an aclfile.
442  *
443  * Parameters  :
444  *          1  :  f = the data structure associated with the aclfile.
445  *
446  * Returns     :  N/A
447  *
448  *********************************************************************/
449 static void unload_aclfile(void *f)
450 {
451    struct access_control_list *b = (struct access_control_list *)f;
452    if (b == NULL) return;
453
454    unload_aclfile(b->next);
455
456    freez(b);
457
458 }
459 #endif /* def ACL_FILES */
460
461
462 /*********************************************************************
463  *
464  * Function    :  unload_permissions_file
465  *
466  * Description :  Unloads a permissions file.
467  *
468  * Parameters  :
469  *          1  :  file_data = the data structure associated with the
470  *                            permissions file.
471  *
472  * Returns     :  N/A
473  *
474  *********************************************************************/
475 static void unload_permissions_file(void *file_data)
476 {
477    struct permissions_spec * next;
478    struct permissions_spec * cur = (struct permissions_spec *)file_data;
479    while (cur != NULL)
480    {
481       next = cur->next;
482       unload_url(cur->url);
483       freez(cur);
484       cur = next;
485    }
486
487 }
488
489
490 #ifdef TRUST_FILES
491 /*********************************************************************
492  *
493  * Function    :  unload_trustfile
494  *
495  * Description :  Unloads a trustfile.
496  *
497  * Parameters  :
498  *          1  :  f = the data structure associated with the trustfile.
499  *
500  * Returns     :  N/A
501  *
502  *********************************************************************/
503 static void unload_trustfile(void *f)
504 {
505    struct block_spec *b = (struct block_spec *)f;
506    if (b == NULL) return;
507
508    unload_trustfile(b->next);
509
510    unload_url(b->url);
511
512    freez(b);
513
514 }
515 #endif /* def TRUST_FILES */
516
517
518 /*********************************************************************
519  *
520  * Function    :  unload_forwardfile
521  *
522  * Description :  Unloads a forwardfile.
523  *
524  * Parameters  :
525  *          1  :  f = the data structure associated with the forwardfile.
526  *
527  * Returns     :  N/A
528  *
529  *********************************************************************/
530 static void unload_forwardfile(void *f)
531 {
532    struct forward_spec *b = (struct forward_spec *)f;
533    if (b == NULL) return;
534
535    unload_forwardfile(b->next);
536
537    unload_url(b->url);
538
539    freez(b->gw->gateway_host);
540    freez(b->gw->forward_host);
541
542    freez(b);
543
544 }
545
546
547 #ifdef PCRS
548 /*********************************************************************
549  *
550  * Function    :  unload_re_filterfile
551  *
552  * Description :  Unload the re_filter list.
553  *
554  * Parameters  :
555  *          1  :  f = the data structure associated with the filterfile.
556  *
557  * Returns     :  N/A
558  *
559  *********************************************************************/
560 static void unload_re_filterfile(void *f)
561 {
562    pcrs_job *joblist;
563    struct re_filterfile_spec *b = (struct re_filterfile_spec *)f;
564
565    if (b == NULL) return;
566
567    destroy_list(b->patterns);
568
569    joblist = b->joblist;
570    while ( NULL != (joblist = pcrs_free_job(joblist)) ) {}
571
572    freez(b);
573
574 }
575 #endif /* def PCRS */
576
577
578 /*********************************************************************
579  *
580  * Function    :  check_file_changed
581  *
582  * Description :  Helper function to check if a file needs reloading.
583  *                If "current" is still current, return it.  Otherwise
584  *                allocates a new (zeroed) "struct file_list", fills 
585  *                in the disk file name and timestamp, and returns it.
586  *
587  * Parameters  :
588  *          1  :  current = The file_list currently being used - will
589  *                          be checked to see if it is out of date. 
590  *                          May be NULL (which is treated as out of
591  *                          date).
592  *          2  :  filename = Name of file to check.
593  *          3  :  newfl    = New file list. [Output only]
594  *                           This will be set to NULL, OR a struct
595  *                           file_list newly allocated on the
596  *                           heap, with the filename and lastmodified
597  *                           fields filled, standard header giving file
598  *                           name in proxy_args, and all others zeroed.
599  *                           (proxy_args is only filled in if !defined
600  *                           SPLIT_PROXY_ARGS and !suppress_blocklists).
601  *
602  * Returns     :  If file unchanged: 0 (and sets newfl == NULL)
603  *                If file changed: 1 and sets newfl != NULL
604  *                On error: 1 and sets newfl == NULL
605  *
606  *********************************************************************/
607 int check_file_changed(const struct file_list * current,
608                        const char * filename,
609                        struct file_list ** newfl)
610 {
611    struct file_list *fs;
612    struct stat statbuf[1];
613
614    *newfl = NULL;
615
616    if (stat(filename, statbuf) < 0)
617    {
618       /* Error, probably file not found. */
619       return 1;
620    }
621
622    if (current
623        && (current->lastmodified == statbuf->st_mtime)
624        && (0 == strcmp(current->filename, filename)))
625    {
626       return 0;
627    }
628
629    fs = (struct file_list *)zalloc(sizeof(struct file_list));
630
631    if (fs == NULL)
632    {
633       /* Out of memory error */
634       return 1;
635    }
636
637    fs->filename = strdup(filename);
638    fs->lastmodified = statbuf->st_mtime;
639
640    if (fs->filename == NULL)
641    {
642       /* Out of memory error */
643       freez (fs);
644       return 1;
645    }
646
647 #ifndef SPLIT_PROXY_ARGS
648    if (!suppress_blocklists)
649    {
650       char * p = html_encode(filename);
651       if (p)
652       {
653          fs->proxy_args = strsav(fs->proxy_args, "<h2>The file `");
654          fs->proxy_args = strsav(fs->proxy_args, p);
655          fs->proxy_args = strsav(fs->proxy_args, 
656             "' contains the following patterns</h2>\n");
657          freez(p);
658       }
659       fs->proxy_args = strsav(fs->proxy_args, "<pre>");
660    }
661 #endif /* ndef SPLIT_PROXY_ARGS */
662
663    *newfl = fs;
664    return 1;
665 }
666
667
668 /*********************************************************************
669  *
670  * Function    :  read_config_line
671  *
672  * Description :  Read a single non-empty line from a file and return
673  *                it.  Trims comments, leading and trailing whitespace
674  *                and respects escaping of newline and comment char.
675  *                Also writes the file to fs->proxy_args.
676  *
677  * Parameters  :
678  *          1  :  buf = Buffer to use.
679  *          2  :  buflen = Size of buffer in bytes.
680  *          3  :  fp = File to read from
681  *          4  :  fs = File will be written to fs->proxy_args.  May
682  *                be NULL to disable this feature.
683  *
684  * Returns     :  NULL on EOF or error
685  *                Otherwise, returns buf.
686  *
687  *********************************************************************/
688 char *read_config_line(char *buf, int buflen, FILE *fp, struct file_list *fs)
689 {
690    char *p, *q;
691    char linebuf[BUFSIZ];
692    int contflag = 0;
693
694    *buf = '\0';
695
696    while (fgets(linebuf, sizeof(linebuf), fp))
697    {
698 #ifndef SPLIT_PROXY_ARGS
699       if (fs && !suppress_blocklists)
700       {
701          char *html_line = html_encode(linebuf);
702          if (html_line != NULL)
703          {
704             fs->proxy_args = strsav(fs->proxy_args, html_line);
705             freez(html_line);
706          }
707          fs->proxy_args = strsav(fs->proxy_args, "<br>");
708       }
709 #endif /* ndef SPLIT_PROXY_ARGS */
710
711       /* Trim off newline */
712       if ((p = strpbrk(linebuf, "\r\n")) != NULL)
713       {
714          *p = '\0';
715       }
716
717       /* Line continuation? Trim escape and set flag. */
718       if ((p != linebuf) && (*--p == '\\'))
719       {
720          contflag = 1;
721          *p = '\0';
722       }
723
724       /* If there's a comment char.. */
725       if ((p = strpbrk(linebuf, "#")) != NULL)
726       {
727          /* ..and it's escaped, left-shift the line over the escape. */
728          if ((p != linebuf) && (*(p-1) == '\\'))
729          {
730             q = p-1;
731             while ((*q++ = *p++) != '\0') /* nop */;
732          }
733          /* Else, chop off the rest of the line */
734          else
735          {
736             *p = '\0';
737          }
738       }
739       
740       /* Trim leading whitespace */
741       p = linebuf;
742       while (*p && ijb_isspace(*p))
743       {
744          p++;
745       }
746
747       if (*p)
748       {
749          /* There is something other than whitespace on the line. */
750
751          /* Move the data to the start of buf */
752          if (p != linebuf)
753          {
754             /* strcpy that can cope with overlap. */
755             q = linebuf;
756             while ((*q++ = *p++) != '\0')
757             {
758                /* Do nothing */
759             }
760          }
761
762          /* Trim trailing whitespace */
763          p = linebuf + strlen(linebuf) - 1;
764
765          /*
766           * Note: the (p >= linebuf) below is paranoia, it's not really needed.
767           * When p == linebuf then ijb_isspace(*p) will be false and we'll drop
768           * out of the loop.
769           */
770          while ((p >= linebuf) && ijb_isspace(*p))
771          {
772             p--;
773          }
774          p[1] = '\0';
775
776          /* More paranoia.  This if statement is always true. */
777          if (*linebuf)
778          {
779             strncat(buf, linebuf, buflen - strlen(buf));
780             if (contflag)
781             {
782                contflag = 0;
783                continue;
784             }
785             else
786             {
787                return buf;
788             }
789          }
790       }
791    }
792
793    /* EOF */
794    return NULL;
795 }
796
797
798 #ifdef ACL_FILES
799 /*********************************************************************
800  *
801  * Function    :  load_aclfile
802  *
803  * Description :  Read and parse an aclfile and add to files list.
804  *
805  * Parameters  :
806  *          1  :  csp = Current client state (buffers, headers, etc...)
807  *
808  * Returns     :  0 => Ok, everything else is an error.
809  *
810  *********************************************************************/
811 int load_aclfile(struct client_state *csp)
812 {
813    FILE *fp;
814    char buf[BUFSIZ], *v[3], *p;
815    int i;
816    struct access_control_list *a, *bl;
817    struct file_list *fs;
818
819    if (!check_file_changed(current_aclfile, csp->config->aclfile, &fs))
820    {
821       /* No need to load */
822       if (csp)
823       {
824          csp->alist = current_aclfile;
825       }
826       return(0);
827    }
828    if (!fs)
829    {
830       goto load_aclfile_error;
831    }
832
833    fs->f = bl = (struct access_control_list *)zalloc(sizeof(*bl));
834    if (bl == NULL)
835    {
836       freez(fs->filename);
837       freez(fs);
838       goto load_aclfile_error;
839    }
840
841    fp = fopen(csp->config->aclfile, "r");
842
843    if (fp == NULL)
844    {
845       goto load_aclfile_error;
846    }
847
848    while (read_config_line(buf, sizeof(buf), fp, fs) != NULL)
849    {
850       i = ssplit(buf, " \t", v, SZ(v), 1, 1);
851
852       /* allocate a new node */
853       a = (struct access_control_list *) zalloc(sizeof(*a));
854
855       if (a == NULL)
856       {
857          fclose(fp);
858          freez(fs->f);
859          freez(fs->filename);
860          freez(fs);
861          goto load_aclfile_error;
862       }
863
864       /* add it to the list */
865       a->next  = bl->next;
866       bl->next = a;
867
868       switch (i)
869       {
870          case 3:
871             if (acl_addr(v[2], a->dst) < 0)
872             {
873                goto load_aclfile_error;
874             }
875             /* no break */
876
877          case 2:
878             if (acl_addr(v[1], a->src) < 0)
879             {
880                goto load_aclfile_error;
881             }
882
883             p = v[0];
884             if (strcmpic(p, "permit") == 0)
885             {
886                a->action = ACL_PERMIT;
887                break;
888             }
889
890             if (strcmpic(p, "deny") == 0)
891             {
892                a->action = ACL_DENY;
893                break;
894             }
895             /* no break */
896
897          default:
898             goto load_aclfile_error;
899       }
900    }
901
902    fclose(fp);
903
904 #ifndef SPLIT_PROXY_ARGS
905    if (!suppress_blocklists)
906    {
907       fs->proxy_args = strsav(fs->proxy_args, "</pre>");
908    }
909 #endif /* ndef SPLIT_PROXY_ARGS */
910
911    if (current_aclfile)
912    {
913       current_aclfile->unloader = unload_aclfile;
914    }
915
916    fs->next = files->next;
917    files->next = fs;
918    current_aclfile = fs;
919
920    if (csp)
921    {
922       csp->alist = fs;
923    }
924
925    return(0);
926
927 load_aclfile_error:
928    log_error(LOG_LEVEL_FATAL, "can't load access control list %s: %E",
929              csp->config->aclfile);
930    return(-1);
931
932 }
933 #endif /* def ACL_FILES */
934
935
936 struct permission_alias
937 {
938    const char * name;
939    unsigned mask;   /* a bit set to "0" = remove permission */
940    unsigned add;    /* a bit set to "1" = add permission */
941    struct permission_alias * next;
942 };
943
944
945 /*
946  * Note: this is special-cased in the code so we don't need to
947  * fill in the ->next fields.
948  */
949 static const struct permission_alias standard_aliases[] =
950 {
951    { "+block",          PERMIT_MASK_ALL, PERMIT_BLOCK          },
952 /* { "+cookies",        PERMIT_MASK_ALL, PERMIT_COOKIE_SET | PERMIT_COOKIE_READ }, */
953    { "+cookies-read",   PERMIT_MASK_ALL, PERMIT_COOKIE_READ    },
954    { "+cookies-set",    PERMIT_MASK_ALL, PERMIT_COOKIE_SET     },
955    { "+fast-redirects", PERMIT_MASK_ALL, PERMIT_FAST_REDIRECTS },
956    { "+filter",         PERMIT_MASK_ALL, PERMIT_RE_FILTER      },
957    { "+image",          PERMIT_MASK_ALL, PERMIT_IMAGE          },
958    { "+popup",          PERMIT_MASK_ALL, PERMIT_POPUPS         },
959    { "+popups",         PERMIT_MASK_ALL, PERMIT_POPUPS         },
960    { "+referer",        PERMIT_MASK_ALL, PERMIT_REFERER        },
961    { "+referrer",       PERMIT_MASK_ALL, PERMIT_REFERER        },
962    { "-block",          ~PERMIT_BLOCK,          0 },
963 /* { "-cookies",        ~(PERMIT_COOKIE_SET | PERMIT_COOKIE_READ), 0 }, */
964    { "-cookies-read",   ~PERMIT_COOKIE_READ,    0 },
965    { "-cookies-set",    ~PERMIT_COOKIE_SET,     0 },
966    { "-fast-redirects", ~PERMIT_FAST_REDIRECTS, 0 },
967    { "-filter",         ~PERMIT_RE_FILTER,      0 },
968    { "-image",          ~PERMIT_IMAGE,          0 },
969    { "-popup",          ~PERMIT_POPUPS,         0 },
970    { "-popups",         ~PERMIT_POPUPS,         0 },
971    { "-referer",        ~PERMIT_REFERER,        0 },
972    { "-referrer",       ~PERMIT_REFERER,        0 },
973    { NULL,              0,                      0 } /* End marker */
974 };
975
976
977 /*********************************************************************
978  *
979  * Function    :  load_permissions_file
980  *
981  * Description :  Read and parse a permissions file and add to files
982  *                list.
983  *
984  * Parameters  :
985  *          1  :  csp = Current client state (buffers, headers, etc...)
986  *
987  * Returns     :  0 => Ok, everything else is an error.
988  *
989  *********************************************************************/
990 int load_permissions_file(struct client_state *csp)
991 {
992    FILE *fp;
993
994    struct permissions_spec *last_perm;
995    struct permissions_spec *perm;
996    char  buf[BUFSIZ];
997    struct file_list *fs;
998 #define MODE_START_OF_FILE 1
999 #define MODE_PERMISSIONS   2
1000 #define MODE_ALIAS         3
1001    int mode = MODE_START_OF_FILE;
1002    unsigned curmask = PERMIT_MASK_ALL;
1003    unsigned curadd  = 0;
1004    struct permission_alias * alias_list = NULL;
1005
1006    if (!check_file_changed(current_permissions_file, csp->config->permissions_file, &fs))
1007    {
1008       /* No need to load */
1009       if (csp)
1010       {
1011          csp->permissions_list = current_permissions_file;
1012       }
1013       return 0;
1014    }
1015    if (!fs)
1016    {
1017       log_error(LOG_LEVEL_FATAL, "can't load permissions file '%s': error finding file: %E",
1018                 csp->config->permissions_file);
1019       return 1; /* never get here */
1020    }
1021
1022    fs->f = last_perm = (struct permissions_spec *)zalloc(sizeof(*last_perm));
1023    if (last_perm == NULL)
1024    {
1025       log_error(LOG_LEVEL_FATAL, "can't load permissions file '%s': out of memory!",
1026                 csp->config->permissions_file);
1027       return 1; /* never get here */
1028    }
1029
1030    if ((fp = fopen(csp->config->permissions_file, "r")) == NULL)
1031    {
1032       log_error(LOG_LEVEL_FATAL, "can't load permissions file '%s': error opening file: %E",
1033                 csp->config->permissions_file);
1034       return 1; /* never get here */
1035    }
1036
1037    while (read_config_line(buf, sizeof(buf), fp, fs) != NULL)
1038    {
1039       if (*buf == '{')
1040       {
1041          /* It's a header block */
1042          if (buf[1] == '{')
1043          {
1044             /* It's {{settings}} or {{alias}} */
1045             int len = strlen(buf);
1046             char * start = buf + 2;
1047             char * end = buf + len - 1;
1048             if ((len < 5) || (*end-- != '}') || (*end-- != '}'))
1049             {
1050                /* too short */
1051                fclose(fp);
1052                log_error(LOG_LEVEL_FATAL, 
1053                   "can't load permissions file '%s': invalid line: %s",
1054                   csp->config->permissions_file, buf);
1055                return 1; /* never get here */
1056             }
1057
1058             /* Trim leading and trailing whitespace. */
1059             while ((*end == ' ') || (*end == '\t'))
1060             {
1061                /*
1062                 * don't need to worry about going off front of string
1063                 * because we know there's a '{' there.
1064                 */
1065                end--;
1066             }
1067             end[1] = '\0';
1068             while ((*start == ' ') || (*start == '\t'))
1069             {
1070                start++;
1071             }
1072
1073             if (*start == '\0')
1074             {
1075                /* too short */
1076                fclose(fp);
1077                log_error(LOG_LEVEL_FATAL, 
1078                   "can't load permissions file '%s': invalid line: {{ }}",
1079                   csp->config->permissions_file);
1080                return 1; /* never get here */
1081             }
1082
1083             if (0 == strcmpic(start, "alias"))
1084             {
1085                /* it's an {{alias}} block */
1086
1087                mode = MODE_ALIAS;
1088             }
1089             else
1090             {
1091                /* invalid {{something}} block */
1092                fclose(fp);
1093                log_error(LOG_LEVEL_FATAL, 
1094                   "can't load permissions file '%s': invalid line: {{%s}}",
1095                   csp->config->permissions_file, start);
1096                return 1; /* never get here */
1097             }
1098          }
1099          else
1100          {
1101             /* It's a permissions block */
1102
1103             int more = 1;
1104
1105             int len = strlen(buf);
1106             char * start = buf + 1;
1107             char * end = buf + len - 1;
1108
1109             if ((len < 3) || (*end-- != '}'))
1110             {
1111                /* too short */
1112                fclose(fp);
1113                log_error(LOG_LEVEL_FATAL, 
1114                   "can't load permissions file '%s': invalid line: %s",
1115                   csp->config->permissions_file, buf);
1116                return 1; /* never get here */
1117             }
1118
1119             /* Trim leading and trailing whitespace. */
1120             while ((*end == ' ') || (*end == '\t'))
1121             {
1122                /*
1123                 * don't need to worry about going off front of string
1124                 * because we know there's a '{' there.
1125                 */
1126                end--;
1127             }
1128             end[1] = '\0';
1129             while ((*start == ' ') || (*start == '\t'))
1130             {
1131                start++;
1132             }
1133
1134             if (*start == '\0')
1135             {
1136                /* too short */
1137                fclose(fp);
1138                log_error(LOG_LEVEL_FATAL, 
1139                   "can't load permissions file '%s': invalid line: { }",
1140                   csp->config->permissions_file);
1141                return 1; /* never get here */
1142             }
1143
1144             mode    = MODE_PERMISSIONS;
1145             
1146             curmask = PERMIT_MASK_ALL;
1147             curadd  = 0;
1148
1149             while (more)
1150             {
1151                const struct permission_alias * alias = standard_aliases;
1152                char * option = start;
1153                while ((*start != '\0') && (*start != ' ') && (*start != '\t'))
1154                {
1155                   start++;
1156                }
1157
1158                more = (*start != 0);
1159                if (more)
1160                {
1161                   *start++ = '\0';
1162
1163                   /* Eat all the whitespace between the options */
1164                   while ((*start == ' ') || (*start == '\t'))
1165                   {
1166                      start++;
1167                   }
1168                }
1169
1170                /* handle option in 'option' */
1171
1172                /* Check for standard permission name */
1173                while ( (alias->name != NULL) && (0 != strcmpic(alias->name, option)) )
1174                {
1175                   alias++;
1176                }
1177                if (alias->name == NULL)
1178                {
1179                   /* try user aliases. */
1180                   alias = alias_list;
1181                   while ( (alias != NULL) && (0 != strcmpic(alias->name, option)) )
1182                   {
1183                      alias = alias->next;
1184                   }
1185                }
1186                if (alias == NULL)
1187                {
1188                   /* Bad permission name */
1189                   fclose(fp);
1190                   log_error(LOG_LEVEL_FATAL, 
1191                      "can't load permissions file '%s': invalid permission name: %s",
1192                      csp->config->permissions_file, option);
1193                   return 1; /* never get here */
1194                }
1195                curmask &= alias->mask;
1196                curadd  &= alias->mask;
1197                curadd  |= alias->add;
1198             }
1199          }
1200       }
1201       else if (mode == MODE_ALIAS)
1202       {
1203          /* define an alias */
1204          struct permission_alias * new_alias;
1205          int more = 1;
1206
1207          char * start = strchr(buf, '=');
1208          char * end = start;
1209
1210          if ((start == NULL) || (start == buf))
1211          {
1212             log_error(LOG_LEVEL_FATAL, 
1213                "can't load permissions file '%s': invalid alias line: %s",
1214                csp->config->permissions_file, buf);
1215             return 1; /* never get here */
1216          }
1217
1218          if ((new_alias = zalloc(sizeof(*new_alias))) == NULL)
1219          {
1220             fclose(fp);
1221             log_error(LOG_LEVEL_FATAL,
1222                "can't load permissions file '%s': out of memory!",
1223                csp->config->permissions_file);
1224             return 1; /* never get here */
1225          }
1226
1227          /* Eat any the whitespace after the '=' */
1228          start++;
1229          while ((*start == ' ') || (*start == '\t'))
1230          {
1231             start++;
1232          }
1233          if (*start == '\0')
1234          {
1235             log_error(LOG_LEVEL_FATAL, 
1236                "can't load permissions file '%s': invalid alias line: %s",
1237                csp->config->permissions_file, buf);
1238             return 1; /* never get here */
1239          }
1240
1241          /* Eat any the whitespace before the '=' */
1242          end--;
1243          while ((*end == ' ') || (*end == '\t'))
1244          {
1245             /*
1246              * we already know we must have at least 1 non-ws char
1247              * at start of buf - no need to check
1248              */
1249             end--;
1250          }
1251          end[1] = '\0';
1252
1253          new_alias->name = strdup(buf);
1254
1255          curmask = PERMIT_MASK_ALL;
1256          curadd  = 0;
1257
1258          while (more)
1259          {
1260             const struct permission_alias * alias = standard_aliases;
1261             char * option = start;
1262             while ((*start != '\0') && (*start != ' ') && (*start != '\t'))
1263             {
1264                start++;
1265             }
1266
1267             more = (*start != 0);
1268             if (more)
1269             {
1270                *start++ = '\0';
1271
1272                /* Eat all the whitespace between the options */
1273                while ((*start == ' ') || (*start == '\t'))
1274                {
1275                   start++;
1276                }
1277             }
1278
1279             /* handle option in 'option' */
1280
1281             /* Check for standard permission name */
1282             while ( (alias->name != NULL) && (0 != strcmpic(alias->name, option)) )
1283             {
1284                alias++;
1285             }
1286             if (alias->name == NULL)
1287             {
1288                /* try user aliases. */
1289                alias = alias_list;
1290                while ( (alias != NULL) && (0 != strcmpic(alias->name, option)) )
1291                {
1292                   alias = alias->next;
1293                }
1294             }
1295             if (alias == NULL)
1296             {
1297                /* Bad permission name */
1298                fclose(fp);
1299                log_error(LOG_LEVEL_FATAL, 
1300                   "can't load permissions file '%s': invalid permission name: %s",
1301                   csp->config->permissions_file, option);
1302                return 1; /* never get here */
1303             }
1304             curmask &= alias->mask;
1305             curadd  &= alias->mask;
1306             curadd  |= alias->add;
1307          }
1308
1309          /* save alias permissions */
1310          new_alias->mask = curmask;
1311          new_alias->add  = curadd;
1312          
1313          /* add to list */
1314          new_alias->next = alias_list;
1315          alias_list = new_alias;
1316       }
1317       else if (mode == MODE_PERMISSIONS)
1318       {
1319          /* it's a URL pattern */
1320
1321          /* allocate a new node */
1322          if ((perm = zalloc(sizeof(*perm))) == NULL)
1323          {
1324             fclose(fp);
1325             log_error(LOG_LEVEL_FATAL,
1326                "can't load permissions file '%s': out of memory!",
1327                csp->config->permissions_file);
1328             return 1; /* never get here */
1329          }
1330
1331          /* Save flags */
1332          perm->mask = curmask;
1333          perm->add = curadd;
1334
1335          /* Save the URL pattern */
1336          if (create_url_spec(perm->url, buf))
1337          {
1338             fclose(fp);
1339             log_error(LOG_LEVEL_FATAL, 
1340                "can't load permissions file '%s': cannot create URL permission from: %s",
1341                csp->config->permissions_file, buf);
1342             return 1; /* never get here */
1343          }
1344
1345          /* add it to the list */
1346          last_perm->next = perm;
1347          last_perm = perm;
1348       }
1349       else if (mode == MODE_START_OF_FILE)
1350       {
1351          /* oops - please have a {} line as 1st line in file. */
1352          fclose(fp);
1353          log_error(LOG_LEVEL_FATAL, 
1354             "can't load permissions file '%s': first line is invalid: %s",
1355             csp->config->permissions_file, buf);
1356          return 1; /* never get here */
1357       }
1358       else
1359       {
1360          /* How did we get here? This is impossible! */
1361          fclose(fp);
1362          log_error(LOG_LEVEL_FATAL, 
1363             "can't load permissions file '%s': INTERNAL ERROR - mode = %d",
1364             csp->config->permissions_file, mode);
1365          return 1; /* never get here */
1366       }
1367    }
1368
1369    fclose(fp);
1370    
1371    while (alias_list != NULL)
1372    {
1373       struct permission_alias * next = alias_list->next;
1374       freez((char *)alias_list->name);
1375       free(alias_list);
1376       alias_list = next;
1377    }
1378
1379 #ifndef SPLIT_PROXY_ARGS
1380    if (!suppress_blocklists)
1381    {
1382       fs->proxy_args = strsav(fs->proxy_args, "</pre>");
1383    }
1384 #endif /* ndef SPLIT_PROXY_ARGS */
1385
1386    /* the old one is now obsolete */
1387    if (current_permissions_file)
1388    {
1389       current_permissions_file->unloader = unload_permissions_file;
1390    }
1391
1392    fs->next    = files->next;
1393    files->next = fs;
1394    current_permissions_file = fs;
1395
1396    if (csp)
1397    {
1398       csp->permissions_list = fs;
1399    }
1400
1401    return(0);
1402
1403 }
1404
1405
1406 #ifdef TRUST_FILES
1407 /*********************************************************************
1408  *
1409  * Function    :  load_trustfile
1410  *
1411  * Description :  Read and parse a trustfile and add to files list.
1412  *
1413  * Parameters  :
1414  *          1  :  csp = Current client state (buffers, headers, etc...)
1415  *
1416  * Returns     :  0 => Ok, everything else is an error.
1417  *
1418  *********************************************************************/
1419 int load_trustfile(struct client_state *csp)
1420 {
1421    FILE *fp;
1422
1423    struct block_spec *b, *bl;
1424    struct url_spec **tl;
1425
1426    char  buf[BUFSIZ], *p, *q;
1427    int reject, trusted;
1428    struct file_list *fs;
1429
1430    if (!check_file_changed(current_trustfile, csp->config->trustfile, &fs))
1431    {
1432       /* No need to load */
1433       if (csp)
1434       {
1435          csp->tlist = current_trustfile;
1436       }
1437       return(0);
1438    }
1439    if (!fs)
1440    {
1441       goto load_trustfile_error;
1442    }
1443
1444    fs->f = bl = (struct block_spec *)zalloc(sizeof(*bl));
1445    if (bl == NULL)
1446    {
1447       goto load_trustfile_error;
1448    }
1449
1450    if ((fp = fopen(csp->config->trustfile, "r")) == NULL)
1451    {
1452       goto load_trustfile_error;
1453    }
1454
1455    tl = csp->config->trust_list;
1456
1457    while (read_config_line(buf, sizeof(buf), fp, fs) != NULL)
1458    {
1459       trusted = 0;
1460       reject  = 1;
1461
1462       if (*buf == '+')
1463       {
1464          trusted = 1;
1465          *buf = '~';
1466       }
1467
1468       if (*buf == '~')
1469       {
1470          reject = 0;
1471          p = buf;
1472          q = p+1;
1473          while ((*p++ = *q++))
1474          {
1475             /* nop */
1476          }
1477       }
1478
1479       /* skip blank lines */
1480       if (*buf == '\0')
1481       {
1482          continue;
1483       }
1484
1485       /* allocate a new node */
1486       if ((b = zalloc(sizeof(*b))) == NULL)
1487       {
1488          fclose(fp);
1489          goto load_trustfile_error;
1490       }
1491
1492       /* add it to the list */
1493       b->next  = bl->next;
1494       bl->next = b;
1495
1496       b->reject = reject;
1497
1498       /* Save the URL pattern */
1499       if (create_url_spec(b->url, buf))
1500       {
1501          fclose(fp);
1502          goto load_trustfile_error;
1503       }
1504
1505       /*
1506        * save a pointer to URL's spec in the list of trusted URL's, too
1507        */
1508       if (trusted)
1509       {
1510          *tl++ = b->url;
1511       }
1512    }
1513
1514    *tl = NULL;
1515
1516    fclose(fp);
1517
1518 #ifndef SPLIT_PROXY_ARGS
1519    if (!suppress_blocklists)
1520    {
1521       fs->proxy_args = strsav(fs->proxy_args, "</pre>");
1522    }
1523 #endif /* ndef SPLIT_PROXY_ARGS */
1524
1525    /* the old one is now obsolete */
1526    if (current_trustfile)
1527    {
1528       current_trustfile->unloader = unload_trustfile;
1529    }
1530
1531    fs->next    = files->next;
1532    files->next = fs;
1533    current_trustfile = fs;
1534
1535    if (csp)
1536    {
1537       csp->tlist = fs;
1538    }
1539
1540    return(0);
1541
1542 load_trustfile_error:
1543    log_error(LOG_LEVEL_FATAL, "can't load trustfile '%s': %E",
1544              csp->config->trustfile);
1545    return(-1);
1546
1547 }
1548 #endif /* def TRUST_FILES */
1549
1550
1551 /*********************************************************************
1552  *
1553  * Function    :  load_forwardfile
1554  *
1555  * Description :  Read and parse a forwardfile and add to files list.
1556  *
1557  * Parameters  :
1558  *          1  :  csp = Current client state (buffers, headers, etc...)
1559  *
1560  * Returns     :  0 => Ok, everything else is an error.
1561  *
1562  *********************************************************************/
1563 int load_forwardfile(struct client_state *csp)
1564 {
1565    FILE *fp;
1566
1567    struct forward_spec *b, *bl;
1568    char buf[BUFSIZ];
1569    char *p, *tmp;
1570    char *vec[4];
1571    int n;
1572    struct file_list *fs;
1573    const struct gateway *gw;
1574
1575    if (!check_file_changed(current_forwardfile, csp->config->forwardfile, &fs))
1576    {
1577       /* No need to load */
1578       if (csp)
1579       {
1580          csp->flist = current_forwardfile;
1581       }
1582       return(0);
1583    }
1584    if (!fs)
1585    {
1586       goto load_forwardfile_error;
1587    }
1588
1589    fs->f = bl = (struct forward_spec  *)zalloc(sizeof(*bl));
1590
1591    if ((fs == NULL) || (bl == NULL))
1592    {
1593       goto load_forwardfile_error;
1594    }
1595
1596    if ((fp = fopen(csp->config->forwardfile, "r")) == NULL)
1597    {
1598       goto load_forwardfile_error;
1599    }
1600
1601    tmp = NULL;
1602
1603    while (read_config_line(buf, sizeof(buf), fp, fs) != NULL)
1604    {
1605       freez(tmp);
1606
1607       tmp = strdup(buf);
1608
1609       n = ssplit(tmp, " \t", vec, SZ(vec), 1, 1);
1610
1611       if (n != 4)
1612       {
1613          log_error(LOG_LEVEL_ERROR, "error in forwardfile: %s", buf);
1614          continue;
1615       }
1616
1617       strcpy(buf, vec[0]);
1618
1619       /* skip lines containing only ~ */
1620       if (*buf == '\0')
1621       {
1622          continue;
1623       }
1624
1625       /* allocate a new node */
1626       if (((b = zalloc(sizeof(*b))) == NULL)
1627       )
1628       {
1629          fclose(fp);
1630          goto load_forwardfile_error;
1631       }
1632
1633       /* add it to the list */
1634       b->next  = bl->next;
1635       bl->next = b;
1636
1637       /* Save the URL pattern */
1638       if (create_url_spec(b->url, buf))
1639       {
1640          fclose(fp);
1641          goto load_forwardfile_error;
1642       }
1643
1644       /* now parse the gateway specs */
1645
1646       p = vec[2];
1647
1648       for (gw = gateways; gw->name; gw++)
1649       {
1650          if (strcmp(gw->name, p) == 0)
1651          {
1652             break;
1653          }
1654       }
1655
1656       if (gw->name == NULL)
1657       {
1658          goto load_forwardfile_error;
1659       }
1660
1661       /* save this as the gateway type */
1662       *b->gw = *gw;
1663
1664       /* now parse the gateway host[:port] spec */
1665       p = vec[3];
1666
1667       if (strcmp(p, ".") != 0)
1668       {
1669          b->gw->gateway_host = strdup(p);
1670
1671          if ((p = strchr(b->gw->gateway_host, ':')))
1672          {
1673             *p++ = '\0';
1674             b->gw->gateway_port = atoi(p);
1675          }
1676
1677          if (b->gw->gateway_port <= 0)
1678          {
1679             goto load_forwardfile_error;
1680          }
1681       }
1682
1683       /* now parse the forwarding spec */
1684       p = vec[1];
1685
1686       if (strcmp(p, ".") != 0)
1687       {
1688          b->gw->forward_host = strdup(p);
1689
1690          if ((p = strchr(b->gw->forward_host, ':')))
1691          {
1692             *p++ = '\0';
1693             b->gw->forward_port = atoi(p);
1694          }
1695
1696          if (b->gw->forward_port <= 0)
1697          {
1698             b->gw->forward_port = 8000;
1699          }
1700       }
1701    }
1702
1703    freez(tmp);
1704
1705    fclose(fp);
1706
1707 #ifndef SPLIT_PROXY_ARGS
1708    if (!suppress_blocklists)
1709    {
1710       fs->proxy_args = strsav(fs->proxy_args, "</pre>");
1711    }
1712 #endif /* ndef SPLIT_PROXY_ARGS */
1713
1714    /* the old one is now obsolete */
1715    if (current_forwardfile)
1716    {
1717       current_forwardfile->unloader = unload_forwardfile;
1718    }
1719
1720    fs->next    = files->next;
1721    files->next = fs;
1722    current_forwardfile = fs;
1723
1724    if (csp)
1725    {
1726       csp->flist = fs;
1727    }
1728
1729    return(0);
1730
1731 load_forwardfile_error:
1732    log_error(LOG_LEVEL_FATAL, "can't load forwardfile '%s': %E",
1733              csp->config->forwardfile);
1734    return(-1);
1735
1736 }
1737
1738
1739 #ifdef PCRS
1740 /*********************************************************************
1741  *
1742  * Function    :  load_re_filterfile
1743  *
1744  * Description :  Load the re_filterfile. Each non-comment, non-empty
1745  *                line is instantly added to the joblist, which is
1746  *                a chained list of pcrs_job structs.
1747  *
1748  * Parameters  :
1749  *          1  :  csp = Current client state (buffers, headers, etc...)
1750  *
1751  * Returns     :  0 => Ok, everything else is an error.
1752  *
1753  *********************************************************************/
1754 int load_re_filterfile(struct client_state *csp)
1755 {
1756    FILE *fp;
1757
1758    struct re_filterfile_spec *bl;
1759    struct file_list *fs;
1760
1761    char  buf[BUFSIZ];
1762    int error;
1763    pcrs_job *dummy;
1764
1765    if (!check_file_changed(current_re_filterfile, csp->config->re_filterfile, &fs))
1766    {
1767       /* No need to load */
1768       if (csp)
1769       {
1770          csp->rlist = current_re_filterfile;
1771       }
1772       return(0);
1773    }
1774    if (!fs)
1775    {
1776       goto load_re_filterfile_error;
1777    }
1778
1779    fs->f = bl = (struct re_filterfile_spec  *)zalloc(sizeof(*bl));
1780    if (bl == NULL)
1781    {
1782       goto load_re_filterfile_error;
1783    }
1784
1785    /* Open the file or fail */
1786    if ((fp = fopen(csp->config->re_filterfile, "r")) == NULL)
1787    {
1788       goto load_re_filterfile_error;
1789    }
1790
1791    /* Read line by line */
1792    while (read_config_line(buf, sizeof(buf), fp, fs) != NULL)
1793    {
1794       enlist( bl->patterns, buf );
1795
1796       /* We have a meaningful line -> make it a job */
1797       if ((dummy = pcrs_make_job(buf, &error)) == NULL)
1798       {
1799          log_error(LOG_LEVEL_RE_FILTER, 
1800                "Adding re_filter job %s failed with error %d.", buf, error);
1801          continue;
1802       }
1803       else
1804       {
1805          dummy->next = bl->joblist;
1806          bl->joblist = dummy;
1807          log_error(LOG_LEVEL_RE_FILTER, "Adding re_filter job %s succeeded.", buf);
1808       }
1809    }
1810
1811    fclose(fp);
1812
1813 #ifndef SPLIT_PROXY_ARGS
1814    if (!suppress_blocklists)
1815    {
1816       fs->proxy_args = strsav(fs->proxy_args, "</pre>");
1817    }
1818 #endif /* ndef SPLIT_PROXY_ARGS */
1819
1820    /* the old one is now obsolete */
1821    if ( NULL != current_re_filterfile )
1822    {
1823       current_re_filterfile->unloader = unload_re_filterfile;
1824    }
1825
1826    fs->next    = files->next;
1827    files->next = fs;
1828    current_re_filterfile = fs;
1829
1830    if (csp)
1831    {
1832       csp->rlist = fs;
1833    }
1834
1835    return( 0 );
1836
1837 load_re_filterfile_error:
1838    log_error(LOG_LEVEL_FATAL, "can't load re_filterfile '%s': %E", 
1839              csp->config->re_filterfile);
1840    return(-1);
1841
1842 }
1843 #endif /* def PCRS */
1844
1845
1846 /*********************************************************************
1847  *
1848  * Function    :  add_loader
1849  *
1850  * Description :  Called from `load_config'.  Called once for each input
1851  *                file found in config.
1852  *
1853  * Parameters  :
1854  *          1  :  loader = pointer to a function that can parse and load
1855  *                the appropriate config file.
1856  *          2  :  config = The configuration_spec to add the loader to.
1857  *
1858  * Returns     :  N/A
1859  *
1860  *********************************************************************/
1861 void add_loader(int (*loader)(struct client_state *), 
1862                 struct configuration_spec * config)
1863 {
1864    int i;
1865
1866    for (i=0; i < NLOADERS; i++)
1867    {
1868       if (config->loaders[i] == NULL)
1869       {
1870          config->loaders[i] = loader;
1871          break;
1872       }
1873    }
1874
1875 }
1876
1877
1878 /*********************************************************************
1879  *
1880  * Function    :  run_loader
1881  *
1882  * Description :  Called from `load_config' and `listen_loop'.  This
1883  *                function keeps the "csp" current with any file mods
1884  *                since the last loop.  If a file is unchanged, the
1885  *                loader functions do NOT reload the file.
1886  *
1887  * Parameters  :
1888  *          1  :  csp = Current client state (buffers, headers, etc...)
1889  *                      Must be non-null.  Reads: "csp->config"
1890  *                      Writes: various data members.
1891  *
1892  * Returns     :  0 => Ok, everything else is an error.
1893  *
1894  *********************************************************************/
1895 int run_loader(struct client_state *csp)
1896 {
1897    int ret = 0;
1898    int i;
1899
1900    for (i=0; i < NLOADERS; i++)
1901    {
1902       if (csp->config->loaders[i] == NULL)
1903       {
1904          break;
1905       }
1906       ret |= (csp->config->loaders[i])(csp);
1907    }
1908    return(ret);
1909
1910 }
1911
1912
1913 /*
1914   Local Variables:
1915   tab-width: 3
1916   end:
1917 */