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