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