Added support for linking with POSIX threads library
[privoxy.git] / loaders.c
1 const char loaders_rcs[] = "$Id: loaders.c,v 1.18 2001/06/29 21:45:41 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.18  2001/06/29 21:45:41  oes
39  *    Indentation, CRLF->LF, Tab-> Space
40  *
41  *    Revision 1.17  2001/06/29 13:31:51  oes
42  *    Various adaptions
43  *
44  *    Revision 1.16  2001/06/09 10:55:28  jongfoster
45  *    Changing BUFSIZ ==> BUFFER_SIZE
46  *
47  *    Revision 1.15  2001/06/07 23:14:14  jongfoster
48  *    Removing ACL and forward file loaders - these
49  *    files have been merged into the config file.
50  *    Cosmetic: Moving unloader funcs next to their
51  *    respective loader funcs
52  *
53  *    Revision 1.14  2001/06/01 03:27:04  oes
54  *    Fixed line continuation problem
55  *
56  *    Revision 1.13  2001/05/31 21:28:49  jongfoster
57  *    Removed all permissionsfile code - it's now called the actions
58  *    file, and (almost) all the code is in actions.c
59  *
60  *    Revision 1.12  2001/05/31 17:32:31  oes
61  *
62  *     - Enhanced domain part globbing with infix and prefix asterisk
63  *       matching and optional unanchored operation
64  *
65  *    Revision 1.11  2001/05/29 23:25:24  oes
66  *
67  *     - load_config_line() and load_permissions_file() now use chomp()
68  *
69  *    Revision 1.10  2001/05/29 09:50:24  jongfoster
70  *    Unified blocklist/imagelist/permissionslist.
71  *    File format is still under discussion, but the internal changes
72  *    are (mostly) done.
73  *
74  *    Also modified interceptor behaviour:
75  *    - We now intercept all URLs beginning with one of the following
76  *      prefixes (and *only* these prefixes):
77  *        * http://i.j.b/
78  *        * http://ijbswa.sf.net/config/
79  *        * http://ijbswa.sourceforge.net/config/
80  *    - New interceptors "home page" - go to http://i.j.b/ to see it.
81  *    - Internal changes so that intercepted and fast redirect pages
82  *      are not replaced with an image.
83  *    - Interceptors now have the option to send a binary page direct
84  *      to the client. (i.e. ijb-send-banner uses this)
85  *    - Implemented show-url-info interceptor.  (Which is why I needed
86  *      the above interceptors changes - a typical URL is
87  *      "http://i.j.b/show-url-info?url=www.somesite.com/banner.gif".
88  *      The previous mechanism would not have intercepted that, and
89  *      if it had been intercepted then it then it would have replaced
90  *      it with an image.)
91  *
92  *    Revision 1.9  2001/05/26 17:12:07  jongfoster
93  *    Fatal errors loading configuration files now give better error messages.
94  *
95  *    Revision 1.8  2001/05/26 00:55:20  jongfoster
96  *    Removing duplicated code.  load_forwardfile() now uses create_url_spec()
97  *
98  *    Revision 1.7  2001/05/26 00:28:36  jongfoster
99  *    Automatic reloading of config file.
100  *    Removed obsolete SIGHUP support (Unix) and Reload menu option (Win32).
101  *    Most of the global variables have been moved to a new
102  *    struct configuration_spec, accessed through csp->config->globalname
103  *    Most of the globals remaining are used by the Win32 GUI.
104  *
105  *    Revision 1.6  2001/05/23 12:27:33  oes
106  *
107  *    Fixed ugly indentation of my last changes
108  *
109  *    Revision 1.5  2001/05/23 10:39:05  oes
110  *    - Added support for escaping the comment character
111  *      in config files by a backslash
112  *    - Added support for line continuation in config
113  *      files
114  *    - Fixed a buffer overflow bug with long config lines
115  *
116  *    Revision 1.4  2001/05/22 18:56:28  oes
117  *    CRLF -> LF
118  *
119  *    Revision 1.3  2001/05/20 01:21:20  jongfoster
120  *    Version 2.9.4 checkin.
121  *    - Merged popupfile and cookiefile, and added control over PCRS
122  *      filtering, in new "permissionsfile".
123  *    - Implemented LOG_LEVEL_FATAL, so that if there is a configuration
124  *      file error you now get a message box (in the Win32 GUI) rather
125  *      than the program exiting with no explanation.
126  *    - Made killpopup use the PCRS MIME-type checking and HTTP-header
127  *      skipping.
128  *    - Removed tabs from "config"
129  *    - Moved duplicated url parsing code in "loaders.c" to a new funcition.
130  *    - Bumped up version number.
131  *
132  *    Revision 1.2  2001/05/17 23:01:01  oes
133  *     - Cleaned CRLF's from the sources and related files
134  *
135  *    Revision 1.1.1.1  2001/05/15 13:58:59  oes
136  *    Initial import of version 2.9.3 source tree
137  *
138  *
139  *********************************************************************/
140 \f
141
142 #include "config.h"
143
144 #include <stdio.h>
145 #include <stdlib.h>
146 #include <sys/types.h>
147 #include <string.h>
148 #include <malloc.h>
149 #include <errno.h>
150 #include <sys/stat.h>
151 #include <ctype.h>
152
153 #ifndef _WIN32
154 #include <unistd.h>
155 #endif
156
157 #include "project.h"
158 #include "list.h"
159 #include "loaders.h"
160 #include "encode.h"
161 #include "filters.h"
162 #include "parsers.h"
163 #include "jcc.h"
164 #include "ssplit.h"
165 #include "miscutil.h"
166 #include "errlog.h"
167 #include "gateway.h"
168 #include "actions.h"
169
170 const char loaders_h_rcs[] = LOADERS_H_VERSION;
171
172 /* Fix a problem with Solaris.  There should be no effect on other
173  * platforms.
174  * Solaris's isspace() is a macro which uses it's argument directly
175  * as an array index.  Therefore we need to make sure that high-bit
176  * characters generate +ve values, and ideally we also want to make
177  * the argument match the declared parameter type of "int".
178  */
179 #define ijb_isspace(__X) isspace((int)(unsigned char)(__X))
180
181
182 /*
183  * Currently active files.
184  * These are also entered in the main linked list of files.
185  */
186
187 #ifdef TRUST_FILES
188 static struct file_list *current_trustfile      = NULL;
189 #endif /* def TRUST_FILES */
190
191 static struct file_list *current_re_filterfile  = NULL;
192
193
194
195 /*********************************************************************
196  *
197  * Function    :  sweep
198  *
199  * Description :  Basically a mark and sweep garbage collector, it is run
200  *                (by the parent thread) every once in a while to reclaim memory.
201  *
202  * It uses a mark and sweep strategy:
203  *   1) mark all files as inactive
204  *
205  *   2) check with each client:
206  *       if it is active,   mark its files as active
207  *       if it is inactive, free its resources
208  *
209  *   3) free the resources of all of the files that
210  *      are still marked as inactive (and are obsolete).
211  *
212  *   N.B. files that are not obsolete don't have an unloader defined.
213  *
214  * Parameters  :  None
215  *
216  * Returns     :  N/A
217  *
218  *********************************************************************/
219 void sweep(void)
220 {
221    struct file_list *fl, *nfl;
222    struct client_state *csp, *ncsp;
223
224    /* clear all of the file's active flags */
225    for ( fl = files->next; NULL != fl; fl = fl->next )
226    {
227       fl->active = 0;
228    }
229
230    for (csp = clients; csp && (ncsp = csp->next) ; csp = csp->next)
231    {
232       if (ncsp->active)
233       {
234          /* mark this client's files as active */
235
236          /*
237           * Always have a configuration file.
238           * (Also note the slightly non-standard extra
239           * indirection here.)
240           */
241          ncsp->config->config_file_list->active = 1;
242
243          if (ncsp->actions_list)     /* actions files */
244          {
245             ncsp->actions_list->active = 1;
246          }
247
248          if (ncsp->rlist)     /* pcrsjob files */
249          {
250             ncsp->rlist->active = 1;
251          }
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 /*********************************************************************
832  *
833  * Function    :  unload_re_filterfile
834  *
835  * Description :  Unload the re_filter list.
836  *
837  * Parameters  :
838  *          1  :  f = the data structure associated with the filterfile.
839  *
840  * Returns     :  N/A
841  *
842  *********************************************************************/
843 static void unload_re_filterfile(void *f)
844 {
845    struct re_filterfile_spec *b = (struct re_filterfile_spec *)f;
846
847    if (b == NULL) return;
848
849    destroy_list(b->patterns);
850    pcrs_free_joblist(b->joblist);
851    freez(b);
852
853    return;
854 }
855
856 /*********************************************************************
857  *
858  * Function    :  load_re_filterfile
859  *
860  * Description :  Load the re_filterfile. Each non-comment, non-empty
861  *                line is instantly added to the joblist, which is
862  *                a chained list of pcrs_job structs.
863  *
864  * Parameters  :
865  *          1  :  csp = Current client state (buffers, headers, etc...)
866  *
867  * Returns     :  0 => Ok, everything else is an error.
868  *
869  *********************************************************************/
870 int load_re_filterfile(struct client_state *csp)
871 {
872    FILE *fp;
873
874    struct re_filterfile_spec *bl;
875    struct file_list *fs;
876
877    char  buf[BUFFER_SIZE];
878    int error;
879    pcrs_job *dummy;
880
881    if (!check_file_changed(current_re_filterfile, csp->config->re_filterfile, &fs))
882    {
883       /* No need to load */
884       if (csp)
885       {
886          csp->rlist = current_re_filterfile;
887       }
888       return(0);
889    }
890    if (!fs)
891    {
892       goto load_re_filterfile_error;
893    }
894
895    fs->f = bl = (struct re_filterfile_spec  *)zalloc(sizeof(*bl));
896    if (bl == NULL)
897    {
898       goto load_re_filterfile_error;
899    }
900
901    /* Open the file or fail */
902    if ((fp = fopen(csp->config->re_filterfile, "r")) == NULL)
903    {
904       goto load_re_filterfile_error;
905    }
906
907    /* Read line by line */
908    while (read_config_line(buf, sizeof(buf), fp, fs) != NULL)
909    {
910       enlist( bl->patterns, buf );
911
912       /* We have a meaningful line -> make it a job */
913       if ((dummy = pcrs_compile(buf, &error)) == NULL)
914       {
915          log_error(LOG_LEVEL_RE_FILTER, 
916                "Adding re_filter job %s failed with error %d.", buf, error);
917          continue;
918       }
919       else
920       {
921          dummy->next = bl->joblist;
922          bl->joblist = dummy;
923          log_error(LOG_LEVEL_RE_FILTER, "Adding re_filter job %s succeeded.", buf);
924       }
925    }
926
927    fclose(fp);
928
929 #ifndef SPLIT_PROXY_ARGS
930    if (!suppress_blocklists)
931    {
932       fs->proxy_args = strsav(fs->proxy_args, "</pre>");
933    }
934 #endif /* ndef SPLIT_PROXY_ARGS */
935
936    /* the old one is now obsolete */
937    if ( NULL != current_re_filterfile )
938    {
939       current_re_filterfile->unloader = unload_re_filterfile;
940    }
941
942    fs->next    = files->next;
943    files->next = fs;
944    current_re_filterfile = fs;
945
946    if (csp)
947    {
948       csp->rlist = fs;
949    }
950
951    return( 0 );
952
953 load_re_filterfile_error:
954    log_error(LOG_LEVEL_FATAL, "can't load re_filterfile '%s': %E", 
955              csp->config->re_filterfile);
956    return(-1);
957
958 }
959
960
961 /*********************************************************************
962  *
963  * Function    :  add_loader
964  *
965  * Description :  Called from `load_config'.  Called once for each input
966  *                file found in config.
967  *
968  * Parameters  :
969  *          1  :  loader = pointer to a function that can parse and load
970  *                the appropriate config file.
971  *          2  :  config = The configuration_spec to add the loader to.
972  *
973  * Returns     :  N/A
974  *
975  *********************************************************************/
976 void add_loader(int (*loader)(struct client_state *), 
977                 struct configuration_spec * config)
978 {
979    int i;
980
981    for (i=0; i < NLOADERS; i++)
982    {
983       if (config->loaders[i] == NULL)
984       {
985          config->loaders[i] = loader;
986          break;
987       }
988    }
989
990 }
991
992
993 /*********************************************************************
994  *
995  * Function    :  run_loader
996  *
997  * Description :  Called from `load_config' and `listen_loop'.  This
998  *                function keeps the "csp" current with any file mods
999  *                since the last loop.  If a file is unchanged, the
1000  *                loader functions do NOT reload the file.
1001  *
1002  * Parameters  :
1003  *          1  :  csp = Current client state (buffers, headers, etc...)
1004  *                      Must be non-null.  Reads: "csp->config"
1005  *                      Writes: various data members.
1006  *
1007  * Returns     :  0 => Ok, everything else is an error.
1008  *
1009  *********************************************************************/
1010 int run_loader(struct client_state *csp)
1011 {
1012    int ret = 0;
1013    int i;
1014
1015    for (i=0; i < NLOADERS; i++)
1016    {
1017       if (csp->config->loaders[i] == NULL)
1018       {
1019          break;
1020       }
1021       ret |= (csp->config->loaders[i])(csp);
1022    }
1023    return(ret);
1024
1025 }
1026
1027
1028 /*
1029   Local Variables:
1030   tab-width: 3
1031   end:
1032 */