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