Removed all #ifdef PCRS and related code
[privoxy.git] / loaders.c
1 const char loaders_rcs[] = "$Id: loaders.c,v 1.17 2001/06/29 13:31:51 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.17  2001/06/29 13:31:51  oes
39  *    Various adaptions
40  *
41  *    Revision 1.16  2001/06/09 10:55:28  jongfoster
42  *    Changing BUFSIZ ==> BUFFER_SIZE
43  *
44  *    Revision 1.15  2001/06/07 23:14:14  jongfoster
45  *    Removing ACL and forward file loaders - these
46  *    files have been merged into the config file.
47  *    Cosmetic: Moving unloader funcs next to their
48  *    respective loader funcs
49  *
50  *    Revision 1.14  2001/06/01 03:27:04  oes
51  *    Fixed line continuation problem
52  *
53  *    Revision 1.13  2001/05/31 21:28:49  jongfoster
54  *    Removed all permissionsfile code - it's now called the actions
55  *    file, and (almost) all the code is in actions.c
56  *
57  *    Revision 1.12  2001/05/31 17:32:31  oes
58  *
59  *     - Enhanced domain part globbing with infix and prefix asterisk
60  *       matching and optional unanchored operation
61  *
62  *    Revision 1.11  2001/05/29 23:25:24  oes
63  *
64  *     - load_config_line() and load_permissions_file() now use chomp()
65  *
66  *    Revision 1.10  2001/05/29 09:50:24  jongfoster
67  *    Unified blocklist/imagelist/permissionslist.
68  *    File format is still under discussion, but the internal changes
69  *    are (mostly) done.
70  *
71  *    Also modified interceptor behaviour:
72  *    - We now intercept all URLs beginning with one of the following
73  *      prefixes (and *only* these prefixes):
74  *        * http://i.j.b/
75  *        * http://ijbswa.sf.net/config/
76  *        * http://ijbswa.sourceforge.net/config/
77  *    - New interceptors "home page" - go to http://i.j.b/ to see it.
78  *    - Internal changes so that intercepted and fast redirect pages
79  *      are not replaced with an image.
80  *    - Interceptors now have the option to send a binary page direct
81  *      to the client. (i.e. ijb-send-banner uses this)
82  *    - Implemented show-url-info interceptor.  (Which is why I needed
83  *      the above interceptors changes - a typical URL is
84  *      "http://i.j.b/show-url-info?url=www.somesite.com/banner.gif".
85  *      The previous mechanism would not have intercepted that, and
86  *      if it had been intercepted then it then it would have replaced
87  *      it with an image.)
88  *
89  *    Revision 1.9  2001/05/26 17:12:07  jongfoster
90  *    Fatal errors loading configuration files now give better error messages.
91  *
92  *    Revision 1.8  2001/05/26 00:55:20  jongfoster
93  *    Removing duplicated code.  load_forwardfile() now uses create_url_spec()
94  *
95  *    Revision 1.7  2001/05/26 00:28:36  jongfoster
96  *    Automatic reloading of config file.
97  *    Removed obsolete SIGHUP support (Unix) and Reload menu option (Win32).
98  *    Most of the global variables have been moved to a new
99  *    struct configuration_spec, accessed through csp->config->globalname
100  *    Most of the globals remaining are used by the Win32 GUI.
101  *
102  *    Revision 1.6  2001/05/23 12:27:33  oes
103  *
104  *    Fixed ugly indentation of my last changes
105  *
106  *    Revision 1.5  2001/05/23 10:39:05  oes
107  *    - Added support for escaping the comment character
108  *      in config files by a backslash
109  *    - Added support for line continuation in config
110  *      files
111  *    - Fixed a buffer overflow bug with long config lines
112  *
113  *    Revision 1.4  2001/05/22 18:56:28  oes
114  *    CRLF -> LF
115  *
116  *    Revision 1.3  2001/05/20 01:21:20  jongfoster
117  *    Version 2.9.4 checkin.
118  *    - Merged popupfile and cookiefile, and added control over PCRS
119  *      filtering, in new "permissionsfile".
120  *    - Implemented LOG_LEVEL_FATAL, so that if there is a configuration
121  *      file error you now get a message box (in the Win32 GUI) rather
122  *      than the program exiting with no explanation.
123  *    - Made killpopup use the PCRS MIME-type checking and HTTP-header
124  *      skipping.
125  *    - Removed tabs from "config"
126  *    - Moved duplicated url parsing code in "loaders.c" to a new funcition.
127  *    - Bumped up version number.
128  *
129  *    Revision 1.2  2001/05/17 23:01:01  oes
130  *     - Cleaned CRLF's from the sources and related files
131  *
132  *    Revision 1.1.1.1  2001/05/15 13:58:59  oes
133  *    Initial import of version 2.9.3 source tree
134  *
135  *
136  *********************************************************************/
137 \f
138
139 #include "config.h"
140
141 #include <stdio.h>
142 #include <stdlib.h>
143 #include <sys/types.h>
144 #include <string.h>
145 #include <malloc.h>
146 #include <errno.h>
147 #include <sys/stat.h>
148 #include <ctype.h>
149
150 #ifndef _WIN32
151 #include <unistd.h>
152 #endif
153
154 #include "project.h"
155 #include "list.h"
156 #include "loaders.h"
157 #include "encode.h"
158 #include "filters.h"
159 #include "parsers.h"
160 #include "jcc.h"
161 #include "ssplit.h"
162 #include "miscutil.h"
163 #include "errlog.h"
164 #include "gateway.h"
165 #include "actions.h"
166
167 const char loaders_h_rcs[] = LOADERS_H_VERSION;
168
169 /* Fix a problem with Solaris.  There should be no effect on other
170  * platforms.
171  * Solaris's isspace() is a macro which uses it's argument directly
172  * as an array index.  Therefore we need to make sure that high-bit
173  * characters generate +ve values, and ideally we also want to make
174  * the argument match the declared parameter type of "int".
175  */
176 #define ijb_isspace(__X) isspace((int)(unsigned char)(__X))
177
178
179 /*
180  * Currently active files.
181  * These are also entered in the main linked list of files.
182  */
183
184 #ifdef TRUST_FILES
185 static struct file_list *current_trustfile      = NULL;
186 #endif /* def TRUST_FILES */
187
188 #ifdef PCRS
189 static struct file_list *current_re_filterfile  = NULL;
190 #endif /* def PCRS */
191
192
193 /*********************************************************************
194  *
195  * Function    :  sweep
196  *
197  * Description :  Basically a mark and sweep garbage collector, it is run
198  *                (by the parent thread) every once in a while to reclaim memory.
199  *
200  * It uses a mark and sweep strategy:
201  *   1) mark all files as inactive
202  *
203  *   2) check with each client:
204  *       if it is active,   mark its files as active
205  *       if it is inactive, free its resources
206  *
207  *   3) free the resources of all of the files that
208  *      are still marked as inactive (and are obsolete).
209  *
210  *   N.B. files that are not obsolete don't have an unloader defined.
211  *
212  * Parameters  :  None
213  *
214  * Returns     :  N/A
215  *
216  *********************************************************************/
217 void sweep(void)
218 {
219    struct file_list *fl, *nfl;
220    struct client_state *csp, *ncsp;
221
222    /* clear all of the file's active flags */
223    for ( fl = files->next; NULL != fl; fl = fl->next )
224    {
225       fl->active = 0;
226    }
227
228    for (csp = clients; csp && (ncsp = csp->next) ; csp = csp->next)
229    {
230       if (ncsp->active)
231       {
232          /* mark this client's files as active */
233
234          /*
235           * Always have a configuration file.
236           * (Also note the slightly non-standard extra
237           * indirection here.)
238           */
239          ncsp->config->config_file_list->active = 1;
240
241          if (ncsp->actions_list)     /* actions files */
242          {
243             ncsp->actions_list->active = 1;
244          }
245
246 #ifdef PCRS
247          if (ncsp->rlist)     /* perl re files */
248          {
249             ncsp->rlist->active = 1;
250          }
251 #endif /* def PCRS */
252
253 #ifdef TRUST_FILES
254          if (ncsp->tlist)     /* trust files */
255          {
256             ncsp->tlist->active = 1;
257          }
258 #endif /* def TRUST_FILES */
259
260       }
261       else
262       {
263          /* this client one is not active, release its resources */
264          csp->next = ncsp->next;
265
266          freez(ncsp->ip_addr_str);
267          freez(ncsp->my_ip_addr_str);
268          freez(ncsp->my_hostname);
269
270 #ifdef TRUST_FILES
271          freez(ncsp->referrer);
272 #endif /* def TRUST_FILES */
273          freez(ncsp->x_forwarded);
274          freez(ncsp->iob->buf);
275
276          free_http_request(ncsp->http);
277
278          destroy_list(ncsp->headers);
279          destroy_list(ncsp->cookie_list);
280
281          free_current_action(ncsp->action);
282
283 #ifdef STATISTICS
284          urls_read++;
285          if (ncsp->rejected)
286          {
287             urls_rejected++;
288          }
289 #endif /* def STATISTICS */
290
291          freez(ncsp);
292       }
293    }
294
295    for (fl = files; fl && (nfl = fl->next) ; fl = fl->next)
296    {
297       if ( ( 0 == nfl->active ) && ( NULL != nfl->unloader ) )
298       {
299          fl->next = nfl->next;
300
301          (nfl->unloader)(nfl->f);
302
303 #ifndef SPLIT_PROXY_ARGS
304          freez(nfl->proxy_args);
305 #endif /* ndef SPLIT_PROXY_ARGS */
306
307          freez(nfl->filename);
308
309          freez(nfl);
310       }
311    }
312
313 }
314
315
316 /*********************************************************************
317  *
318  * Function    :  create_url_spec
319  *
320  * Description :  Creates a "url_spec" structure from a string.
321  *                When finished, free with unload_url().
322  *
323  * Parameters  :
324  *          1  :  url = Target url_spec to be filled in.  Must be
325  *                      zeroed out before the call (e.g. using zalloc).
326  *          2  :  buf = Source pattern, null terminated.  NOTE: The
327  *                      contents of this buffer are destroyed by this
328  *                      function.  If this function succeeds, the
329  *                      buffer is copied to url->spec.  If this
330  *                      function fails, the contents of the buffer
331  *                      are lost forever.
332  *
333  * Returns     :  0 => Ok, everything else is an error.
334  *
335  *********************************************************************/
336 int create_url_spec(struct url_spec * url, char * buf)
337 {
338    char *p;
339    struct url_spec tmp_url[1];
340
341    /* paranoia - should never happen. */
342    if ((url == NULL) || (buf == NULL))
343    {
344       return 1;
345    }
346
347    /* save a copy of the orignal specification */
348    if ((url->spec = strdup(buf)) == NULL)
349    {
350       return 1;
351    }
352
353    if ((p = strchr(buf, '/')))
354    {
355       if (NULL == (url->path = strdup(p)))
356       {
357          freez(url->spec);
358          return 1;
359       }
360       url->pathlen = strlen(url->path);
361       *p = '\0';
362    }
363    else
364    {
365       url->path    = NULL;
366       url->pathlen = 0;
367    }
368 #ifdef REGEX
369    if (url->path)
370    {
371       int errcode;
372       char rebuf[BUFFER_SIZE];
373
374       if (NULL == (url->preg = zalloc(sizeof(*url->preg))))
375       {
376          freez(url->spec);
377          freez(url->path);
378          return 1;
379       }
380
381       sprintf(rebuf, "^(%s)", url->path);
382
383       errcode = regcomp(url->preg, rebuf,
384             (REG_EXTENDED|REG_NOSUB|REG_ICASE));
385       if (errcode)
386       {
387          size_t errlen = regerror(errcode,
388             url->preg, buf, sizeof(buf));
389
390          buf[errlen] = '\0';
391
392          log_error(LOG_LEVEL_ERROR, "error compiling %s: %s",
393             url->spec, buf);
394
395          freez(url->spec);
396          freez(url->path);
397          freez(url->preg);
398
399          return 1;
400       }
401    }
402 #endif
403    if ((p = strchr(buf, ':')) == NULL)
404    {
405       url->port = 0;
406    }
407    else
408    {
409       *p++ = '\0';
410       url->port = atoi(p);
411    }
412
413    if ((url->domain = strdup(buf)) == NULL)
414    {
415       freez(url->spec);
416       freez(url->path);
417 #ifdef REGEX
418       freez(url->preg);
419 #endif /* def REGEX */
420       return 1;
421    }
422
423    /* split domain into components */
424
425    *tmp_url = dsplit(url->domain);
426    url->dbuf = tmp_url->dbuf;
427    url->dcnt = tmp_url->dcnt;
428    url->dvec = tmp_url->dvec;
429    url->unanchored = tmp_url->unanchored;
430
431    return 0; /* OK */
432
433 }
434
435
436 /*********************************************************************
437  *
438  * Function    :  free_url
439  *
440  * Description :  Called from the "unloaders".  Freez the url
441  *                structure elements.
442  *
443  * Parameters  :
444  *          1  :  url = pointer to a url_spec structure.
445  *
446  * Returns     :  N/A
447  *
448  *********************************************************************/
449 void free_url(struct url_spec *url)
450 {
451    if (url == NULL) return;
452
453    freez(url->spec);
454    freez(url->domain);
455    freez(url->dbuf);
456    freez(url->dvec);
457    freez(url->path);
458 #ifdef REGEX
459    if (url->preg)
460    {
461       regfree(url->preg);
462       freez(url->preg);
463    }
464 #endif
465
466 }
467
468
469 /*********************************************************************
470  *
471  * Function    :  check_file_changed
472  *
473  * Description :  Helper function to check if a file needs reloading.
474  *                If "current" is still current, return it.  Otherwise
475  *                allocates a new (zeroed) "struct file_list", fills 
476  *                in the disk file name and timestamp, and returns it.
477  *
478  * Parameters  :
479  *          1  :  current = The file_list currently being used - will
480  *                          be checked to see if it is out of date. 
481  *                          May be NULL (which is treated as out of
482  *                          date).
483  *          2  :  filename = Name of file to check.
484  *          3  :  newfl    = New file list. [Output only]
485  *                           This will be set to NULL, OR a struct
486  *                           file_list newly allocated on the
487  *                           heap, with the filename and lastmodified
488  *                           fields filled, standard header giving file
489  *                           name in proxy_args, and all others zeroed.
490  *                           (proxy_args is only filled in if !defined
491  *                           SPLIT_PROXY_ARGS and !suppress_blocklists).
492  *
493  * Returns     :  If file unchanged: 0 (and sets newfl == NULL)
494  *                If file changed: 1 and sets newfl != NULL
495  *                On error: 1 and sets newfl == NULL
496  *
497  *********************************************************************/
498 int check_file_changed(const struct file_list * current,
499                        const char * filename,
500                        struct file_list ** newfl)
501 {
502    struct file_list *fs;
503    struct stat statbuf[1];
504
505    *newfl = NULL;
506
507    if (stat(filename, statbuf) < 0)
508    {
509       /* Error, probably file not found. */
510       return 1;
511    }
512
513    if (current
514        && (current->lastmodified == statbuf->st_mtime)
515        && (0 == strcmp(current->filename, filename)))
516    {
517       return 0;
518    }
519
520    fs = (struct file_list *)zalloc(sizeof(struct file_list));
521
522    if (fs == NULL)
523    {
524       /* Out of memory error */
525       return 1;
526    }
527
528    fs->filename = strdup(filename);
529    fs->lastmodified = statbuf->st_mtime;
530
531    if (fs->filename == NULL)
532    {
533       /* Out of memory error */
534       freez (fs);
535       return 1;
536    }
537
538 #ifndef SPLIT_PROXY_ARGS
539    if (!suppress_blocklists)
540    {
541       char * p = html_encode(filename);
542       if (p)
543       {
544          fs->proxy_args = strsav(fs->proxy_args, "<h2>The file `");
545          fs->proxy_args = strsav(fs->proxy_args, p);
546          fs->proxy_args = strsav(fs->proxy_args, 
547             "' contains the following patterns</h2>\n");
548          freez(p);
549       }
550       fs->proxy_args = strsav(fs->proxy_args, "<pre>");
551    }
552 #endif /* ndef SPLIT_PROXY_ARGS */
553
554    *newfl = fs;
555    return 1;
556
557 }
558
559
560 /*********************************************************************
561  *
562  * Function    :  read_config_line
563  *
564  * Description :  Read a single non-empty line from a file and return
565  *                it.  Trims comments, leading and trailing whitespace
566  *                and respects escaping of newline and comment char.
567  *                Also writes the file to fs->proxy_args.
568  *
569  * Parameters  :
570  *          1  :  buf = Buffer to use.
571  *          2  :  buflen = Size of buffer in bytes.
572  *          3  :  fp = File to read from
573  *          4  :  fs = File will be written to fs->proxy_args.  May
574  *                be NULL to disable this feature.
575  *
576  * Returns     :  NULL on EOF or error
577  *                Otherwise, returns buf.
578  *
579  *********************************************************************/
580 char *read_config_line(char *buf, int buflen, FILE *fp, struct file_list *fs)
581 {
582    char *p, *q;
583    char linebuf[BUFFER_SIZE];
584    int contflag = 0;
585
586    *buf = '\0';
587
588    while (fgets(linebuf, sizeof(linebuf), fp))
589    {
590 #ifndef SPLIT_PROXY_ARGS
591       if (fs && !suppress_blocklists)
592       {
593          char *html_line = html_encode(linebuf);
594          if (html_line != NULL)
595          {
596             fs->proxy_args = strsav(fs->proxy_args, html_line);
597             freez(html_line);
598          }
599          fs->proxy_args = strsav(fs->proxy_args, "<br>");
600       }
601 #endif /* ndef SPLIT_PROXY_ARGS */
602
603       /* Trim off newline */
604       if ((p = strpbrk(linebuf, "\r\n")) != NULL)
605       {
606          *p = '\0';
607       }
608
609       /* Line continuation? Trim escape and set flag. */
610       if ((p != linebuf) && (*--p == '\\'))
611       {
612          contflag = 1;
613          *p = '\0';
614       }
615
616       /* If there's a comment char.. */
617       if ((p = strpbrk(linebuf, "#")) != NULL)
618       {
619          /* ..and it's escaped, left-shift the line over the escape. */
620          if ((p != linebuf) && (*(p-1) == '\\'))
621          {
622             q = p-1;
623             while ((*q++ = *p++) != '\0') /* nop */;
624          }
625          /* Else, chop off the rest of the line */
626          else
627          {
628             *p = '\0';
629          }
630       }
631
632       /* Write to the buffer */
633       if (*linebuf)
634       {
635          strncat(buf, linebuf, buflen - strlen(buf));
636       }
637
638       /* Continue? */
639       if (contflag)
640       {
641          contflag = 0;
642                 continue;
643       }
644
645       /* Remove leading and trailing whitespace */         
646       chomp(buf);
647
648       if (*buf)
649       {
650          return buf;
651       }
652    }
653
654    /* EOF */
655    return NULL;
656
657 }
658
659
660 #ifdef TRUST_FILES
661 /*********************************************************************
662  *
663  * Function    :  unload_trustfile
664  *
665  * Description :  Unloads a trustfile.
666  *
667  * Parameters  :
668  *          1  :  f = the data structure associated with the trustfile.
669  *
670  * Returns     :  N/A
671  *
672  *********************************************************************/
673 static void unload_trustfile(void *f)
674 {
675    struct block_spec *b = (struct block_spec *)f;
676    if (b == NULL) return;
677
678    unload_trustfile(b->next); /* Stack is cheap, isn't it? */
679
680    free_url(b->url);
681
682    freez(b);
683
684 }
685
686
687 /*********************************************************************
688  *
689  * Function    :  load_trustfile
690  *
691  * Description :  Read and parse a trustfile and add to files list.
692  *
693  * Parameters  :
694  *          1  :  csp = Current client state (buffers, headers, etc...)
695  *
696  * Returns     :  0 => Ok, everything else is an error.
697  *
698  *********************************************************************/
699 int load_trustfile(struct client_state *csp)
700 {
701    FILE *fp;
702
703    struct block_spec *b, *bl;
704    struct url_spec **tl;
705
706    char  buf[BUFFER_SIZE], *p, *q;
707    int reject, trusted;
708    struct file_list *fs;
709
710    if (!check_file_changed(current_trustfile, csp->config->trustfile, &fs))
711    {
712       /* No need to load */
713       if (csp)
714       {
715          csp->tlist = current_trustfile;
716       }
717       return(0);
718    }
719    if (!fs)
720    {
721       goto load_trustfile_error;
722    }
723
724    fs->f = bl = (struct block_spec *)zalloc(sizeof(*bl));
725    if (bl == NULL)
726    {
727       goto load_trustfile_error;
728    }
729
730    if ((fp = fopen(csp->config->trustfile, "r")) == NULL)
731    {
732       goto load_trustfile_error;
733    }
734
735    tl = csp->config->trust_list;
736
737    while (read_config_line(buf, sizeof(buf), fp, fs) != NULL)
738    {
739       trusted = 0;
740       reject  = 1;
741
742       if (*buf == '+')
743       {
744          trusted = 1;
745          *buf = '~';
746       }
747
748       if (*buf == '~')
749       {
750          reject = 0;
751          p = buf;
752          q = p+1;
753          while ((*p++ = *q++))
754          {
755             /* nop */
756          }
757       }
758
759       /* skip blank lines */
760       if (*buf == '\0')
761       {
762          continue;
763       }
764
765       /* allocate a new node */
766       if ((b = zalloc(sizeof(*b))) == NULL)
767       {
768          fclose(fp);
769          goto load_trustfile_error;
770       }
771
772       /* add it to the list */
773       b->next  = bl->next;
774       bl->next = b;
775
776       b->reject = reject;
777
778       /* Save the URL pattern */
779       if (create_url_spec(b->url, buf))
780       {
781          fclose(fp);
782          goto load_trustfile_error;
783       }
784
785       /*
786        * save a pointer to URL's spec in the list of trusted URL's, too
787        */
788       if (trusted)
789       {
790          *tl++ = b->url;
791       }
792    }
793
794    *tl = NULL;
795
796    fclose(fp);
797
798 #ifndef SPLIT_PROXY_ARGS
799    if (!suppress_blocklists)
800    {
801       fs->proxy_args = strsav(fs->proxy_args, "</pre>");
802    }
803 #endif /* ndef SPLIT_PROXY_ARGS */
804
805    /* the old one is now obsolete */
806    if (current_trustfile)
807    {
808       current_trustfile->unloader = unload_trustfile;
809    }
810
811    fs->next    = files->next;
812    files->next = fs;
813    current_trustfile = fs;
814
815    if (csp)
816    {
817       csp->tlist = fs;
818    }
819
820    return(0);
821
822 load_trustfile_error:
823    log_error(LOG_LEVEL_FATAL, "can't load trustfile '%s': %E",
824              csp->config->trustfile);
825    return(-1);
826
827 }
828 #endif /* def TRUST_FILES */
829
830
831 #ifdef PCRS
832 /*********************************************************************
833  *
834  * Function    :  unload_re_filterfile
835  *
836  * Description :  Unload the re_filter list.
837  *
838  * Parameters  :
839  *          1  :  f = the data structure associated with the filterfile.
840  *
841  * Returns     :  N/A
842  *
843  *********************************************************************/
844 static void unload_re_filterfile(void *f)
845 {
846    struct re_filterfile_spec *b = (struct re_filterfile_spec *)f;
847
848    if (b == NULL) return;
849
850    destroy_list(b->patterns);
851    pcrs_free_joblist(b->joblist);
852    freez(b);
853
854    return;
855 }
856
857 /*********************************************************************
858  *
859  * Function    :  load_re_filterfile
860  *
861  * Description :  Load the re_filterfile. Each non-comment, non-empty
862  *                line is instantly added to the joblist, which is
863  *                a chained list of pcrs_job structs.
864  *
865  * Parameters  :
866  *          1  :  csp = Current client state (buffers, headers, etc...)
867  *
868  * Returns     :  0 => Ok, everything else is an error.
869  *
870  *********************************************************************/
871 int load_re_filterfile(struct client_state *csp)
872 {
873    FILE *fp;
874
875    struct re_filterfile_spec *bl;
876    struct file_list *fs;
877
878    char  buf[BUFFER_SIZE];
879    int error;
880    pcrs_job *dummy;
881
882    if (!check_file_changed(current_re_filterfile, csp->config->re_filterfile, &fs))
883    {
884       /* No need to load */
885       if (csp)
886       {
887          csp->rlist = current_re_filterfile;
888       }
889       return(0);
890    }
891    if (!fs)
892    {
893       goto load_re_filterfile_error;
894    }
895
896    fs->f = bl = (struct re_filterfile_spec  *)zalloc(sizeof(*bl));
897    if (bl == NULL)
898    {
899       goto load_re_filterfile_error;
900    }
901
902    /* Open the file or fail */
903    if ((fp = fopen(csp->config->re_filterfile, "r")) == NULL)
904    {
905       goto load_re_filterfile_error;
906    }
907
908    /* Read line by line */
909    while (read_config_line(buf, sizeof(buf), fp, fs) != NULL)
910    {
911       enlist( bl->patterns, buf );
912
913       /* We have a meaningful line -> make it a job */
914       if ((dummy = pcrs_compile(buf, &error)) == NULL)
915       {
916          log_error(LOG_LEVEL_RE_FILTER, 
917                "Adding re_filter job %s failed with error %d.", buf, error);
918          continue;
919       }
920       else
921       {
922          dummy->next = bl->joblist;
923          bl->joblist = dummy;
924          log_error(LOG_LEVEL_RE_FILTER, "Adding re_filter job %s succeeded.", buf);
925       }
926    }
927
928    fclose(fp);
929
930 #ifndef SPLIT_PROXY_ARGS
931    if (!suppress_blocklists)
932    {
933       fs->proxy_args = strsav(fs->proxy_args, "</pre>");
934    }
935 #endif /* ndef SPLIT_PROXY_ARGS */
936
937    /* the old one is now obsolete */
938    if ( NULL != current_re_filterfile )
939    {
940       current_re_filterfile->unloader = unload_re_filterfile;
941    }
942
943    fs->next    = files->next;
944    files->next = fs;
945    current_re_filterfile = fs;
946
947    if (csp)
948    {
949       csp->rlist = fs;
950    }
951
952    return( 0 );
953
954 load_re_filterfile_error:
955    log_error(LOG_LEVEL_FATAL, "can't load re_filterfile '%s': %E", 
956              csp->config->re_filterfile);
957    return(-1);
958
959 }
960 #endif /* def PCRS */
961
962
963 /*********************************************************************
964  *
965  * Function    :  add_loader
966  *
967  * Description :  Called from `load_config'.  Called once for each input
968  *                file found in config.
969  *
970  * Parameters  :
971  *          1  :  loader = pointer to a function that can parse and load
972  *                the appropriate config file.
973  *          2  :  config = The configuration_spec to add the loader to.
974  *
975  * Returns     :  N/A
976  *
977  *********************************************************************/
978 void add_loader(int (*loader)(struct client_state *), 
979                 struct configuration_spec * config)
980 {
981    int i;
982
983    for (i=0; i < NLOADERS; i++)
984    {
985       if (config->loaders[i] == NULL)
986       {
987          config->loaders[i] = loader;
988          break;
989       }
990    }
991
992 }
993
994
995 /*********************************************************************
996  *
997  * Function    :  run_loader
998  *
999  * Description :  Called from `load_config' and `listen_loop'.  This
1000  *                function keeps the "csp" current with any file mods
1001  *                since the last loop.  If a file is unchanged, the
1002  *                loader functions do NOT reload the file.
1003  *
1004  * Parameters  :
1005  *          1  :  csp = Current client state (buffers, headers, etc...)
1006  *                      Must be non-null.  Reads: "csp->config"
1007  *                      Writes: various data members.
1008  *
1009  * Returns     :  0 => Ok, everything else is an error.
1010  *
1011  *********************************************************************/
1012 int run_loader(struct client_state *csp)
1013 {
1014    int ret = 0;
1015    int i;
1016
1017    for (i=0; i < NLOADERS; i++)
1018    {
1019       if (csp->config->loaders[i] == NULL)
1020       {
1021          break;
1022       }
1023       ret |= (csp->config->loaders[i])(csp);
1024    }
1025    return(ret);
1026
1027 }
1028
1029
1030 /*
1031   Local Variables:
1032   tab-width: 3
1033   end:
1034 */