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