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