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