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