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