added FIXME
[privoxy.git] / loaders.c
1 const char loaders_rcs[] = "$Id: loaders.c,v 1.13 2001/05/31 21:28:49 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.13  2001/05/31 21:28:49  jongfoster
39  *    Removed all permissionsfile code - it's now called the actions
40  *    file, and (almost) all the code is in actions.c
41  *
42  *    Revision 1.12  2001/05/31 17:32:31  oes
43  *
44  *     - Enhanced domain part globbing with infix and prefix asterisk
45  *       matching and optional unanchored operation
46  *
47  *    Revision 1.11  2001/05/29 23:25:24  oes
48  *
49  *     - load_config_line() and load_permissions_file() now use chomp()
50  *
51  *    Revision 1.10  2001/05/29 09:50:24  jongfoster
52  *    Unified blocklist/imagelist/permissionslist.
53  *    File format is still under discussion, but the internal changes
54  *    are (mostly) done.
55  *
56  *    Also modified interceptor behaviour:
57  *    - We now intercept all URLs beginning with one of the following
58  *      prefixes (and *only* these prefixes):
59  *        * http://i.j.b/
60  *        * http://ijbswa.sf.net/config/
61  *        * http://ijbswa.sourceforge.net/config/
62  *    - New interceptors "home page" - go to http://i.j.b/ to see it.
63  *    - Internal changes so that intercepted and fast redirect pages
64  *      are not replaced with an image.
65  *    - Interceptors now have the option to send a binary page direct
66  *      to the client. (i.e. ijb-send-banner uses this)
67  *    - Implemented show-url-info interceptor.  (Which is why I needed
68  *      the above interceptors changes - a typical URL is
69  *      "http://i.j.b/show-url-info?url=www.somesite.com/banner.gif".
70  *      The previous mechanism would not have intercepted that, and
71  *      if it had been intercepted then it then it would have replaced
72  *      it with an image.)
73  *
74  *    Revision 1.9  2001/05/26 17:12:07  jongfoster
75  *    Fatal errors loading configuration files now give better error messages.
76  *
77  *    Revision 1.8  2001/05/26 00:55:20  jongfoster
78  *    Removing duplicated code.  load_forwardfile() now uses create_url_spec()
79  *
80  *    Revision 1.7  2001/05/26 00:28:36  jongfoster
81  *    Automatic reloading of config file.
82  *    Removed obsolete SIGHUP support (Unix) and Reload menu option (Win32).
83  *    Most of the global variables have been moved to a new
84  *    struct configuration_spec, accessed through csp->config->globalname
85  *    Most of the globals remaining are used by the Win32 GUI.
86  *
87  *    Revision 1.6  2001/05/23 12:27:33  oes
88  *
89  *    Fixed ugly indentation of my last changes
90  *
91  *    Revision 1.5  2001/05/23 10:39:05  oes
92  *    - Added support for escaping the comment character
93  *      in config files by a backslash
94  *    - Added support for line continuation in config
95  *      files
96  *    - Fixed a buffer overflow bug with long config lines
97  *
98  *    Revision 1.4  2001/05/22 18:56:28  oes
99  *    CRLF -> LF
100  *
101  *    Revision 1.3  2001/05/20 01:21:20  jongfoster
102  *    Version 2.9.4 checkin.
103  *    - Merged popupfile and cookiefile, and added control over PCRS
104  *      filtering, in new "permissionsfile".
105  *    - Implemented LOG_LEVEL_FATAL, so that if there is a configuration
106  *      file error you now get a message box (in the Win32 GUI) rather
107  *      than the program exiting with no explanation.
108  *    - Made killpopup use the PCRS MIME-type checking and HTTP-header
109  *      skipping.
110  *    - Removed tabs from "config"
111  *    - Moved duplicated url parsing code in "loaders.c" to a new funcition.
112  *    - Bumped up version number.
113  *
114  *    Revision 1.2  2001/05/17 23:01:01  oes
115  *     - Cleaned CRLF's from the sources and related files
116  *
117  *    Revision 1.1.1.1  2001/05/15 13:58:59  oes
118  *    Initial import of version 2.9.3 source tree
119  *
120  *
121  *********************************************************************/
122 \f
123
124 #include "config.h"
125
126 #include <stdio.h>
127 #include <stdlib.h>
128 #include <sys/types.h>
129 #include <string.h>
130 #include <malloc.h>
131 #include <errno.h>
132 #include <sys/stat.h>
133 #include <ctype.h>
134
135 #ifndef _WIN32
136 #include <unistd.h>
137 #endif
138
139 #include "project.h"
140 #include "list.h"
141 #include "loaders.h"
142 #include "encode.h"
143 #include "filters.h"
144 #include "parsers.h"
145 #include "jcc.h"
146 #include "ssplit.h"
147 #include "miscutil.h"
148 #include "errlog.h"
149 #include "gateway.h"
150 #include "actions.h"
151
152 #ifndef SPLIT_PROXY_ARGS
153 /* For strsav */
154 #include "showargs.h"
155 #endif /* ndef SPLIT_PROXY_ARGS */
156
157 const char loaders_h_rcs[] = LOADERS_H_VERSION;
158
159 /* Fix a problem with Solaris.  There should be no effect on other
160  * platforms.
161  * Solaris's isspace() is a macro which uses it's argument directly
162  * as an array index.  Therefore we need to make sure that high-bit
163  * characters generate +ve values, and ideally we also want to make
164  * the argument match the declared parameter type of "int".
165  */
166 #define ijb_isspace(__X) isspace((int)(unsigned char)(__X))
167
168
169 /*
170  * Currently active files.
171  * These are also entered in the main linked list of files.
172  */
173 static struct file_list *current_forwardfile    = NULL;
174
175 #ifdef ACL_FILES
176 static struct file_list *current_aclfile        = NULL;
177 #endif /* def ACL_FILES */
178
179 #ifdef TRUST_FILES
180 static struct file_list *current_trustfile      = NULL;
181 #endif /* def TRUST_FILES */
182
183 #ifdef PCRS
184 static struct file_list *current_re_filterfile  = NULL;
185 #endif /* def PCRS */
186
187
188 /*********************************************************************
189  *
190  * Function    :  sweep
191  *
192  * Description :  Basically a mark and sweep garbage collector, it is run
193  *                (by the parent thread) every once in a while to reclaim memory.
194  *
195  * It uses a mark and sweep strategy:
196  *   1) mark all files as inactive
197  *
198  *   2) check with each client:
199  *       if it is active,   mark its files as active
200  *       if it is inactive, free its resources
201  *
202  *   3) free the resources of all of the files that
203  *      are still marked as inactive (and are obsolete).
204  *
205  *   N.B. files that are not obsolete don't have an unloader defined.
206  *
207  * Parameters  :  None
208  *
209  * Returns     :  N/A
210  *
211  *********************************************************************/
212 void sweep(void)
213 {
214    struct file_list *fl, *nfl;
215    struct client_state *csp, *ncsp;
216
217    /* clear all of the file's active flags */
218    for ( fl = files->next; NULL != fl; fl = fl->next )
219    {
220       fl->active = 0;
221    }
222
223    for (csp = clients; csp && (ncsp = csp->next) ; csp = csp->next)
224    {
225       if (ncsp->active)
226       {
227          /* mark this client's files as active */
228
229          /*
230           * Always have a configuration file.
231           * (Also note the slightly non-standard extra
232           * indirection here.)
233           */
234          ncsp->config->config_file_list->active = 1;
235
236          if (ncsp->actions_list)     /* actions files */
237          {
238             ncsp->actions_list->active = 1;
239          }
240
241          if (ncsp->flist)     /* forward files */
242          {
243             ncsp->flist->active = 1;
244          }
245
246 #ifdef ACL_FILES
247          if (ncsp->alist)     /* acl files */
248          {
249             ncsp->alist->active = 1;
250          }
251 #endif /* def ACL_FILES */
252
253 #ifdef PCRS
254          if (ncsp->rlist)     /* perl re files */
255          {
256             ncsp->rlist->active = 1;
257          }
258 #endif /* def PCRS */
259
260 #ifdef TRUST_FILES
261          if (ncsp->tlist)     /* trust files */
262          {
263             ncsp->tlist->active = 1;
264          }
265 #endif /* def TRUST_FILES */
266
267       }
268       else
269       {
270          /* this client one is not active, release its resources */
271          csp->next = ncsp->next;
272
273          freez(ncsp->ip_addr_str);
274 #ifdef TRUST_FILES
275          freez(ncsp->referrer);
276 #endif /* def TRUST_FILES */
277          freez(ncsp->x_forwarded);
278          freez(ncsp->iob->buf);
279
280          free_http_request(ncsp->http);
281
282          destroy_list(ncsp->headers);
283          destroy_list(ncsp->cookie_list);
284
285          free_current_action(ncsp->action);
286
287 #ifdef STATISTICS
288          urls_read++;
289          if (ncsp->rejected)
290          {
291             urls_rejected++;
292          }
293 #endif /* def STATISTICS */
294
295          freez(ncsp);
296       }
297    }
298
299    for (fl = files; fl && (nfl = fl->next) ; fl = fl->next)
300    {
301       if ( ( 0 == nfl->active ) && ( NULL != nfl->unloader ) )
302       {
303          fl->next = nfl->next;
304
305          (nfl->unloader)(nfl->f);
306
307 #ifndef SPLIT_PROXY_ARGS
308          freez(nfl->proxy_args);
309 #endif /* ndef SPLIT_PROXY_ARGS */
310
311          freez(nfl->filename);
312
313          freez(nfl);
314       }
315    }
316
317 }
318
319
320 /*********************************************************************
321  *
322  * Function    :  create_url_spec
323  *
324  * Description :  Creates a "url_spec" structure from a string.
325  *                When finished, free with unload_url().
326  *
327  * Parameters  :
328  *          1  :  url = Target url_spec to be filled in.  Must be
329  *                      zeroed out before the call (e.g. using zalloc).
330  *          2  :  buf = Source pattern, null terminated.  NOTE: The
331  *                      contents of this buffer are destroyed by this
332  *                      function.  If this function succeeds, the
333  *                      buffer is copied to url->spec.  If this
334  *                      function fails, the contents of the buffer
335  *                      are lost forever.
336  *
337  * Returns     :  0 => Ok, everything else is an error.
338  *
339  *********************************************************************/
340 int create_url_spec(struct url_spec * url, char * buf)
341 {
342    char *p;
343    struct url_spec tmp_url[1];
344
345    /* paranoia - should never happen. */
346    if ((url == NULL) || (buf == NULL))
347    {
348       return 1;
349    }
350
351    /* save a copy of the orignal specification */
352    if ((url->spec = strdup(buf)) == NULL)
353    {
354       return 1;
355    }
356
357    if ((p = strchr(buf, '/')))
358    {
359       if (NULL == (url->path = strdup(p)))
360       {
361          freez(url->spec);
362          return 1;
363       }
364       url->pathlen = strlen(url->path);
365       *p = '\0';
366    }
367    else
368    {
369       url->path    = NULL;
370       url->pathlen = 0;
371    }
372 #ifdef REGEX
373    if (url->path)
374    {
375       int errcode;
376       char rebuf[BUFSIZ];
377
378       if (NULL == (url->preg = zalloc(sizeof(*url->preg))))
379       {
380          freez(url->spec);
381          freez(url->path);
382          return 1;
383       }
384
385       sprintf(rebuf, "^(%s)", url->path);
386
387       errcode = regcomp(url->preg, rebuf,
388             (REG_EXTENDED|REG_NOSUB|REG_ICASE));
389       if (errcode)
390       {
391          size_t errlen =
392             regerror(errcode,
393                url->preg, buf, sizeof(buf));
394
395          buf[errlen] = '\0';
396
397          log_error(LOG_LEVEL_ERROR, "error compiling %s: %s",
398                  url->spec, buf);
399
400          freez(url->spec);
401          freez(url->path);
402          freez(url->preg);
403
404          return 1;
405       }
406    }
407 #endif
408    if ((p = strchr(buf, ':')) == NULL)
409    {
410       url->port = 0;
411    }
412    else
413    {
414       *p++ = '\0';
415       url->port = atoi(p);
416    }
417
418    if ((url->domain = strdup(buf)) == NULL)
419    {
420       freez(url->spec);
421       freez(url->path);
422 #ifdef REGEX
423       freez(url->preg);
424 #endif /* def REGEX */
425       return 1;
426    }
427
428    /* split domain into components */
429
430    *tmp_url = dsplit(url->domain);
431    url->dbuf = tmp_url->dbuf;
432    url->dcnt = tmp_url->dcnt;
433    url->dvec = tmp_url->dvec;
434    url->unanchored = tmp_url->unanchored;
435
436    return 0; /* OK */
437 }
438
439
440 /*********************************************************************
441  *
442  * Function    :  free_url
443  *
444  * Description :  Called from the "unloaders".  Freez the url
445  *                structure elements.
446  *
447  * Parameters  :
448  *          1  :  url = pointer to a url_spec structure.
449  *
450  * Returns     :  N/A
451  *
452  *********************************************************************/
453 void free_url(struct url_spec *url)
454 {
455    if (url == NULL) return;
456
457    freez(url->spec);
458    freez(url->domain);
459    freez(url->dbuf);
460    freez(url->dvec);
461    freez(url->path);
462 #ifdef REGEX
463    if (url->preg)
464    {
465       regfree(url->preg);
466       freez(url->preg);
467    }
468 #endif
469
470 }
471
472
473 #ifdef ACL_FILES
474 /*********************************************************************
475  *
476  * Function    :  unload_aclfile
477  *
478  * Description :  Unloads an aclfile.
479  *
480  * Parameters  :
481  *          1  :  f = the data structure associated with the aclfile.
482  *
483  * Returns     :  N/A
484  *
485  *********************************************************************/
486 static void unload_aclfile(void *f)
487 {
488    struct access_control_list *b = (struct access_control_list *)f;
489    if (b == NULL) return;
490
491    unload_aclfile(b->next);
492
493    freez(b);
494
495 }
496 #endif /* def ACL_FILES */
497
498
499 #ifdef TRUST_FILES
500 /*********************************************************************
501  *
502  * Function    :  unload_trustfile
503  *
504  * Description :  Unloads a trustfile.
505  *
506  * Parameters  :
507  *          1  :  f = the data structure associated with the trustfile.
508  *
509  * Returns     :  N/A
510  *
511  *********************************************************************/
512 static void unload_trustfile(void *f)
513 {
514    struct block_spec *b = (struct block_spec *)f;
515    if (b == NULL) return;
516
517    unload_trustfile(b->next);
518
519    free_url(b->url);
520
521    freez(b);
522
523 }
524 #endif /* def TRUST_FILES */
525
526
527 /*********************************************************************
528  *
529  * Function    :  unload_forwardfile
530  *
531  * Description :  Unloads a forwardfile.
532  *
533  * Parameters  :
534  *          1  :  f = the data structure associated with the forwardfile.
535  *
536  * Returns     :  N/A
537  *
538  *********************************************************************/
539 static void unload_forwardfile(void *f)
540 {
541    struct forward_spec *b = (struct forward_spec *)f;
542    if (b == NULL) return;
543
544    unload_forwardfile(b->next);
545
546    free_url(b->url);
547
548    freez(b->gw->gateway_host);
549    freez(b->gw->forward_host);
550
551    freez(b);
552
553 }
554
555
556 #ifdef PCRS
557 /*********************************************************************
558  *
559  * Function    :  unload_re_filterfile
560  *
561  * Description :  Unload the re_filter list.
562  *
563  * Parameters  :
564  *          1  :  f = the data structure associated with the filterfile.
565  *
566  * Returns     :  N/A
567  *
568  *********************************************************************/
569 static void unload_re_filterfile(void *f)
570 {
571    pcrs_job *joblist;
572    struct re_filterfile_spec *b = (struct re_filterfile_spec *)f;
573
574    if (b == NULL) return;
575
576    destroy_list(b->patterns);
577
578    joblist = b->joblist;
579    while ( NULL != (joblist = pcrs_free_job(joblist)) ) {}
580
581    freez(b);
582
583 }
584 #endif /* def PCRS */
585
586
587 /*********************************************************************
588  *
589  * Function    :  check_file_changed
590  *
591  * Description :  Helper function to check if a file needs reloading.
592  *                If "current" is still current, return it.  Otherwise
593  *                allocates a new (zeroed) "struct file_list", fills 
594  *                in the disk file name and timestamp, and returns it.
595  *
596  * Parameters  :
597  *          1  :  current = The file_list currently being used - will
598  *                          be checked to see if it is out of date. 
599  *                          May be NULL (which is treated as out of
600  *                          date).
601  *          2  :  filename = Name of file to check.
602  *          3  :  newfl    = New file list. [Output only]
603  *                           This will be set to NULL, OR a struct
604  *                           file_list newly allocated on the
605  *                           heap, with the filename and lastmodified
606  *                           fields filled, standard header giving file
607  *                           name in proxy_args, and all others zeroed.
608  *                           (proxy_args is only filled in if !defined
609  *                           SPLIT_PROXY_ARGS and !suppress_blocklists).
610  *
611  * Returns     :  If file unchanged: 0 (and sets newfl == NULL)
612  *                If file changed: 1 and sets newfl != NULL
613  *                On error: 1 and sets newfl == NULL
614  *
615  *********************************************************************/
616 int check_file_changed(const struct file_list * current,
617                        const char * filename,
618                        struct file_list ** newfl)
619 {
620    struct file_list *fs;
621    struct stat statbuf[1];
622
623    *newfl = NULL;
624
625    if (stat(filename, statbuf) < 0)
626    {
627       /* Error, probably file not found. */
628       return 1;
629    }
630
631    if (current
632        && (current->lastmodified == statbuf->st_mtime)
633        && (0 == strcmp(current->filename, filename)))
634    {
635       return 0;
636    }
637
638    fs = (struct file_list *)zalloc(sizeof(struct file_list));
639
640    if (fs == NULL)
641    {
642       /* Out of memory error */
643       return 1;
644    }
645
646    fs->filename = strdup(filename);
647    fs->lastmodified = statbuf->st_mtime;
648
649    if (fs->filename == NULL)
650    {
651       /* Out of memory error */
652       freez (fs);
653       return 1;
654    }
655
656 #ifndef SPLIT_PROXY_ARGS
657    if (!suppress_blocklists)
658    {
659       char * p = html_encode(filename);
660       if (p)
661       {
662          fs->proxy_args = strsav(fs->proxy_args, "<h2>The file `");
663          fs->proxy_args = strsav(fs->proxy_args, p);
664          fs->proxy_args = strsav(fs->proxy_args, 
665             "' contains the following patterns</h2>\n");
666          freez(p);
667       }
668       fs->proxy_args = strsav(fs->proxy_args, "<pre>");
669    }
670 #endif /* ndef SPLIT_PROXY_ARGS */
671
672    *newfl = fs;
673    return 1;
674 }
675
676
677 /*********************************************************************
678  *
679  * Function    :  read_config_line
680  *
681  * Description :  Read a single non-empty line from a file and return
682  *                it.  Trims comments, leading and trailing whitespace
683  *                and respects escaping of newline and comment char.
684  *                Also writes the file to fs->proxy_args.
685  *
686  * Parameters  :
687  *          1  :  buf = Buffer to use.
688  *          2  :  buflen = Size of buffer in bytes.
689  *          3  :  fp = File to read from
690  *          4  :  fs = File will be written to fs->proxy_args.  May
691  *                be NULL to disable this feature.
692  *
693  * Returns     :  NULL on EOF or error
694  *                Otherwise, returns buf.
695  *
696  *********************************************************************/
697 char *read_config_line(char *buf, int buflen, FILE *fp, struct file_list *fs)
698 {
699    char *p, *q;
700    char linebuf[BUFSIZ];
701    int contflag = 0;
702
703    *buf = '\0';
704
705    while (fgets(linebuf, sizeof(linebuf), fp))
706    {
707 #ifndef SPLIT_PROXY_ARGS
708       if (fs && !suppress_blocklists)
709       {
710          char *html_line = html_encode(linebuf);
711          if (html_line != NULL)
712          {
713             fs->proxy_args = strsav(fs->proxy_args, html_line);
714             freez(html_line);
715          }
716          fs->proxy_args = strsav(fs->proxy_args, "<br>");
717       }
718 #endif /* ndef SPLIT_PROXY_ARGS */
719
720       /* Trim off newline */
721       if ((p = strpbrk(linebuf, "\r\n")) != NULL)
722       {
723          *p = '\0';
724       }
725
726       /* Line continuation? Trim escape and set flag. */
727       if ((p != linebuf) && (*--p == '\\'))
728       {
729          contflag = 1;
730          *p = '\0';
731       }
732
733       /* If there's a comment char.. */
734       if ((p = strpbrk(linebuf, "#")) != NULL)
735       {
736          /* ..and it's escaped, left-shift the line over the escape. */
737          if ((p != linebuf) && (*(p-1) == '\\'))
738          {
739             q = p-1;
740             while ((*q++ = *p++) != '\0') /* nop */;
741          }
742          /* Else, chop off the rest of the line */
743          else
744          {
745             *p = '\0';
746          }
747       }
748
749       /* Write to the buffer */
750       if (*linebuf)
751       {
752          strncat(buf, linebuf, buflen - strlen(buf));
753       }
754
755       /* Continue? */
756       if (contflag)
757       {
758          contflag = 0;
759                         continue;
760       }
761
762       /* Remove leading and trailing whitespace */         
763       chomp(buf);
764
765       if (*buf)
766       {
767          return buf;
768       }
769    }
770
771    /* EOF */
772    return NULL;
773
774 }
775
776
777 #ifdef ACL_FILES
778 /*********************************************************************
779  *
780  * Function    :  load_aclfile
781  *
782  * Description :  Read and parse an aclfile and add to files list.
783  *
784  * Parameters  :
785  *          1  :  csp = Current client state (buffers, headers, etc...)
786  *
787  * Returns     :  0 => Ok, everything else is an error.
788  *
789  *********************************************************************/
790 int load_aclfile(struct client_state *csp)
791 {
792    FILE *fp;
793    char buf[BUFSIZ], *v[3], *p;
794    int i;
795    struct access_control_list *a, *bl;
796    struct file_list *fs;
797
798    if (!check_file_changed(current_aclfile, csp->config->aclfile, &fs))
799    {
800       /* No need to load */
801       if (csp)
802       {
803          csp->alist = current_aclfile;
804       }
805       return(0);
806    }
807    if (!fs)
808    {
809       goto load_aclfile_error;
810    }
811
812    fs->f = bl = (struct access_control_list *)zalloc(sizeof(*bl));
813    if (bl == NULL)
814    {
815       freez(fs->filename);
816       freez(fs);
817       goto load_aclfile_error;
818    }
819
820    fp = fopen(csp->config->aclfile, "r");
821
822    if (fp == NULL)
823    {
824       goto load_aclfile_error;
825    }
826
827    while (read_config_line(buf, sizeof(buf), fp, fs) != NULL)
828    {
829       i = ssplit(buf, " \t", v, SZ(v), 1, 1);
830
831       /* allocate a new node */
832       a = (struct access_control_list *) zalloc(sizeof(*a));
833
834       if (a == NULL)
835       {
836          fclose(fp);
837          freez(fs->f);
838          freez(fs->filename);
839          freez(fs);
840          goto load_aclfile_error;
841       }
842
843       /* add it to the list */
844       a->next  = bl->next;
845       bl->next = a;
846
847       switch (i)
848       {
849          case 3:
850             if (acl_addr(v[2], a->dst) < 0)
851             {
852                goto load_aclfile_error;
853             }
854             /* no break */
855
856          case 2:
857             if (acl_addr(v[1], a->src) < 0)
858             {
859                goto load_aclfile_error;
860             }
861
862             p = v[0];
863             if (strcmpic(p, "permit") == 0)
864             {
865                a->action = ACL_PERMIT;
866                break;
867             }
868
869             if (strcmpic(p, "deny") == 0)
870             {
871                a->action = ACL_DENY;
872                break;
873             }
874             /* no break */
875
876          default:
877             goto load_aclfile_error;
878       }
879    }
880
881    fclose(fp);
882
883 #ifndef SPLIT_PROXY_ARGS
884    if (!suppress_blocklists)
885    {
886       fs->proxy_args = strsav(fs->proxy_args, "</pre>");
887    }
888 #endif /* ndef SPLIT_PROXY_ARGS */
889
890    if (current_aclfile)
891    {
892       current_aclfile->unloader = unload_aclfile;
893    }
894
895    fs->next = files->next;
896    files->next = fs;
897    current_aclfile = fs;
898
899    if (csp)
900    {
901       csp->alist = fs;
902    }
903
904    return(0);
905
906 load_aclfile_error:
907    log_error(LOG_LEVEL_FATAL, "can't load access control list %s: %E",
908              csp->config->aclfile);
909    return(-1);
910
911 }
912 #endif /* def ACL_FILES */
913
914
915 #ifdef TRUST_FILES
916 /*********************************************************************
917  *
918  * Function    :  load_trustfile
919  *
920  * Description :  Read and parse a trustfile and add to files list.
921  *
922  * Parameters  :
923  *          1  :  csp = Current client state (buffers, headers, etc...)
924  *
925  * Returns     :  0 => Ok, everything else is an error.
926  *
927  *********************************************************************/
928 int load_trustfile(struct client_state *csp)
929 {
930    FILE *fp;
931
932    struct block_spec *b, *bl;
933    struct url_spec **tl;
934
935    char  buf[BUFSIZ], *p, *q;
936    int reject, trusted;
937    struct file_list *fs;
938
939    if (!check_file_changed(current_trustfile, csp->config->trustfile, &fs))
940    {
941       /* No need to load */
942       if (csp)
943       {
944          csp->tlist = current_trustfile;
945       }
946       return(0);
947    }
948    if (!fs)
949    {
950       goto load_trustfile_error;
951    }
952
953    fs->f = bl = (struct block_spec *)zalloc(sizeof(*bl));
954    if (bl == NULL)
955    {
956       goto load_trustfile_error;
957    }
958
959    if ((fp = fopen(csp->config->trustfile, "r")) == NULL)
960    {
961       goto load_trustfile_error;
962    }
963
964    tl = csp->config->trust_list;
965
966    while (read_config_line(buf, sizeof(buf), fp, fs) != NULL)
967    {
968       trusted = 0;
969       reject  = 1;
970
971       if (*buf == '+')
972       {
973          trusted = 1;
974          *buf = '~';
975       }
976
977       if (*buf == '~')
978       {
979          reject = 0;
980          p = buf;
981          q = p+1;
982          while ((*p++ = *q++))
983          {
984             /* nop */
985          }
986       }
987
988       /* skip blank lines */
989       if (*buf == '\0')
990       {
991          continue;
992       }
993
994       /* allocate a new node */
995       if ((b = zalloc(sizeof(*b))) == NULL)
996       {
997          fclose(fp);
998          goto load_trustfile_error;
999       }
1000
1001       /* add it to the list */
1002       b->next  = bl->next;
1003       bl->next = b;
1004
1005       b->reject = reject;
1006
1007       /* Save the URL pattern */
1008       if (create_url_spec(b->url, buf))
1009       {
1010          fclose(fp);
1011          goto load_trustfile_error;
1012       }
1013
1014       /*
1015        * save a pointer to URL's spec in the list of trusted URL's, too
1016        */
1017       if (trusted)
1018       {
1019          *tl++ = b->url;
1020       }
1021    }
1022
1023    *tl = NULL;
1024
1025    fclose(fp);
1026
1027 #ifndef SPLIT_PROXY_ARGS
1028    if (!suppress_blocklists)
1029    {
1030       fs->proxy_args = strsav(fs->proxy_args, "</pre>");
1031    }
1032 #endif /* ndef SPLIT_PROXY_ARGS */
1033
1034    /* the old one is now obsolete */
1035    if (current_trustfile)
1036    {
1037       current_trustfile->unloader = unload_trustfile;
1038    }
1039
1040    fs->next    = files->next;
1041    files->next = fs;
1042    current_trustfile = fs;
1043
1044    if (csp)
1045    {
1046       csp->tlist = fs;
1047    }
1048
1049    return(0);
1050
1051 load_trustfile_error:
1052    log_error(LOG_LEVEL_FATAL, "can't load trustfile '%s': %E",
1053              csp->config->trustfile);
1054    return(-1);
1055
1056 }
1057 #endif /* def TRUST_FILES */
1058
1059
1060 /*********************************************************************
1061  *
1062  * Function    :  load_forwardfile
1063  *
1064  * Description :  Read and parse a forwardfile and add to files list.
1065  *
1066  * Parameters  :
1067  *          1  :  csp = Current client state (buffers, headers, etc...)
1068  *
1069  * Returns     :  0 => Ok, everything else is an error.
1070  *
1071  *********************************************************************/
1072 int load_forwardfile(struct client_state *csp)
1073 {
1074    FILE *fp;
1075
1076    struct forward_spec *b, *bl;
1077    char buf[BUFSIZ];
1078    char *p, *tmp;
1079    char *vec[4];
1080    int n;
1081    struct file_list *fs;
1082    const struct gateway *gw;
1083
1084    if (!check_file_changed(current_forwardfile, csp->config->forwardfile, &fs))
1085    {
1086       /* No need to load */
1087       if (csp)
1088       {
1089          csp->flist = current_forwardfile;
1090       }
1091       return(0);
1092    }
1093    if (!fs)
1094    {
1095       goto load_forwardfile_error;
1096    }
1097
1098    fs->f = bl = (struct forward_spec  *)zalloc(sizeof(*bl));
1099
1100    if ((fs == NULL) || (bl == NULL))
1101    {
1102       goto load_forwardfile_error;
1103    }
1104
1105    if ((fp = fopen(csp->config->forwardfile, "r")) == NULL)
1106    {
1107       goto load_forwardfile_error;
1108    }
1109
1110    tmp = NULL;
1111
1112    while (read_config_line(buf, sizeof(buf), fp, fs) != NULL)
1113    {
1114       freez(tmp);
1115
1116       tmp = strdup(buf);
1117
1118       n = ssplit(tmp, " \t", vec, SZ(vec), 1, 1);
1119
1120       if (n != 4)
1121       {
1122          log_error(LOG_LEVEL_ERROR, "error in forwardfile: %s", buf);
1123          continue;
1124       }
1125
1126       strcpy(buf, vec[0]);
1127
1128       /* skip lines containing only ~ */
1129       if (*buf == '\0')
1130       {
1131          continue;
1132       }
1133
1134       /* allocate a new node */
1135       if (((b = zalloc(sizeof(*b))) == NULL)
1136       )
1137       {
1138          fclose(fp);
1139          goto load_forwardfile_error;
1140       }
1141
1142       /* add it to the list */
1143       b->next  = bl->next;
1144       bl->next = b;
1145
1146       /* Save the URL pattern */
1147       if (create_url_spec(b->url, buf))
1148       {
1149          fclose(fp);
1150          goto load_forwardfile_error;
1151       }
1152
1153       /* now parse the gateway specs */
1154
1155       p = vec[2];
1156
1157       for (gw = gateways; gw->name; gw++)
1158       {
1159          if (strcmp(gw->name, p) == 0)
1160          {
1161             break;
1162          }
1163       }
1164
1165       if (gw->name == NULL)
1166       {
1167          goto load_forwardfile_error;
1168       }
1169
1170       /* save this as the gateway type */
1171       *b->gw = *gw;
1172
1173       /* now parse the gateway host[:port] spec */
1174       p = vec[3];
1175
1176       if (strcmp(p, ".") != 0)
1177       {
1178          b->gw->gateway_host = strdup(p);
1179
1180          if ((p = strchr(b->gw->gateway_host, ':')))
1181          {
1182             *p++ = '\0';
1183             b->gw->gateway_port = atoi(p);
1184          }
1185
1186          if (b->gw->gateway_port <= 0)
1187          {
1188             goto load_forwardfile_error;
1189          }
1190       }
1191
1192       /* now parse the forwarding spec */
1193       p = vec[1];
1194
1195       if (strcmp(p, ".") != 0)
1196       {
1197          b->gw->forward_host = strdup(p);
1198
1199          if ((p = strchr(b->gw->forward_host, ':')))
1200          {
1201             *p++ = '\0';
1202             b->gw->forward_port = atoi(p);
1203          }
1204
1205          if (b->gw->forward_port <= 0)
1206          {
1207             b->gw->forward_port = 8000;
1208          }
1209       }
1210    }
1211
1212    freez(tmp);
1213
1214    fclose(fp);
1215
1216 #ifndef SPLIT_PROXY_ARGS
1217    if (!suppress_blocklists)
1218    {
1219       fs->proxy_args = strsav(fs->proxy_args, "</pre>");
1220    }
1221 #endif /* ndef SPLIT_PROXY_ARGS */
1222
1223    /* the old one is now obsolete */
1224    if (current_forwardfile)
1225    {
1226       current_forwardfile->unloader = unload_forwardfile;
1227    }
1228
1229    fs->next    = files->next;
1230    files->next = fs;
1231    current_forwardfile = fs;
1232
1233    if (csp)
1234    {
1235       csp->flist = fs;
1236    }
1237
1238    return(0);
1239
1240 load_forwardfile_error:
1241    log_error(LOG_LEVEL_FATAL, "can't load forwardfile '%s': %E",
1242              csp->config->forwardfile);
1243    return(-1);
1244
1245 }
1246
1247
1248 #ifdef PCRS
1249 /*********************************************************************
1250  *
1251  * Function    :  load_re_filterfile
1252  *
1253  * Description :  Load the re_filterfile. Each non-comment, non-empty
1254  *                line is instantly added to the joblist, which is
1255  *                a chained list of pcrs_job structs.
1256  *
1257  * Parameters  :
1258  *          1  :  csp = Current client state (buffers, headers, etc...)
1259  *
1260  * Returns     :  0 => Ok, everything else is an error.
1261  *
1262  *********************************************************************/
1263 int load_re_filterfile(struct client_state *csp)
1264 {
1265    FILE *fp;
1266
1267    struct re_filterfile_spec *bl;
1268    struct file_list *fs;
1269
1270    char  buf[BUFSIZ];
1271    int error;
1272    pcrs_job *dummy;
1273
1274    if (!check_file_changed(current_re_filterfile, csp->config->re_filterfile, &fs))
1275    {
1276       /* No need to load */
1277       if (csp)
1278       {
1279          csp->rlist = current_re_filterfile;
1280       }
1281       return(0);
1282    }
1283    if (!fs)
1284    {
1285       goto load_re_filterfile_error;
1286    }
1287
1288    fs->f = bl = (struct re_filterfile_spec  *)zalloc(sizeof(*bl));
1289    if (bl == NULL)
1290    {
1291       goto load_re_filterfile_error;
1292    }
1293
1294    /* Open the file or fail */
1295    if ((fp = fopen(csp->config->re_filterfile, "r")) == NULL)
1296    {
1297       goto load_re_filterfile_error;
1298    }
1299
1300    /* Read line by line */
1301    while (read_config_line(buf, sizeof(buf), fp, fs) != NULL)
1302    {
1303       enlist( bl->patterns, buf );
1304
1305       /* We have a meaningful line -> make it a job */
1306       if ((dummy = pcrs_make_job(buf, &error)) == NULL)
1307       {
1308          log_error(LOG_LEVEL_RE_FILTER, 
1309                "Adding re_filter job %s failed with error %d.", buf, error);
1310          continue;
1311       }
1312       else
1313       {
1314          dummy->next = bl->joblist;
1315          bl->joblist = dummy;
1316          log_error(LOG_LEVEL_RE_FILTER, "Adding re_filter job %s succeeded.", buf);
1317       }
1318    }
1319
1320    fclose(fp);
1321
1322 #ifndef SPLIT_PROXY_ARGS
1323    if (!suppress_blocklists)
1324    {
1325       fs->proxy_args = strsav(fs->proxy_args, "</pre>");
1326    }
1327 #endif /* ndef SPLIT_PROXY_ARGS */
1328
1329    /* the old one is now obsolete */
1330    if ( NULL != current_re_filterfile )
1331    {
1332       current_re_filterfile->unloader = unload_re_filterfile;
1333    }
1334
1335    fs->next    = files->next;
1336    files->next = fs;
1337    current_re_filterfile = fs;
1338
1339    if (csp)
1340    {
1341       csp->rlist = fs;
1342    }
1343
1344    return( 0 );
1345
1346 load_re_filterfile_error:
1347    log_error(LOG_LEVEL_FATAL, "can't load re_filterfile '%s': %E", 
1348              csp->config->re_filterfile);
1349    return(-1);
1350
1351 }
1352 #endif /* def PCRS */
1353
1354
1355 /*********************************************************************
1356  *
1357  * Function    :  add_loader
1358  *
1359  * Description :  Called from `load_config'.  Called once for each input
1360  *                file found in config.
1361  *
1362  * Parameters  :
1363  *          1  :  loader = pointer to a function that can parse and load
1364  *                the appropriate config file.
1365  *          2  :  config = The configuration_spec to add the loader to.
1366  *
1367  * Returns     :  N/A
1368  *
1369  *********************************************************************/
1370 void add_loader(int (*loader)(struct client_state *), 
1371                 struct configuration_spec * config)
1372 {
1373    int i;
1374
1375    for (i=0; i < NLOADERS; i++)
1376    {
1377       if (config->loaders[i] == NULL)
1378       {
1379          config->loaders[i] = loader;
1380          break;
1381       }
1382    }
1383
1384 }
1385
1386
1387 /*********************************************************************
1388  *
1389  * Function    :  run_loader
1390  *
1391  * Description :  Called from `load_config' and `listen_loop'.  This
1392  *                function keeps the "csp" current with any file mods
1393  *                since the last loop.  If a file is unchanged, the
1394  *                loader functions do NOT reload the file.
1395  *
1396  * Parameters  :
1397  *          1  :  csp = Current client state (buffers, headers, etc...)
1398  *                      Must be non-null.  Reads: "csp->config"
1399  *                      Writes: various data members.
1400  *
1401  * Returns     :  0 => Ok, everything else is an error.
1402  *
1403  *********************************************************************/
1404 int run_loader(struct client_state *csp)
1405 {
1406    int ret = 0;
1407    int i;
1408
1409    for (i=0; i < NLOADERS; i++)
1410    {
1411       if (csp->config->loaders[i] == NULL)
1412       {
1413          break;
1414       }
1415       ret |= (csp->config->loaders[i])(csp);
1416    }
1417    return(ret);
1418
1419 }
1420
1421
1422 /*
1423   Local Variables:
1424   tab-width: 3
1425   end:
1426 */