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