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