- load_config_line() and load_permissions_file() now use chomp()
[privoxy.git] / loaders.c
1 const char loaders_rcs[] = "$Id: loaders.c,v 1.10 2001/05/29 09:50:24 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.10  2001/05/29 09:50:24  jongfoster
39  *    Unified blocklist/imagelist/permissionslist.
40  *    File format is still under discussion, but the internal changes
41  *    are (mostly) done.
42  *
43  *    Also modified interceptor behaviour:
44  *    - We now intercept all URLs beginning with one of the following
45  *      prefixes (and *only* these prefixes):
46  *        * http://i.j.b/
47  *        * http://ijbswa.sf.net/config/
48  *        * http://ijbswa.sourceforge.net/config/
49  *    - New interceptors "home page" - go to http://i.j.b/ to see it.
50  *    - Internal changes so that intercepted and fast redirect pages
51  *      are not replaced with an image.
52  *    - Interceptors now have the option to send a binary page direct
53  *      to the client. (i.e. ijb-send-banner uses this)
54  *    - Implemented show-url-info interceptor.  (Which is why I needed
55  *      the above interceptors changes - a typical URL is
56  *      "http://i.j.b/show-url-info?url=www.somesite.com/banner.gif".
57  *      The previous mechanism would not have intercepted that, and
58  *      if it had been intercepted then it then it would have replaced
59  *      it with an image.)
60  *
61  *    Revision 1.9  2001/05/26 17:12:07  jongfoster
62  *    Fatal errors loading configuration files now give better error messages.
63  *
64  *    Revision 1.8  2001/05/26 00:55:20  jongfoster
65  *    Removing duplicated code.  load_forwardfile() now uses create_url_spec()
66  *
67  *    Revision 1.7  2001/05/26 00:28:36  jongfoster
68  *    Automatic reloading of config file.
69  *    Removed obsolete SIGHUP support (Unix) and Reload menu option (Win32).
70  *    Most of the global variables have been moved to a new
71  *    struct configuration_spec, accessed through csp->config->globalname
72  *    Most of the globals remaining are used by the Win32 GUI.
73  *
74  *    Revision 1.6  2001/05/23 12:27:33  oes
75  *
76  *    Fixed ugly indentation of my last changes
77  *
78  *    Revision 1.5  2001/05/23 10:39:05  oes
79  *    - Added support for escaping the comment character
80  *      in config files by a backslash
81  *    - Added support for line continuation in config
82  *      files
83  *    - Fixed a buffer overflow bug with long config lines
84  *
85  *    Revision 1.4  2001/05/22 18:56:28  oes
86  *    CRLF -> LF
87  *
88  *    Revision 1.3  2001/05/20 01:21:20  jongfoster
89  *    Version 2.9.4 checkin.
90  *    - Merged popupfile and cookiefile, and added control over PCRS
91  *      filtering, in new "permissionsfile".
92  *    - Implemented LOG_LEVEL_FATAL, so that if there is a configuration
93  *      file error you now get a message box (in the Win32 GUI) rather
94  *      than the program exiting with no explanation.
95  *    - Made killpopup use the PCRS MIME-type checking and HTTP-header
96  *      skipping.
97  *    - Removed tabs from "config"
98  *    - Moved duplicated url parsing code in "loaders.c" to a new funcition.
99  *    - Bumped up version number.
100  *
101  *    Revision 1.2  2001/05/17 23:01:01  oes
102  *     - Cleaned CRLF's from the sources and related files
103  *
104  *    Revision 1.1.1.1  2001/05/15 13:58:59  oes
105  *    Initial import of version 2.9.3 source tree
106  *
107  *
108  *********************************************************************/
109 \f
110
111 #include "config.h"
112
113 #include <stdio.h>
114 #include <stdlib.h>
115 #include <sys/types.h>
116 #include <string.h>
117 #include <malloc.h>
118 #include <errno.h>
119 #include <sys/stat.h>
120 #include <ctype.h>
121
122 #ifndef _WIN32
123 #include <unistd.h>
124 #endif
125
126 #include "project.h"
127 #include "loaders.h"
128 #include "encode.h"
129 #include "filters.h"
130 #include "parsers.h"
131 #include "jcc.h"
132 #include "ssplit.h"
133 #include "miscutil.h"
134 #include "errlog.h"
135 #include "gateway.h"
136
137 #ifndef SPLIT_PROXY_ARGS
138 /* For strsav */
139 #include "showargs.h"
140 #endif /* ndef SPLIT_PROXY_ARGS */
141
142 const char loaders_h_rcs[] = LOADERS_H_VERSION;
143
144 /* Fix a problem with Solaris.  There should be no effect on other
145  * platforms.
146  * Solaris's isspace() is a macro which uses it's argument directly
147  * as an array index.  Therefore we need to make sure that high-bit
148  * characters generate +ve values, and ideally we also want to make
149  * the argument match the declared parameter type of "int".
150  */
151 #define ijb_isspace(__X) isspace((int)(unsigned char)(__X))
152
153
154 /*
155  * Currently active files.
156  * These are also entered in the main linked list of files.
157  */
158 static struct file_list *current_permissions_file  = NULL;
159 static struct file_list *current_forwardfile    = NULL;
160
161 #ifdef ACL_FILES
162 static struct file_list *current_aclfile        = NULL;
163 #endif /* def ACL_FILES */
164
165 #ifdef TRUST_FILES
166 static struct file_list *current_trustfile      = NULL;
167 #endif /* def TRUST_FILES */
168
169 #ifdef PCRS
170 static struct file_list *current_re_filterfile  = NULL;
171 #endif /* def PCRS */
172
173
174 static int create_url_spec(struct url_spec * url, char * buf);
175
176
177 /*********************************************************************
178  *
179  * Function    :  sweep
180  *
181  * Description :  Basically a mark and sweep garbage collector, it is run
182  *                (by the parent thread) every once in a while to reclaim memory.
183  *
184  * It uses a mark and sweep strategy:
185  *   1) mark all files as inactive
186  *
187  *   2) check with each client:
188  *       if it is active,   mark its files as active
189  *       if it is inactive, free its resources
190  *
191  *   3) free the resources of all of the files that
192  *      are still marked as inactive (and are obsolete).
193  *
194  *   N.B. files that are not obsolete don't have an unloader defined.
195  *
196  * Parameters  :  None
197  *
198  * Returns     :  N/A
199  *
200  *********************************************************************/
201 void sweep(void)
202 {
203    struct file_list *fl, *nfl;
204    struct client_state *csp, *ncsp;
205
206    /* clear all of the file's active flags */
207    for ( fl = files->next; NULL != fl; fl = fl->next )
208    {
209       fl->active = 0;
210    }
211
212    for (csp = clients; csp && (ncsp = csp->next) ; csp = csp->next)
213    {
214       if (ncsp->active)
215       {
216          /* mark this client's files as active */
217
218          /*
219           * Always have a configuration file.
220           * (Also note the slightly non-standard extra
221           * indirection here.)
222           */
223          ncsp->config->config_file_list->active = 1;
224
225          if (ncsp->permissions_list)     /* permissions files */
226          {
227             ncsp->permissions_list->active = 1;
228          }
229
230          if (ncsp->flist)     /* forward files */
231          {
232             ncsp->flist->active = 1;
233          }
234
235 #ifdef ACL_FILES
236          if (ncsp->alist)     /* acl files */
237          {
238             ncsp->alist->active = 1;
239          }
240 #endif /* def ACL_FILES */
241
242 #ifdef PCRS
243          if (ncsp->rlist)     /* perl re files */
244          {
245             ncsp->rlist->active = 1;
246          }
247 #endif /* def PCRS */
248
249 #ifdef TRUST_FILES
250          if (ncsp->tlist)     /* trust files */
251          {
252             ncsp->tlist->active = 1;
253          }
254 #endif /* def TRUST_FILES */
255
256       }
257       else
258       {
259          /* this client one is not active, release its resources */
260          csp->next = ncsp->next;
261
262          freez(ncsp->ip_addr_str);
263 #ifdef TRUST_FILES
264          freez(ncsp->referrer);
265 #endif /* def TRUST_FILES */
266          freez(ncsp->x_forwarded);
267          freez(ncsp->iob->buf);
268
269          free_http_request(ncsp->http);
270
271          destroy_list(ncsp->headers);
272          destroy_list(ncsp->cookie_list);
273
274 #ifdef STATISTICS
275          urls_read++;
276          if (ncsp->rejected)
277          {
278             urls_rejected++;
279          }
280 #endif /* def STATISTICS */
281
282          freez(ncsp);
283       }
284    }
285
286    for (fl = files; fl && (nfl = fl->next) ; fl = fl->next)
287    {
288       if ( ( 0 == nfl->active ) && ( NULL != nfl->unloader ) )
289       {
290          fl->next = nfl->next;
291
292          (nfl->unloader)(nfl->f);
293
294 #ifndef SPLIT_PROXY_ARGS
295          freez(nfl->proxy_args);
296 #endif /* ndef SPLIT_PROXY_ARGS */
297
298          freez(nfl->filename);
299
300          freez(nfl);
301       }
302    }
303
304 }
305
306
307 /*********************************************************************
308  *
309  * Function    :  create_url_spec
310  *
311  * Description :  Creates a "url_spec" structure from a string.
312  *                When finished, free with unload_url().
313  *
314  * Parameters  :
315  *          1  :  url = Target url_spec to be filled in.  Must be
316  *                      zeroed out before the call (e.g. using zalloc).
317  *          2  :  buf = Source pattern, null terminated.  NOTE: The
318  *                      contents of this buffer are destroyed by this
319  *                      function.  If this function succeeds, the
320  *                      buffer is copied to url->spec.  If this
321  *                      function fails, the contents of the buffer
322  *                      are lost forever.
323  *
324  * Returns     :  0 => Ok, everything else is an error.
325  *
326  *********************************************************************/
327 static int create_url_spec(struct url_spec * url, char * buf)
328 {
329    char *p;
330    struct url_spec tmp_url[1];
331
332    /* paranoia - should never happen. */
333    if ((url == NULL) || (buf == NULL))
334    {
335       return 1;
336    }
337
338    /* save a copy of the orignal specification */
339    if ((url->spec = strdup(buf)) == NULL)
340    {
341       return 1;
342    }
343
344    if ((p = strchr(buf, '/')))
345    {
346       if (NULL == (url->path = strdup(p)))
347       {
348          freez(url->spec);
349          return 1;
350       }
351       url->pathlen = strlen(url->path);
352       *p = '\0';
353    }
354    else
355    {
356       url->path    = NULL;
357       url->pathlen = 0;
358    }
359 #ifdef REGEX
360    if (url->path)
361    {
362       int errcode;
363       char rebuf[BUFSIZ];
364
365       if (NULL == (url->preg = zalloc(sizeof(*url->preg))))
366       {
367          freez(url->spec);
368          freez(url->path);
369          return 1;
370       }
371
372       sprintf(rebuf, "^(%s)", url->path);
373
374       errcode = regcomp(url->preg, rebuf,
375             (REG_EXTENDED|REG_NOSUB|REG_ICASE));
376       if (errcode)
377       {
378          size_t errlen =
379             regerror(errcode,
380                url->preg, buf, sizeof(buf));
381
382          buf[errlen] = '\0';
383
384          log_error(LOG_LEVEL_ERROR, "error compiling %s: %s",
385                  url->spec, buf);
386
387          freez(url->spec);
388          freez(url->path);
389          freez(url->preg);
390
391          return 1;
392       }
393    }
394 #endif
395    if ((p = strchr(buf, ':')) == NULL)
396    {
397       url->port = 0;
398    }
399    else
400    {
401       *p++ = '\0';
402       url->port = atoi(p);
403    }
404
405    if ((url->domain = strdup(buf)) == NULL)
406    {
407       freez(url->spec);
408       freez(url->path);
409 #ifdef REGEX
410       freez(url->preg);
411 #endif /* def REGEX */
412       return 1;
413    }
414
415    /* split domain into components */
416
417    *tmp_url = dsplit(url->domain);
418    url->dbuf = tmp_url->dbuf;
419    url->dcnt = tmp_url->dcnt;
420    url->dvec = tmp_url->dvec;
421
422    return 0; /* OK */
423 }
424
425
426 /*********************************************************************
427  *
428  * Function    :  unload_url
429  *
430  * Description :  Called from the "unloaders".  Freez the url
431  *                structure elements.
432  *
433  * Parameters  :
434  *          1  :  url = pointer to a url_spec structure.
435  *
436  * Returns     :  N/A
437  *
438  *********************************************************************/
439 static void unload_url(struct url_spec *url)
440 {
441    if (url == NULL) return;
442
443    freez(url->spec);
444    freez(url->domain);
445    freez(url->dbuf);
446    freez(url->dvec);
447    freez(url->path);
448 #ifdef REGEX
449    if (url->preg)
450    {
451       regfree(url->preg);
452       freez(url->preg);
453    }
454 #endif
455
456 }
457
458
459 #ifdef ACL_FILES
460 /*********************************************************************
461  *
462  * Function    :  unload_aclfile
463  *
464  * Description :  Unloads an aclfile.
465  *
466  * Parameters  :
467  *          1  :  f = the data structure associated with the aclfile.
468  *
469  * Returns     :  N/A
470  *
471  *********************************************************************/
472 static void unload_aclfile(void *f)
473 {
474    struct access_control_list *b = (struct access_control_list *)f;
475    if (b == NULL) return;
476
477    unload_aclfile(b->next);
478
479    freez(b);
480
481 }
482 #endif /* def ACL_FILES */
483
484
485 /*********************************************************************
486  *
487  * Function    :  unload_permissions_file
488  *
489  * Description :  Unloads a permissions file.
490  *
491  * Parameters  :
492  *          1  :  file_data = the data structure associated with the
493  *                            permissions file.
494  *
495  * Returns     :  N/A
496  *
497  *********************************************************************/
498 static void unload_permissions_file(void *file_data)
499 {
500    struct permissions_spec * next;
501    struct permissions_spec * cur = (struct permissions_spec *)file_data;
502    while (cur != NULL)
503    {
504       next = cur->next;
505       unload_url(cur->url);
506       freez(cur);
507       cur = next;
508    }
509
510 }
511
512
513 #ifdef TRUST_FILES
514 /*********************************************************************
515  *
516  * Function    :  unload_trustfile
517  *
518  * Description :  Unloads a trustfile.
519  *
520  * Parameters  :
521  *          1  :  f = the data structure associated with the trustfile.
522  *
523  * Returns     :  N/A
524  *
525  *********************************************************************/
526 static void unload_trustfile(void *f)
527 {
528    struct block_spec *b = (struct block_spec *)f;
529    if (b == NULL) return;
530
531    unload_trustfile(b->next);
532
533    unload_url(b->url);
534
535    freez(b);
536
537 }
538 #endif /* def TRUST_FILES */
539
540
541 /*********************************************************************
542  *
543  * Function    :  unload_forwardfile
544  *
545  * Description :  Unloads a forwardfile.
546  *
547  * Parameters  :
548  *          1  :  f = the data structure associated with the forwardfile.
549  *
550  * Returns     :  N/A
551  *
552  *********************************************************************/
553 static void unload_forwardfile(void *f)
554 {
555    struct forward_spec *b = (struct forward_spec *)f;
556    if (b == NULL) return;
557
558    unload_forwardfile(b->next);
559
560    unload_url(b->url);
561
562    freez(b->gw->gateway_host);
563    freez(b->gw->forward_host);
564
565    freez(b);
566
567 }
568
569
570 #ifdef PCRS
571 /*********************************************************************
572  *
573  * Function    :  unload_re_filterfile
574  *
575  * Description :  Unload the re_filter list.
576  *
577  * Parameters  :
578  *          1  :  f = the data structure associated with the filterfile.
579  *
580  * Returns     :  N/A
581  *
582  *********************************************************************/
583 static void unload_re_filterfile(void *f)
584 {
585    pcrs_job *joblist;
586    struct re_filterfile_spec *b = (struct re_filterfile_spec *)f;
587
588    if (b == NULL) return;
589
590    destroy_list(b->patterns);
591
592    joblist = b->joblist;
593    while ( NULL != (joblist = pcrs_free_job(joblist)) ) {}
594
595    freez(b);
596
597 }
598 #endif /* def PCRS */
599
600
601 /*********************************************************************
602  *
603  * Function    :  check_file_changed
604  *
605  * Description :  Helper function to check if a file needs reloading.
606  *                If "current" is still current, return it.  Otherwise
607  *                allocates a new (zeroed) "struct file_list", fills 
608  *                in the disk file name and timestamp, and returns it.
609  *
610  * Parameters  :
611  *          1  :  current = The file_list currently being used - will
612  *                          be checked to see if it is out of date. 
613  *                          May be NULL (which is treated as out of
614  *                          date).
615  *          2  :  filename = Name of file to check.
616  *          3  :  newfl    = New file list. [Output only]
617  *                           This will be set to NULL, OR a struct
618  *                           file_list newly allocated on the
619  *                           heap, with the filename and lastmodified
620  *                           fields filled, standard header giving file
621  *                           name in proxy_args, and all others zeroed.
622  *                           (proxy_args is only filled in if !defined
623  *                           SPLIT_PROXY_ARGS and !suppress_blocklists).
624  *
625  * Returns     :  If file unchanged: 0 (and sets newfl == NULL)
626  *                If file changed: 1 and sets newfl != NULL
627  *                On error: 1 and sets newfl == NULL
628  *
629  *********************************************************************/
630 int check_file_changed(const struct file_list * current,
631                        const char * filename,
632                        struct file_list ** newfl)
633 {
634    struct file_list *fs;
635    struct stat statbuf[1];
636
637    *newfl = NULL;
638
639    if (stat(filename, statbuf) < 0)
640    {
641       /* Error, probably file not found. */
642       return 1;
643    }
644
645    if (current
646        && (current->lastmodified == statbuf->st_mtime)
647        && (0 == strcmp(current->filename, filename)))
648    {
649       return 0;
650    }
651
652    fs = (struct file_list *)zalloc(sizeof(struct file_list));
653
654    if (fs == NULL)
655    {
656       /* Out of memory error */
657       return 1;
658    }
659
660    fs->filename = strdup(filename);
661    fs->lastmodified = statbuf->st_mtime;
662
663    if (fs->filename == NULL)
664    {
665       /* Out of memory error */
666       freez (fs);
667       return 1;
668    }
669
670 #ifndef SPLIT_PROXY_ARGS
671    if (!suppress_blocklists)
672    {
673       char * p = html_encode(filename);
674       if (p)
675       {
676          fs->proxy_args = strsav(fs->proxy_args, "<h2>The file `");
677          fs->proxy_args = strsav(fs->proxy_args, p);
678          fs->proxy_args = strsav(fs->proxy_args, 
679             "' contains the following patterns</h2>\n");
680          freez(p);
681       }
682       fs->proxy_args = strsav(fs->proxy_args, "<pre>");
683    }
684 #endif /* ndef SPLIT_PROXY_ARGS */
685
686    *newfl = fs;
687    return 1;
688 }
689
690
691 /*********************************************************************
692  *
693  * Function    :  read_config_line
694  *
695  * Description :  Read a single non-empty line from a file and return
696  *                it.  Trims comments, leading and trailing whitespace
697  *                and respects escaping of newline and comment char.
698  *                Also writes the file to fs->proxy_args.
699  *
700  * Parameters  :
701  *          1  :  buf = Buffer to use.
702  *          2  :  buflen = Size of buffer in bytes.
703  *          3  :  fp = File to read from
704  *          4  :  fs = File will be written to fs->proxy_args.  May
705  *                be NULL to disable this feature.
706  *
707  * Returns     :  NULL on EOF or error
708  *                Otherwise, returns buf.
709  *
710  *********************************************************************/
711 char *read_config_line(char *buf, int buflen, FILE *fp, struct file_list *fs)
712 {
713    char *p, *q;
714    char linebuf[BUFSIZ];
715    int contflag = 0;
716
717    *buf = '\0';
718
719    while (fgets(linebuf, sizeof(linebuf), fp))
720    {
721 #ifndef SPLIT_PROXY_ARGS
722       if (fs && !suppress_blocklists)
723       {
724          char *html_line = html_encode(linebuf);
725          if (html_line != NULL)
726          {
727             fs->proxy_args = strsav(fs->proxy_args, html_line);
728             freez(html_line);
729          }
730          fs->proxy_args = strsav(fs->proxy_args, "<br>");
731       }
732 #endif /* ndef SPLIT_PROXY_ARGS */
733
734       /* Trim off newline */
735       if ((p = strpbrk(linebuf, "\r\n")) != NULL)
736       {
737          *p = '\0';
738       }
739
740       /* Line continuation? Trim escape and set flag. */
741       if ((p != linebuf) && (*--p == '\\'))
742       {
743          contflag = 1;
744          *p = '\0';
745       }
746
747       /* If there's a comment char.. */
748       if ((p = strpbrk(linebuf, "#")) != NULL)
749       {
750          /* ..and it's escaped, left-shift the line over the escape. */
751          if ((p != linebuf) && (*(p-1) == '\\'))
752          {
753             q = p-1;
754             while ((*q++ = *p++) != '\0') /* nop */;
755          }
756          /* Else, chop off the rest of the line */
757          else
758          {
759             *p = '\0';
760          }
761       }
762       
763       /* Remove leading and trailing whitespace */
764       chomp(linebuf);
765
766       if (*linebuf)
767       {
768          strncat(buf, linebuf, buflen - strlen(buf));
769          if (contflag)
770          {
771             contflag = 0;
772             continue;
773          }
774          else
775          {
776             return buf;
777          }
778       }
779    }
780    /* EOF */
781    return NULL;
782
783 }
784
785
786 #ifdef ACL_FILES
787 /*********************************************************************
788  *
789  * Function    :  load_aclfile
790  *
791  * Description :  Read and parse an aclfile and add to files list.
792  *
793  * Parameters  :
794  *          1  :  csp = Current client state (buffers, headers, etc...)
795  *
796  * Returns     :  0 => Ok, everything else is an error.
797  *
798  *********************************************************************/
799 int load_aclfile(struct client_state *csp)
800 {
801    FILE *fp;
802    char buf[BUFSIZ], *v[3], *p;
803    int i;
804    struct access_control_list *a, *bl;
805    struct file_list *fs;
806
807    if (!check_file_changed(current_aclfile, csp->config->aclfile, &fs))
808    {
809       /* No need to load */
810       if (csp)
811       {
812          csp->alist = current_aclfile;
813       }
814       return(0);
815    }
816    if (!fs)
817    {
818       goto load_aclfile_error;
819    }
820
821    fs->f = bl = (struct access_control_list *)zalloc(sizeof(*bl));
822    if (bl == NULL)
823    {
824       freez(fs->filename);
825       freez(fs);
826       goto load_aclfile_error;
827    }
828
829    fp = fopen(csp->config->aclfile, "r");
830
831    if (fp == NULL)
832    {
833       goto load_aclfile_error;
834    }
835
836    while (read_config_line(buf, sizeof(buf), fp, fs) != NULL)
837    {
838       i = ssplit(buf, " \t", v, SZ(v), 1, 1);
839
840       /* allocate a new node */
841       a = (struct access_control_list *) zalloc(sizeof(*a));
842
843       if (a == NULL)
844       {
845          fclose(fp);
846          freez(fs->f);
847          freez(fs->filename);
848          freez(fs);
849          goto load_aclfile_error;
850       }
851
852       /* add it to the list */
853       a->next  = bl->next;
854       bl->next = a;
855
856       switch (i)
857       {
858          case 3:
859             if (acl_addr(v[2], a->dst) < 0)
860             {
861                goto load_aclfile_error;
862             }
863             /* no break */
864
865          case 2:
866             if (acl_addr(v[1], a->src) < 0)
867             {
868                goto load_aclfile_error;
869             }
870
871             p = v[0];
872             if (strcmpic(p, "permit") == 0)
873             {
874                a->action = ACL_PERMIT;
875                break;
876             }
877
878             if (strcmpic(p, "deny") == 0)
879             {
880                a->action = ACL_DENY;
881                break;
882             }
883             /* no break */
884
885          default:
886             goto load_aclfile_error;
887       }
888    }
889
890    fclose(fp);
891
892 #ifndef SPLIT_PROXY_ARGS
893    if (!suppress_blocklists)
894    {
895       fs->proxy_args = strsav(fs->proxy_args, "</pre>");
896    }
897 #endif /* ndef SPLIT_PROXY_ARGS */
898
899    if (current_aclfile)
900    {
901       current_aclfile->unloader = unload_aclfile;
902    }
903
904    fs->next = files->next;
905    files->next = fs;
906    current_aclfile = fs;
907
908    if (csp)
909    {
910       csp->alist = fs;
911    }
912
913    return(0);
914
915 load_aclfile_error:
916    log_error(LOG_LEVEL_FATAL, "can't load access control list %s: %E",
917              csp->config->aclfile);
918    return(-1);
919
920 }
921 #endif /* def ACL_FILES */
922
923
924 struct permission_alias
925 {
926    const char * name;
927    unsigned mask;   /* a bit set to "0" = remove permission */
928    unsigned add;    /* a bit set to "1" = add permission */
929    struct permission_alias * next;
930 };
931
932
933 /*
934  * Note: this is special-cased in the code so we don't need to
935  * fill in the ->next fields.
936  */
937 static const struct permission_alias standard_aliases[] =
938 {
939    { "+block",          PERMIT_MASK_ALL, PERMIT_BLOCK          },
940 /* { "+cookies",        PERMIT_MASK_ALL, PERMIT_COOKIE_SET | PERMIT_COOKIE_READ }, */
941    { "+cookies-read",   PERMIT_MASK_ALL, PERMIT_COOKIE_READ    },
942    { "+cookies-set",    PERMIT_MASK_ALL, PERMIT_COOKIE_SET     },
943    { "+fast-redirects", PERMIT_MASK_ALL, PERMIT_FAST_REDIRECTS },
944    { "+filter",         PERMIT_MASK_ALL, PERMIT_RE_FILTER      },
945    { "+image",          PERMIT_MASK_ALL, PERMIT_IMAGE          },
946    { "+popup",          PERMIT_MASK_ALL, PERMIT_POPUPS         },
947    { "+popups",         PERMIT_MASK_ALL, PERMIT_POPUPS         },
948    { "+referer",        PERMIT_MASK_ALL, PERMIT_REFERER        },
949    { "+referrer",       PERMIT_MASK_ALL, PERMIT_REFERER        },
950    { "-block",          ~PERMIT_BLOCK,          0 },
951 /* { "-cookies",        ~(PERMIT_COOKIE_SET | PERMIT_COOKIE_READ), 0 }, */
952    { "-cookies-read",   ~PERMIT_COOKIE_READ,    0 },
953    { "-cookies-set",    ~PERMIT_COOKIE_SET,     0 },
954    { "-fast-redirects", ~PERMIT_FAST_REDIRECTS, 0 },
955    { "-filter",         ~PERMIT_RE_FILTER,      0 },
956    { "-image",          ~PERMIT_IMAGE,          0 },
957    { "-popup",          ~PERMIT_POPUPS,         0 },
958    { "-popups",         ~PERMIT_POPUPS,         0 },
959    { "-referer",        ~PERMIT_REFERER,        0 },
960    { "-referrer",       ~PERMIT_REFERER,        0 },
961    { NULL,              0,                      0 } /* End marker */
962 };
963
964
965 /*********************************************************************
966  *
967  * Function    :  load_permissions_file
968  *
969  * Description :  Read and parse a permissions file and add to files
970  *                list.
971  *
972  * Parameters  :
973  *          1  :  csp = Current client state (buffers, headers, etc...)
974  *
975  * Returns     :  0 => Ok, everything else is an error.
976  *
977  *********************************************************************/
978 int load_permissions_file(struct client_state *csp)
979 {
980    FILE *fp;
981
982    struct permissions_spec *last_perm;
983    struct permissions_spec *perm;
984    char  buf[BUFSIZ];
985    struct file_list *fs;
986 #define MODE_START_OF_FILE 1
987 #define MODE_PERMISSIONS   2
988 #define MODE_ALIAS         3
989    int mode = MODE_START_OF_FILE;
990    unsigned curmask = PERMIT_MASK_ALL;
991    unsigned curadd  = 0;
992    struct permission_alias * alias_list = NULL;
993
994    if (!check_file_changed(current_permissions_file, csp->config->permissions_file, &fs))
995    {
996       /* No need to load */
997       if (csp)
998       {
999          csp->permissions_list = current_permissions_file;
1000       }
1001       return 0;
1002    }
1003    if (!fs)
1004    {
1005       log_error(LOG_LEVEL_FATAL, "can't load permissions file '%s': error finding file: %E",
1006                 csp->config->permissions_file);
1007       return 1; /* never get here */
1008    }
1009
1010    fs->f = last_perm = (struct permissions_spec *)zalloc(sizeof(*last_perm));
1011    if (last_perm == NULL)
1012    {
1013       log_error(LOG_LEVEL_FATAL, "can't load permissions file '%s': out of memory!",
1014                 csp->config->permissions_file);
1015       return 1; /* never get here */
1016    }
1017
1018    if ((fp = fopen(csp->config->permissions_file, "r")) == NULL)
1019    {
1020       log_error(LOG_LEVEL_FATAL, "can't load permissions file '%s': error opening file: %E",
1021                 csp->config->permissions_file);
1022       return 1; /* never get here */
1023    }
1024
1025    while (read_config_line(buf, sizeof(buf), fp, fs) != NULL)
1026    {
1027       if (*buf == '{')
1028       {
1029          /* It's a header block */
1030          if (buf[1] == '{')
1031          {
1032             /* It's {{settings}} or {{alias}} */
1033             int len = strlen(buf);
1034             char * start = buf + 2;
1035             char * end = buf + len - 1;
1036             if ((len < 5) || (*end-- != '}') || (*end-- != '}'))
1037             {
1038                /* too short */
1039                fclose(fp);
1040                log_error(LOG_LEVEL_FATAL, 
1041                   "can't load permissions file '%s': invalid line: %s",
1042                   csp->config->permissions_file, buf);
1043                return 1; /* never get here */
1044             }
1045
1046             /* Trim leading and trailing whitespace. */
1047             end[1] = '\0';
1048             chomp(start);
1049
1050             if (*start == '\0')
1051             {
1052                /* too short */
1053                fclose(fp);
1054                log_error(LOG_LEVEL_FATAL, 
1055                   "can't load permissions file '%s': invalid line: {{ }}",
1056                   csp->config->permissions_file);
1057                return 1; /* never get here */
1058             }
1059
1060             if (0 == strcmpic(start, "alias"))
1061             {
1062                /* it's an {{alias}} block */
1063
1064                mode = MODE_ALIAS;
1065             }
1066             else
1067             {
1068                /* invalid {{something}} block */
1069                fclose(fp);
1070                log_error(LOG_LEVEL_FATAL, 
1071                   "can't load permissions file '%s': invalid line: {{%s}}",
1072                   csp->config->permissions_file, start);
1073                return 1; /* never get here */
1074             }
1075          }
1076          else
1077          {
1078             /* It's a permissions block */
1079
1080             int more = 1;
1081
1082             int len = strlen(buf);
1083             char * start = buf + 1;
1084             char * end = buf + len - 1;
1085
1086             if ((len < 3) || (*end-- != '}'))
1087             {
1088                /* too short */
1089                fclose(fp);
1090                log_error(LOG_LEVEL_FATAL, 
1091                   "can't load permissions file '%s': invalid line: %s",
1092                   csp->config->permissions_file, buf);
1093                return 1; /* never get here */
1094             }
1095
1096             end[1] = '\0';
1097             chomp(start);
1098
1099             if (*start == '\0')
1100             {
1101                /* too short */
1102                fclose(fp);
1103                log_error(LOG_LEVEL_FATAL, 
1104                   "can't load permissions file '%s': invalid line: { }",
1105                   csp->config->permissions_file);
1106                return 1; /* never get here */
1107             }
1108
1109             mode    = MODE_PERMISSIONS;
1110             
1111             curmask = PERMIT_MASK_ALL;
1112             curadd  = 0;
1113
1114             while (more)
1115             {
1116                const struct permission_alias * alias = standard_aliases;
1117                char * option = start;
1118                while ((*start != '\0') && (*start != ' ') && (*start != '\t'))
1119                {
1120                   start++;
1121                }
1122
1123                more = (*start != 0);
1124                if (more)
1125                {
1126                   *start++ = '\0';
1127
1128                   /* Eat all the whitespace between the options */
1129                   while ((*start == ' ') || (*start == '\t'))
1130                   {
1131                      start++;
1132                   }
1133                }
1134
1135                /* handle option in 'option' */
1136
1137                /* Check for standard permission name */
1138                while ( (alias->name != NULL) && (0 != strcmpic(alias->name, option)) )
1139                {
1140                   alias++;
1141                }
1142                if (alias->name == NULL)
1143                {
1144                   /* try user aliases. */
1145                   alias = alias_list;
1146                   while ( (alias != NULL) && (0 != strcmpic(alias->name, option)) )
1147                   {
1148                      alias = alias->next;
1149                   }
1150                }
1151                if (alias == NULL)
1152                {
1153                   /* Bad permission name */
1154                   fclose(fp);
1155                   log_error(LOG_LEVEL_FATAL, 
1156                      "can't load permissions file '%s': invalid permission name: %s",
1157                      csp->config->permissions_file, option);
1158                   return 1; /* never get here */
1159                }
1160                curmask &= alias->mask;
1161                curadd  &= alias->mask;
1162                curadd  |= alias->add;
1163             }
1164          }
1165       }
1166       else if (mode == MODE_ALIAS)
1167       {
1168          /* define an alias */
1169          struct permission_alias * new_alias;
1170          int more = 1;
1171
1172          char * start = strchr(buf, '=');
1173          char * end = start;
1174
1175          if ((start == NULL) || (start == buf))
1176          {
1177             log_error(LOG_LEVEL_FATAL, 
1178                "can't load permissions file '%s': invalid alias line: %s",
1179                csp->config->permissions_file, buf);
1180             return 1; /* never get here */
1181          }
1182
1183          if ((new_alias = zalloc(sizeof(*new_alias))) == NULL)
1184          {
1185             fclose(fp);
1186             log_error(LOG_LEVEL_FATAL,
1187                "can't load permissions file '%s': out of memory!",
1188                csp->config->permissions_file);
1189             return 1; /* never get here */
1190          }
1191
1192          /* Eat any the whitespace after the '=' */
1193          start++;
1194          while ((*start == ' ') || (*start == '\t'))
1195          {
1196             start++;
1197          }
1198          if (*start == '\0')
1199          {
1200             log_error(LOG_LEVEL_FATAL, 
1201                "can't load permissions file '%s': invalid alias line: %s",
1202                csp->config->permissions_file, buf);
1203             return 1; /* never get here */
1204          }
1205
1206          /* Eat any the whitespace before the '=' */
1207          end--;
1208          while ((*end == ' ') || (*end == '\t'))
1209          {
1210             /*
1211              * we already know we must have at least 1 non-ws char
1212              * at start of buf - no need to check
1213              */
1214             end--;
1215          }
1216          end[1] = '\0';
1217
1218          new_alias->name = strdup(buf);
1219
1220          curmask = PERMIT_MASK_ALL;
1221          curadd  = 0;
1222
1223          while (more)
1224          {
1225             const struct permission_alias * alias = standard_aliases;
1226             char * option = start;
1227             while ((*start != '\0') && (*start != ' ') && (*start != '\t'))
1228             {
1229                start++;
1230             }
1231
1232             more = (*start != 0);
1233             if (more)
1234             {
1235                *start++ = '\0';
1236
1237                /* Eat all the whitespace between the options */
1238                while ((*start == ' ') || (*start == '\t'))
1239                {
1240                   start++;
1241                }
1242             }
1243
1244             /* handle option in 'option' */
1245
1246             /* Check for standard permission name */
1247             while ( (alias->name != NULL) && (0 != strcmpic(alias->name, option)) )
1248             {
1249                alias++;
1250             }
1251             if (alias->name == NULL)
1252             {
1253                /* try user aliases. */
1254                alias = alias_list;
1255                while ( (alias != NULL) && (0 != strcmpic(alias->name, option)) )
1256                {
1257                   alias = alias->next;
1258                }
1259             }
1260             if (alias == NULL)
1261             {
1262                /* Bad permission name */
1263                fclose(fp);
1264                log_error(LOG_LEVEL_FATAL, 
1265                   "can't load permissions file '%s': invalid permission name: %s",
1266                   csp->config->permissions_file, option);
1267                return 1; /* never get here */
1268             }
1269             curmask &= alias->mask;
1270             curadd  &= alias->mask;
1271             curadd  |= alias->add;
1272          }
1273
1274          /* save alias permissions */
1275          new_alias->mask = curmask;
1276          new_alias->add  = curadd;
1277          
1278          /* add to list */
1279          new_alias->next = alias_list;
1280          alias_list = new_alias;
1281       }
1282       else if (mode == MODE_PERMISSIONS)
1283       {
1284          /* it's a URL pattern */
1285
1286          /* allocate a new node */
1287          if ((perm = zalloc(sizeof(*perm))) == NULL)
1288          {
1289             fclose(fp);
1290             log_error(LOG_LEVEL_FATAL,
1291                "can't load permissions file '%s': out of memory!",
1292                csp->config->permissions_file);
1293             return 1; /* never get here */
1294          }
1295
1296          /* Save flags */
1297          perm->mask = curmask;
1298          perm->add = curadd;
1299
1300          /* Save the URL pattern */
1301          if (create_url_spec(perm->url, buf))
1302          {
1303             fclose(fp);
1304             log_error(LOG_LEVEL_FATAL, 
1305                "can't load permissions file '%s': cannot create URL permission from: %s",
1306                csp->config->permissions_file, buf);
1307             return 1; /* never get here */
1308          }
1309
1310          /* add it to the list */
1311          last_perm->next = perm;
1312          last_perm = perm;
1313       }
1314       else if (mode == MODE_START_OF_FILE)
1315       {
1316          /* oops - please have a {} line as 1st line in file. */
1317          fclose(fp);
1318          log_error(LOG_LEVEL_FATAL, 
1319             "can't load permissions file '%s': first line is invalid: %s",
1320             csp->config->permissions_file, buf);
1321          return 1; /* never get here */
1322       }
1323       else
1324       {
1325          /* How did we get here? This is impossible! */
1326          fclose(fp);
1327          log_error(LOG_LEVEL_FATAL, 
1328             "can't load permissions file '%s': INTERNAL ERROR - mode = %d",
1329             csp->config->permissions_file, mode);
1330          return 1; /* never get here */
1331       }
1332    }
1333
1334    fclose(fp);
1335    
1336    while (alias_list != NULL)
1337    {
1338       struct permission_alias * next = alias_list->next;
1339       freez((char *)alias_list->name);
1340       free(alias_list);
1341       alias_list = next;
1342    }
1343
1344 #ifndef SPLIT_PROXY_ARGS
1345    if (!suppress_blocklists)
1346    {
1347       fs->proxy_args = strsav(fs->proxy_args, "</pre>");
1348    }
1349 #endif /* ndef SPLIT_PROXY_ARGS */
1350
1351    /* the old one is now obsolete */
1352    if (current_permissions_file)
1353    {
1354       current_permissions_file->unloader = unload_permissions_file;
1355    }
1356
1357    fs->next    = files->next;
1358    files->next = fs;
1359    current_permissions_file = fs;
1360
1361    if (csp)
1362    {
1363       csp->permissions_list = fs;
1364    }
1365
1366    return(0);
1367
1368 }
1369
1370
1371 #ifdef TRUST_FILES
1372 /*********************************************************************
1373  *
1374  * Function    :  load_trustfile
1375  *
1376  * Description :  Read and parse a trustfile and add to files list.
1377  *
1378  * Parameters  :
1379  *          1  :  csp = Current client state (buffers, headers, etc...)
1380  *
1381  * Returns     :  0 => Ok, everything else is an error.
1382  *
1383  *********************************************************************/
1384 int load_trustfile(struct client_state *csp)
1385 {
1386    FILE *fp;
1387
1388    struct block_spec *b, *bl;
1389    struct url_spec **tl;
1390
1391    char  buf[BUFSIZ], *p, *q;
1392    int reject, trusted;
1393    struct file_list *fs;
1394
1395    if (!check_file_changed(current_trustfile, csp->config->trustfile, &fs))
1396    {
1397       /* No need to load */
1398       if (csp)
1399       {
1400          csp->tlist = current_trustfile;
1401       }
1402       return(0);
1403    }
1404    if (!fs)
1405    {
1406       goto load_trustfile_error;
1407    }
1408
1409    fs->f = bl = (struct block_spec *)zalloc(sizeof(*bl));
1410    if (bl == NULL)
1411    {
1412       goto load_trustfile_error;
1413    }
1414
1415    if ((fp = fopen(csp->config->trustfile, "r")) == NULL)
1416    {
1417       goto load_trustfile_error;
1418    }
1419
1420    tl = csp->config->trust_list;
1421
1422    while (read_config_line(buf, sizeof(buf), fp, fs) != NULL)
1423    {
1424       trusted = 0;
1425       reject  = 1;
1426
1427       if (*buf == '+')
1428       {
1429          trusted = 1;
1430          *buf = '~';
1431       }
1432
1433       if (*buf == '~')
1434       {
1435          reject = 0;
1436          p = buf;
1437          q = p+1;
1438          while ((*p++ = *q++))
1439          {
1440             /* nop */
1441          }
1442       }
1443
1444       /* skip blank lines */
1445       if (*buf == '\0')
1446       {
1447          continue;
1448       }
1449
1450       /* allocate a new node */
1451       if ((b = zalloc(sizeof(*b))) == NULL)
1452       {
1453          fclose(fp);
1454          goto load_trustfile_error;
1455       }
1456
1457       /* add it to the list */
1458       b->next  = bl->next;
1459       bl->next = b;
1460
1461       b->reject = reject;
1462
1463       /* Save the URL pattern */
1464       if (create_url_spec(b->url, buf))
1465       {
1466          fclose(fp);
1467          goto load_trustfile_error;
1468       }
1469
1470       /*
1471        * save a pointer to URL's spec in the list of trusted URL's, too
1472        */
1473       if (trusted)
1474       {
1475          *tl++ = b->url;
1476       }
1477    }
1478
1479    *tl = NULL;
1480
1481    fclose(fp);
1482
1483 #ifndef SPLIT_PROXY_ARGS
1484    if (!suppress_blocklists)
1485    {
1486       fs->proxy_args = strsav(fs->proxy_args, "</pre>");
1487    }
1488 #endif /* ndef SPLIT_PROXY_ARGS */
1489
1490    /* the old one is now obsolete */
1491    if (current_trustfile)
1492    {
1493       current_trustfile->unloader = unload_trustfile;
1494    }
1495
1496    fs->next    = files->next;
1497    files->next = fs;
1498    current_trustfile = fs;
1499
1500    if (csp)
1501    {
1502       csp->tlist = fs;
1503    }
1504
1505    return(0);
1506
1507 load_trustfile_error:
1508    log_error(LOG_LEVEL_FATAL, "can't load trustfile '%s': %E",
1509              csp->config->trustfile);
1510    return(-1);
1511
1512 }
1513 #endif /* def TRUST_FILES */
1514
1515
1516 /*********************************************************************
1517  *
1518  * Function    :  load_forwardfile
1519  *
1520  * Description :  Read and parse a forwardfile and add to files list.
1521  *
1522  * Parameters  :
1523  *          1  :  csp = Current client state (buffers, headers, etc...)
1524  *
1525  * Returns     :  0 => Ok, everything else is an error.
1526  *
1527  *********************************************************************/
1528 int load_forwardfile(struct client_state *csp)
1529 {
1530    FILE *fp;
1531
1532    struct forward_spec *b, *bl;
1533    char buf[BUFSIZ];
1534    char *p, *tmp;
1535    char *vec[4];
1536    int n;
1537    struct file_list *fs;
1538    const struct gateway *gw;
1539
1540    if (!check_file_changed(current_forwardfile, csp->config->forwardfile, &fs))
1541    {
1542       /* No need to load */
1543       if (csp)
1544       {
1545          csp->flist = current_forwardfile;
1546       }
1547       return(0);
1548    }
1549    if (!fs)
1550    {
1551       goto load_forwardfile_error;
1552    }
1553
1554    fs->f = bl = (struct forward_spec  *)zalloc(sizeof(*bl));
1555
1556    if ((fs == NULL) || (bl == NULL))
1557    {
1558       goto load_forwardfile_error;
1559    }
1560
1561    if ((fp = fopen(csp->config->forwardfile, "r")) == NULL)
1562    {
1563       goto load_forwardfile_error;
1564    }
1565
1566    tmp = NULL;
1567
1568    while (read_config_line(buf, sizeof(buf), fp, fs) != NULL)
1569    {
1570       freez(tmp);
1571
1572       tmp = strdup(buf);
1573
1574       n = ssplit(tmp, " \t", vec, SZ(vec), 1, 1);
1575
1576       if (n != 4)
1577       {
1578          log_error(LOG_LEVEL_ERROR, "error in forwardfile: %s", buf);
1579          continue;
1580       }
1581
1582       strcpy(buf, vec[0]);
1583
1584       /* skip lines containing only ~ */
1585       if (*buf == '\0')
1586       {
1587          continue;
1588       }
1589
1590       /* allocate a new node */
1591       if (((b = zalloc(sizeof(*b))) == NULL)
1592       )
1593       {
1594          fclose(fp);
1595          goto load_forwardfile_error;
1596       }
1597
1598       /* add it to the list */
1599       b->next  = bl->next;
1600       bl->next = b;
1601
1602       /* Save the URL pattern */
1603       if (create_url_spec(b->url, buf))
1604       {
1605          fclose(fp);
1606          goto load_forwardfile_error;
1607       }
1608
1609       /* now parse the gateway specs */
1610
1611       p = vec[2];
1612
1613       for (gw = gateways; gw->name; gw++)
1614       {
1615          if (strcmp(gw->name, p) == 0)
1616          {
1617             break;
1618          }
1619       }
1620
1621       if (gw->name == NULL)
1622       {
1623          goto load_forwardfile_error;
1624       }
1625
1626       /* save this as the gateway type */
1627       *b->gw = *gw;
1628
1629       /* now parse the gateway host[:port] spec */
1630       p = vec[3];
1631
1632       if (strcmp(p, ".") != 0)
1633       {
1634          b->gw->gateway_host = strdup(p);
1635
1636          if ((p = strchr(b->gw->gateway_host, ':')))
1637          {
1638             *p++ = '\0';
1639             b->gw->gateway_port = atoi(p);
1640          }
1641
1642          if (b->gw->gateway_port <= 0)
1643          {
1644             goto load_forwardfile_error;
1645          }
1646       }
1647
1648       /* now parse the forwarding spec */
1649       p = vec[1];
1650
1651       if (strcmp(p, ".") != 0)
1652       {
1653          b->gw->forward_host = strdup(p);
1654
1655          if ((p = strchr(b->gw->forward_host, ':')))
1656          {
1657             *p++ = '\0';
1658             b->gw->forward_port = atoi(p);
1659          }
1660
1661          if (b->gw->forward_port <= 0)
1662          {
1663             b->gw->forward_port = 8000;
1664          }
1665       }
1666    }
1667
1668    freez(tmp);
1669
1670    fclose(fp);
1671
1672 #ifndef SPLIT_PROXY_ARGS
1673    if (!suppress_blocklists)
1674    {
1675       fs->proxy_args = strsav(fs->proxy_args, "</pre>");
1676    }
1677 #endif /* ndef SPLIT_PROXY_ARGS */
1678
1679    /* the old one is now obsolete */
1680    if (current_forwardfile)
1681    {
1682       current_forwardfile->unloader = unload_forwardfile;
1683    }
1684
1685    fs->next    = files->next;
1686    files->next = fs;
1687    current_forwardfile = fs;
1688
1689    if (csp)
1690    {
1691       csp->flist = fs;
1692    }
1693
1694    return(0);
1695
1696 load_forwardfile_error:
1697    log_error(LOG_LEVEL_FATAL, "can't load forwardfile '%s': %E",
1698              csp->config->forwardfile);
1699    return(-1);
1700
1701 }
1702
1703
1704 #ifdef PCRS
1705 /*********************************************************************
1706  *
1707  * Function    :  load_re_filterfile
1708  *
1709  * Description :  Load the re_filterfile. Each non-comment, non-empty
1710  *                line is instantly added to the joblist, which is
1711  *                a chained list of pcrs_job structs.
1712  *
1713  * Parameters  :
1714  *          1  :  csp = Current client state (buffers, headers, etc...)
1715  *
1716  * Returns     :  0 => Ok, everything else is an error.
1717  *
1718  *********************************************************************/
1719 int load_re_filterfile(struct client_state *csp)
1720 {
1721    FILE *fp;
1722
1723    struct re_filterfile_spec *bl;
1724    struct file_list *fs;
1725
1726    char  buf[BUFSIZ];
1727    int error;
1728    pcrs_job *dummy;
1729
1730    if (!check_file_changed(current_re_filterfile, csp->config->re_filterfile, &fs))
1731    {
1732       /* No need to load */
1733       if (csp)
1734       {
1735          csp->rlist = current_re_filterfile;
1736       }
1737       return(0);
1738    }
1739    if (!fs)
1740    {
1741       goto load_re_filterfile_error;
1742    }
1743
1744    fs->f = bl = (struct re_filterfile_spec  *)zalloc(sizeof(*bl));
1745    if (bl == NULL)
1746    {
1747       goto load_re_filterfile_error;
1748    }
1749
1750    /* Open the file or fail */
1751    if ((fp = fopen(csp->config->re_filterfile, "r")) == NULL)
1752    {
1753       goto load_re_filterfile_error;
1754    }
1755
1756    /* Read line by line */
1757    while (read_config_line(buf, sizeof(buf), fp, fs) != NULL)
1758    {
1759       enlist( bl->patterns, buf );
1760
1761       /* We have a meaningful line -> make it a job */
1762       if ((dummy = pcrs_make_job(buf, &error)) == NULL)
1763       {
1764          log_error(LOG_LEVEL_RE_FILTER, 
1765                "Adding re_filter job %s failed with error %d.", buf, error);
1766          continue;
1767       }
1768       else
1769       {
1770          dummy->next = bl->joblist;
1771          bl->joblist = dummy;
1772          log_error(LOG_LEVEL_RE_FILTER, "Adding re_filter job %s succeeded.", buf);
1773       }
1774    }
1775
1776    fclose(fp);
1777
1778 #ifndef SPLIT_PROXY_ARGS
1779    if (!suppress_blocklists)
1780    {
1781       fs->proxy_args = strsav(fs->proxy_args, "</pre>");
1782    }
1783 #endif /* ndef SPLIT_PROXY_ARGS */
1784
1785    /* the old one is now obsolete */
1786    if ( NULL != current_re_filterfile )
1787    {
1788       current_re_filterfile->unloader = unload_re_filterfile;
1789    }
1790
1791    fs->next    = files->next;
1792    files->next = fs;
1793    current_re_filterfile = fs;
1794
1795    if (csp)
1796    {
1797       csp->rlist = fs;
1798    }
1799
1800    return( 0 );
1801
1802 load_re_filterfile_error:
1803    log_error(LOG_LEVEL_FATAL, "can't load re_filterfile '%s': %E", 
1804              csp->config->re_filterfile);
1805    return(-1);
1806
1807 }
1808 #endif /* def PCRS */
1809
1810
1811 /*********************************************************************
1812  *
1813  * Function    :  add_loader
1814  *
1815  * Description :  Called from `load_config'.  Called once for each input
1816  *                file found in config.
1817  *
1818  * Parameters  :
1819  *          1  :  loader = pointer to a function that can parse and load
1820  *                the appropriate config file.
1821  *          2  :  config = The configuration_spec to add the loader to.
1822  *
1823  * Returns     :  N/A
1824  *
1825  *********************************************************************/
1826 void add_loader(int (*loader)(struct client_state *), 
1827                 struct configuration_spec * config)
1828 {
1829    int i;
1830
1831    for (i=0; i < NLOADERS; i++)
1832    {
1833       if (config->loaders[i] == NULL)
1834       {
1835          config->loaders[i] = loader;
1836          break;
1837       }
1838    }
1839
1840 }
1841
1842
1843 /*********************************************************************
1844  *
1845  * Function    :  run_loader
1846  *
1847  * Description :  Called from `load_config' and `listen_loop'.  This
1848  *                function keeps the "csp" current with any file mods
1849  *                since the last loop.  If a file is unchanged, the
1850  *                loader functions do NOT reload the file.
1851  *
1852  * Parameters  :
1853  *          1  :  csp = Current client state (buffers, headers, etc...)
1854  *                      Must be non-null.  Reads: "csp->config"
1855  *                      Writes: various data members.
1856  *
1857  * Returns     :  0 => Ok, everything else is an error.
1858  *
1859  *********************************************************************/
1860 int run_loader(struct client_state *csp)
1861 {
1862    int ret = 0;
1863    int i;
1864
1865    for (i=0; i < NLOADERS; i++)
1866    {
1867       if (csp->config->loaders[i] == NULL)
1868       {
1869          break;
1870       }
1871       ret |= (csp->config->loaders[i])(csp);
1872    }
1873    return(ret);
1874
1875 }
1876
1877
1878 /*
1879   Local Variables:
1880   tab-width: 3
1881   end:
1882 */