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