Added support for enable-edit-actions and enable-remote-toggle config
[privoxy.git] / loadcfg.c
1 const char loadcfg_rcs[] = "$Id: loadcfg.c,v 1.23 2001/10/07 15:36:00 oes Exp $";
2 /*********************************************************************
3  *
4  * File        :  $Source: /cvsroot/ijbswa/current/loadcfg.c,v $
5  *
6  * Purpose     :  Loads settings from the configuration file into
7  *                global variables.  This file contains both the 
8  *                routine to load the configuration and the global
9  *                variables it writes to.
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: loadcfg.c,v $
38  *    Revision 1.23  2001/10/07 15:36:00  oes
39  *    Introduced new config option "buffer-limit"
40  *
41  *    Revision 1.22  2001/09/22 16:36:59  jongfoster
42  *    Removing unused parameter fs from read_config_line()
43  *
44  *    Revision 1.21  2001/09/16 17:10:43  jongfoster
45  *    Moving function savearg() here, since it was the only thing left in
46  *    showargs.c.
47  *
48  *    Revision 1.20  2001/07/30 22:08:36  jongfoster
49  *    Tidying up #defines:
50  *    - All feature #defines are now of the form FEATURE_xxx
51  *    - Permanently turned off WIN_GUI_EDIT
52  *    - Permanently turned on WEBDAV and SPLIT_PROXY_ARGS
53  *
54  *    Revision 1.19  2001/07/15 17:45:16  jongfoster
55  *    Removing some unused #includes
56  *
57  *    Revision 1.18  2001/07/13 14:01:14  oes
58  *     - Removed all #ifdef PCRS
59  *     - Removed vim-settings
60  *
61  *    Revision 1.17  2001/06/29 13:31:03  oes
62  *    - Improved comments
63  *    - Fixed (actionsfile) and sorted hashes
64  *    - Introduced admin_address and proxy-info-url
65  *      as config parameters
66  *    - Renamed config->proxy_args_invocation (which didn't have
67  *      the invocation but the options!) to config->proxy_args
68  *    - Various adaptions
69  *    - Removed logentry from cancelled commit
70  *
71  *    Revision 1.16  2001/06/09 10:55:28  jongfoster
72  *    Changing BUFSIZ ==> BUFFER_SIZE
73  *
74  *    Revision 1.15  2001/06/07 23:13:40  jongfoster
75  *    Merging ACL and forward files into config file.
76  *    Cosmetic: Sorting config file options alphabetically.
77  *    Cosmetic: Adding brief syntax comments to config file options.
78  *
79  *    Revision 1.14  2001/06/07 14:46:25  joergs
80  *    Missing make_path() added for re_filterfile.
81  *
82  *    Revision 1.13  2001/06/05 22:33:54  jongfoster
83  *
84  *    Fixed minor memory leak.
85  *    Also now uses make_path to prepend the pathnames.
86  *
87  *    Revision 1.12  2001/06/05 20:04:09  jongfoster
88  *    Now uses _snprintf() in place of snprintf() under Win32.
89  *
90  *    Revision 1.11  2001/06/04 18:31:58  swa
91  *    files are now prefixed with either `confdir' or `logdir'.
92  *    `make redhat-dist' replaces both entries confdir and logdir
93  *    with redhat values
94  *
95  *    Revision 1.10  2001/06/03 19:11:54  oes
96  *    introduced confdir option
97  *
98  *    Revision 1.9  2001/06/01 20:06:24  jongfoster
99  *    Removed support for "tinygif" option - moved to actions file.
100  *
101  *    Revision 1.8  2001/05/31 21:27:13  jongfoster
102  *    Removed many options from the config file and into the
103  *    "actions" file: add_forwarded, suppress_vanilla_wafer,
104  *    wafer, add_header, user_agent, referer, from
105  *    Also globally replaced "permission" with "action".
106  *
107  *    Revision 1.7  2001/05/29 09:50:24  jongfoster
108  *    Unified blocklist/imagelist/permissionslist.
109  *    File format is still under discussion, but the internal changes
110  *    are (mostly) done.
111  *
112  *    Also modified interceptor behaviour:
113  *    - We now intercept all URLs beginning with one of the following
114  *      prefixes (and *only* these prefixes):
115  *        * http://i.j.b/
116  *        * http://ijbswa.sf.net/config/
117  *        * http://ijbswa.sourceforge.net/config/
118  *    - New interceptors "home page" - go to http://i.j.b/ to see it.
119  *    - Internal changes so that intercepted and fast redirect pages
120  *      are not replaced with an image.
121  *    - Interceptors now have the option to send a binary page direct
122  *      to the client. (i.e. ijb-send-banner uses this)
123  *    - Implemented show-url-info interceptor.  (Which is why I needed
124  *      the above interceptors changes - a typical URL is
125  *      "http://i.j.b/show-url-info?url=www.somesite.com/banner.gif".
126  *      The previous mechanism would not have intercepted that, and
127  *      if it had been intercepted then it then it would have replaced
128  *      it with an image.)
129  *
130  *    Revision 1.6  2001/05/26 00:28:36  jongfoster
131  *    Automatic reloading of config file.
132  *    Removed obsolete SIGHUP support (Unix) and Reload menu option (Win32).
133  *    Most of the global variables have been moved to a new
134  *    struct configuration_spec, accessed through csp->config->globalname
135  *    Most of the globals remaining are used by the Win32 GUI.
136  *
137  *    Revision 1.5  2001/05/25 22:34:30  jongfoster
138  *    Hard tabs->Spaces
139  *
140  *    Revision 1.4  2001/05/22 18:46:04  oes
141  *
142  *    - Enabled filtering banners by size rather than URL
143  *      by adding patterns that replace all standard banner
144  *      sizes with the "Junkbuster" gif to the re_filterfile
145  *
146  *    - Enabled filtering WebBugs by providing a pattern
147  *      which kills all 1x1 images
148  *
149  *    - Added support for PCRE_UNGREEDY behaviour to pcrs,
150  *      which is selected by the (nonstandard and therefore
151  *      capital) letter 'U' in the option string.
152  *      It causes the quantifiers to be ungreedy by default.
153  *      Appending a ? turns back to greedy (!).
154  *
155  *    - Added a new interceptor ijb-send-banner, which
156  *      sends back the "Junkbuster" gif. Without imagelist or
157  *      MSIE detection support, or if tinygif = 1, or the
158  *      URL isn't recognized as an imageurl, a lame HTML
159  *      explanation is sent instead.
160  *
161  *    - Added new feature, which permits blocking remote
162  *      script redirects and firing back a local redirect
163  *      to the browser.
164  *      The feature is conditionally compiled, i.e. it
165  *      can be disabled with --disable-fast-redirects,
166  *      plus it must be activated by a "fast-redirects"
167  *      line in the config file, has its own log level
168  *      and of course wants to be displayed by show-proxy-args
169  *      Note: Boy, all the #ifdefs in 1001 locations and
170  *      all the fumbling with configure.in and acconfig.h
171  *      were *way* more work than the feature itself :-(
172  *
173  *    - Because a generic redirect template was needed for
174  *      this, tinygif = 3 now uses the same.
175  *
176  *    - Moved GIFs, and other static HTTP response templates
177  *      to project.h
178  *
179  *    - Some minor fixes
180  *
181  *    - Removed some >400 CRs again (Jon, you really worked
182  *      a lot! ;-)
183  *
184  *    Revision 1.3  2001/05/20 01:21:20  jongfoster
185  *    Version 2.9.4 checkin.
186  *    - Merged popupfile and cookiefile, and added control over PCRS
187  *      filtering, in new "permissionsfile".
188  *    - Implemented LOG_LEVEL_FATAL, so that if there is a configuration
189  *      file error you now get a message box (in the Win32 GUI) rather
190  *      than the program exiting with no explanation.
191  *    - Made killpopup use the PCRS MIME-type checking and HTTP-header
192  *      skipping.
193  *    - Removed tabs from "config"
194  *    - Moved duplicated url parsing code in "loaders.c" to a new funcition.
195  *    - Bumped up version number.
196  *
197  *    Revision 1.2  2001/05/17 23:01:01  oes
198  *     - Cleaned CRLF's from the sources and related files
199  *
200  *    Revision 1.1.1.1  2001/05/15 13:58:58  oes
201  *    Initial import of version 2.9.3 source tree
202  *
203  *
204  *********************************************************************/
205 \f
206
207 #include "config.h"
208
209 #include <stdio.h>
210 #include <sys/types.h>
211 #include <stdlib.h>
212 #include <string.h>
213 #include <signal.h>
214 #include <fcntl.h>
215 #include <errno.h>
216 #include <ctype.h>
217
218 #ifdef _WIN32
219
220 # include <windows.h>
221
222 # include "win32.h"
223 # ifndef _WIN_CONSOLE
224 #  include "w32log.h"
225 # endif /* ndef _WIN_CONSOLE */
226
227 /* VC++ has "_snprintf", not "snprintf" */
228 #define snprintf _snprintf
229
230 #else /* ifndef _WIN32 */
231
232 # include <unistd.h>
233 # include <sys/time.h>
234 # include <sys/wait.h>
235 # include <sys/stat.h>
236 # include <signal.h>
237
238 #endif
239
240 #include "loadcfg.h"
241 #include "list.h"
242 #include "jcc.h"
243 #include "filters.h"
244 #include "loaders.h"
245 #include "miscutil.h"
246 #include "errlog.h"
247 #include "ssplit.h"
248 #include "encode.h"
249
250 const char loadcfg_h_rcs[] = LOADCFG_H_VERSION;
251
252 /*
253  * Fix a problem with Solaris.  There should be no effect on other
254  * platforms.
255  * Solaris's isspace() is a macro which uses it's argument directly
256  * as an array index.  Therefore we need to make sure that high-bit
257  * characters generate +ve values, and ideally we also want to make
258  * the argument match the declared parameter type of "int".
259  */
260 #define ijb_isupper(__X) isupper((int)(unsigned char)(__X))
261 #define ijb_tolower(__X) tolower((int)(unsigned char)(__X))
262
263 #ifdef FEATURE_TOGGLE
264 /* by haroon - indicates if ijb is enabled */
265 int g_bToggleIJB        = 1;   /* JunkBusters is enabled by default. */
266 #endif /* def FEATURE_TOGGLE */
267
268 /* The filename of the configfile */
269 const char *configfile  = NULL;
270
271 /*
272  * CGI functions will later need access to the invocation args,
273  * so we will make argc and argv global.
274  */
275 int Argc = 0;
276 const char **Argv = NULL;
277
278 static struct file_list *current_configfile = NULL;
279
280
281 /*
282  * This takes the "cryptic" hash of each keyword and aliases them to
283  * something a little more readable.  This also makes changing the
284  * hash values easier if they should change or the hash algorthm changes.
285  * Use the included "hash" program to find out what the hash will be
286  * for any string supplied on the command line.  (Or just put it in the
287  * config file and read the number from the error message in the log).
288  *
289  * Please keep this list sorted alphabetically (but with the Windows
290  * console and GUI specific options last).
291  */
292
293 #define hash_actions_file              1196306641ul /* "actionsfile" */
294 #define hash_admin_address             4112573064ul /* "admin-address" */
295 #define hash_buffer_limit              1881726070ul /* "buffer-limit */
296 #define hash_confdir                      1978389ul /* "confdir" */
297 #define hash_debug                          78263ul /* "debug" */
298 #define hash_deny_access               1227333715ul /* "deny-access" */
299 #define hash_enable_edit_actions       2517097536ul /* "enable-edit-actions" */
300 #define hash_enable_remote_toggle      2979744683ul /* "enable-remote-toggle" */
301 #define hash_forward                      2029845ul /* "forward" */
302 #define hash_forward_socks4            3963965521ul /* "forward-socks4" */
303 #define hash_forward_socks4a           2639958518ul /* "forward-socks4a" */
304 #define hash_jarfile                      2046641ul /* "jarfile" */
305 #define hash_listen_address            1255650842ul /* "listen-address" */
306 #define hash_logdir                        422889ul /* "logdir" */
307 #define hash_logfile                      2114766ul /* "logfile" */
308 #define hash_permit_access             3587953268ul /* "permit-access" */
309 #define hash_proxy_info_url            3903079059ul /* "proxy-info-url" */
310 #define hash_re_filterfile             3877522444ul /* "re_filterfile" */
311 #define hash_single_threaded           4250084780ul /* "single-threaded" */
312 #define hash_suppress_blocklists       1948693308ul /* "suppress-blocklists" */
313 #define hash_toggle                        447966ul /* "toggle" */
314 #define hash_trust_info_url             430331967ul /* "trust-info-url" */
315 #define hash_trustfile                   56494766ul /* "trustfile" */
316
317 #define hash_activity_animation        1817904738ul /* "activity-animation" */
318 #define hash_close_button_minimizes    3651284693ul /* "close-button-minimizes" */
319 #define hash_hide_console              2048809870ul /* "hide-console" */
320 #define hash_log_buffer_size           2918070425ul /* "log-buffer-size" */
321 #define hash_log_font_name             2866730124ul /* "log-font-name" */
322 #define hash_log_font_size             2866731014ul /* "log-font-size" */
323 #define hash_log_highlight_messages    4032101240ul /* "log-highlight-messages" */
324 #define hash_log_max_lines             2868344173ul /* "log-max-lines" */
325 #define hash_log_messages              2291744899ul /* "log-messages" */
326 #define hash_show_on_task_bar           215410365ul /* "show-on-task-bar" */
327
328
329 static void savearg(char *c, char *o, struct configuration_spec * config);
330
331
332 /*********************************************************************
333  *
334  * Function    :  unload_configfile
335  *
336  * Description :  Free the config structure and all components.
337  *
338  * Parameters  :
339  *          1  :  data: struct configuration_spec to unload
340  *
341  * Returns     :  N/A
342  *
343  *********************************************************************/
344 void unload_configfile (void * data)
345 {
346    struct configuration_spec * config = (struct configuration_spec *)data;
347    struct forward_spec *cur_fwd = config->forward;
348 #ifdef FEATURE_ACL
349    struct access_control_list *cur_acl = config->acl;
350
351    while (cur_acl != NULL)
352    {
353       struct access_control_list * next_acl = cur_acl->next;
354       free(cur_acl);
355       cur_acl = next_acl;
356    }
357    config->acl = NULL;
358 #endif /* def FEATURE_ACL */
359
360    while (cur_fwd != NULL)
361    {
362       struct forward_spec * next_fwd = cur_fwd->next;
363       free_url(cur_fwd->url);
364
365       freez(cur_fwd->gateway_host);
366       freez(cur_fwd->forward_host);
367       free(cur_fwd);
368       cur_fwd = next_fwd;
369    }
370    config->forward = NULL;
371    
372 #ifdef FEATURE_COOKIE_JAR
373    if ( NULL != config->jar )
374    {
375       fclose( config->jar );
376       config->jar = NULL;
377    }
378 #endif /* def FEATURE_COOKIE_JAR */
379
380    freez((char *)config->confdir);
381    freez((char *)config->logdir);
382
383    freez((char *)config->haddr);
384    freez((char *)config->logfile);
385
386    freez((char *)config->actions_file);
387    freez((char *)config->admin_address);
388    freez((char *)config->proxy_info_url);
389    freez((char *)config->proxy_args);
390
391 #ifdef FEATURE_COOKIE_JAR
392    freez((char *)config->jarfile);
393 #endif /* def FEATURE_COOKIE_JAR */
394
395    freez((char *)config->re_filterfile);
396
397 }
398
399
400 /*********************************************************************
401  *
402  * Function    :  load_config
403  *
404  * Description :  Load the config file and all parameters.
405  *
406  * Parameters  :
407  *          1  :  csp = Client state (the config member will be 
408  *                filled in by this function).
409  *
410  * Returns     :  0 => Ok, everything else is an error.
411  *
412  *********************************************************************/
413 struct configuration_spec * load_config(void)
414 {
415    char buf[BUFFER_SIZE];
416    char *p, *q;
417    FILE *configfp = NULL;
418    struct configuration_spec * config = NULL;
419    struct client_state * fake_csp;
420    struct file_list *fs;
421
422    if (!check_file_changed(current_configfile, configfile, &fs))
423    {
424       /* No need to load */
425       return ((struct configuration_spec *)current_configfile->f);
426    }
427    if (!fs)
428    {
429       log_error(LOG_LEVEL_FATAL, "can't check configuration file '%s':  %E",
430                 configfile);
431    }
432
433    log_error(LOG_LEVEL_INFO, "loading configuration file '%s':", configfile);
434
435 #ifdef FEATURE_TOGGLE
436    g_bToggleIJB      = 1;
437 #endif /* def FEATURE_TOGGLE */
438
439    fs->f = config = (struct configuration_spec *)zalloc(sizeof(*config));
440
441    if (config==NULL)
442    {
443       freez(fs->filename);
444       freez(fs);
445       log_error(LOG_LEVEL_FATAL, "can't allocate memory for configuration");
446       /* Never get here - LOG_LEVEL_FATAL causes program exit */
447    }
448
449    /*
450     * This is backwards from how it's usually done.
451     * Following the usual pattern, "fs" would be stored in a member 
452     * variable in "csp", and then we'd access "config" from "fs->f",
453     * using a cast.  However, "config" is used so often that a 
454     * cast each time would be very ugly, and the extra indirection
455     * would waste CPU cycles.  Therefore we store "config" in
456     * "csp->config", and "fs" in "csp->config->config_file_list".
457     */
458    config->config_file_list = fs;
459
460    /*
461     * Set to defaults
462     */
463
464    config->multi_threaded    = 1;
465    config->hport             = HADDR_PORT;
466    config->buffer_limit      = 4096 * 1024;
467
468    if ((configfp = fopen(configfile, "r")) == NULL)
469    {
470       log_error(LOG_LEVEL_FATAL, "can't open configuration file '%s':  %E",
471               configfile);
472       /* Never get here - LOG_LEVEL_FATAL causes program exit */
473    }
474
475    while (read_config_line(buf, sizeof(buf), configfp) != NULL)
476    {
477       char cmd[BUFFER_SIZE];
478       char arg[BUFFER_SIZE];
479       char tmp[BUFFER_SIZE];
480 #ifdef FEATURE_ACL
481       struct access_control_list *cur_acl;
482 #endif /* def FEATURE_ACL */
483       struct forward_spec *cur_fwd;
484       int vec_count;
485       char *vec[3];
486
487       strcpy(tmp, buf);
488
489       /* Copy command (i.e. up to space or tab) into cmd */
490       p = buf;
491       q = cmd;
492       while (*p && (*p != ' ') && (*p != '\t'))
493       {
494          *q++ = *p++;
495       }
496       *q = '\0';
497
498       /* Skip over the whitespace in buf */
499       while (*p && ((*p == ' ') || (*p == '\t')))
500       {
501          p++;
502       }
503
504       /* Copy the argument into arg */
505       strcpy(arg, p);
506
507       /* Should never happen, but check this anyway */
508       if (*cmd == '\0')
509       {
510          continue;
511       }
512
513       /* Make sure the command field is lower case */
514       for (p=cmd; *p; p++)
515       {
516          if (ijb_isupper(*p))
517          {
518             *p = ijb_tolower(*p);
519          }
520       }
521
522       /* Save the argument for show-proxy-args */
523       savearg(cmd, arg, config);
524
525
526       switch( hash_string( cmd ) )
527       {
528 /****************************************************************************
529  * actionsfile actions-file-name
530  * In confdir by default
531  ****************************************************************************/
532          case hash_actions_file :
533             freez((char *)config->actions_file);
534             config->actions_file = make_path(config->confdir, arg);
535             continue;
536
537 /****************************************************************************
538  * admin-address email-address
539  ****************************************************************************/
540          case hash_admin_address :
541             freez((char *)config->admin_address);
542             config->admin_address = strdup(arg);
543             continue;       
544
545 /****************************************************************************
546  * buffer-limit n
547  ****************************************************************************/
548          case hash_buffer_limit :
549             config->buffer_limit = (size_t) 1024 * atoi(arg);
550             continue;       
551
552 /****************************************************************************
553  * confdir directory-name
554  ****************************************************************************/
555          case hash_confdir :
556             freez((char *)config->confdir);
557             config->confdir = strdup(arg);
558             continue;            
559
560 /****************************************************************************
561  * debug n
562  * Specifies debug level, multiple values are ORed together.
563  ****************************************************************************/
564          case hash_debug :
565             config->debug |= atoi(arg);
566             continue;
567
568 /****************************************************************************
569  * deny-access source-ip[/significant-bits] [dest-ip[/significant-bits]]
570  ****************************************************************************/
571 #ifdef FEATURE_ACL
572          case hash_deny_access:
573             vec_count = ssplit(arg, " \t", vec, SZ(vec), 1, 1);
574
575             if ((vec_count != 1) && (vec_count != 2))
576             {
577                log_error(LOG_LEVEL_ERROR, "Wrong number of parameters for "
578                      "deny-access directive in configuration file.");
579                config->proxy_args = strsav( config->proxy_args,
580                   "<br>\nWARNING: Wrong number of parameters for "
581                   "deny-access directive in configuration file.<br><br>\n");
582                continue;
583             }
584
585             /* allocate a new node */
586             cur_acl = (struct access_control_list *) zalloc(sizeof(*cur_acl));
587
588             if (cur_acl == NULL)
589             {
590                log_error(LOG_LEVEL_FATAL, "can't allocate memory for configuration");
591                /* Never get here - LOG_LEVEL_FATAL causes program exit */
592                continue;
593             }
594             cur_acl->action = ACL_DENY;
595
596             if (acl_addr(vec[0], cur_acl->src) < 0)
597             {
598                log_error(LOG_LEVEL_ERROR, "Invalid source IP for deny-access "
599                      "directive in configuration file: \"%s\"", vec[0]);
600                config->proxy_args = strsav( config->proxy_args,
601                   "<br>\nWARNING: Invalid source IP for deny-access directive"
602                   " in configuration file: \"");
603                config->proxy_args = strsav( config->proxy_args,
604                   vec[0]);
605                config->proxy_args = strsav( config->proxy_args,
606                   "\"<br><br>\n");
607                freez(cur_acl);
608                continue;
609             }
610             if (vec_count == 2)
611             {
612                if (acl_addr(vec[1], cur_acl->dst) < 0)
613                {
614                   log_error(LOG_LEVEL_ERROR, "Invalid destination IP for deny-access "
615                         "directive in configuration file: \"%s\"", vec[0]);
616                   config->proxy_args = strsav( config->proxy_args,
617                      "<br>\nWARNING: Invalid destination IP for deny-access directive"
618                      " in configuration file: \"");
619                   config->proxy_args = strsav( config->proxy_args,
620                      vec[0]);
621                   config->proxy_args = strsav( config->proxy_args,
622                      "\"<br><br>\n");
623                   freez(cur_acl);
624                   continue;
625                }
626             }
627
628             /*
629              * Add it to the list.  Note we reverse the list to get the
630              * behaviour the user expects.  With both the ACL and
631              * actions file, the last match wins.  However, the internal
632              * implementations are different:  The actions file is stored
633              * in the same order as the file, and scanned completely.
634              * With the ACL, we reverse the order as we load it, then 
635              * when we scan it we stop as soon as we get a match.
636              */
637             cur_acl->next  = config->acl;
638             config->acl = cur_acl;
639
640             continue;
641 #endif /* def FEATURE_ACL */
642
643 /****************************************************************************
644  * enable-edit-actions 0|1
645  ****************************************************************************/
646 #ifdef FEATURE_CGI_EDIT_ACTIONS
647          case hash_enable_edit_actions:
648             if ((*arg != '\0') && (0 != atoi(arg)))
649             {
650                config->feature_flags |= RUNTIME_FEATURE_CGI_EDIT_ACTIONS;
651             }
652             else
653             {
654                config->feature_flags &= ~RUNTIME_FEATURE_CGI_EDIT_ACTIONS;
655             }
656             continue;
657 #endif /* def FEATURE_CGI_EDIT_ACTIONS */
658
659 /****************************************************************************
660  * enable-remote-toggle 0|1
661  ****************************************************************************/
662 #ifdef FEATURE_CGI_EDIT_ACTIONS
663          case hash_enable_remote_toggle:
664             if ((*arg != '\0') && (0 != atoi(arg)))
665             {
666                config->feature_flags |= RUNTIME_FEATURE_CGI_TOGGLE;
667             }
668             else
669             {
670                config->feature_flags &= ~RUNTIME_FEATURE_CGI_TOGGLE;
671             }
672             continue;
673 #endif /* def FEATURE_CGI_EDIT_ACTIONS */
674
675 /****************************************************************************
676  * forward url-pattern (.|http-proxy-host[:port])
677  ****************************************************************************/
678          case hash_forward:
679             vec_count = ssplit(arg, " \t", vec, SZ(vec), 1, 1);
680
681             if (vec_count != 2)
682             {
683                log_error(LOG_LEVEL_ERROR, "Wrong number of parameters for forward "
684                      "directive in configuration file.");
685                config->proxy_args = strsav( config->proxy_args,
686                   "<br>\nWARNING: Wrong number of parameters for "
687                   "forward directive in configuration file.");
688                continue;
689             }
690
691             /* allocate a new node */
692             cur_fwd = zalloc(sizeof(*cur_fwd));
693             if (cur_fwd == NULL)
694             {
695                log_error(LOG_LEVEL_FATAL, "can't allocate memory for configuration");
696                /* Never get here - LOG_LEVEL_FATAL causes program exit */
697                continue;
698             }
699
700             cur_fwd->type = SOCKS_NONE;
701
702             /* Save the URL pattern */
703             if (create_url_spec(cur_fwd->url, vec[0]))
704             {
705                log_error(LOG_LEVEL_ERROR, "Bad URL specifier for forward "
706                      "directive in configuration file.");
707                config->proxy_args = strsav( config->proxy_args,
708                   "<br>\nWARNING: Bad URL specifier for "
709                   "forward directive in configuration file.");
710                continue;
711             }
712
713             /* Parse the parent HTTP proxy host:port */
714             p = vec[1];
715
716             if (strcmp(p, ".") != 0)
717             {
718                cur_fwd->forward_host = strdup(p);
719
720                if ((p = strchr(cur_fwd->forward_host, ':')))
721                {
722                   *p++ = '\0';
723                   cur_fwd->forward_port = atoi(p);
724                }
725
726                if (cur_fwd->forward_port <= 0)
727                {
728                   cur_fwd->forward_port = 8000;
729                }
730             }
731
732             /* Add to list. */
733             cur_fwd->next = config->forward;
734             config->forward = cur_fwd;
735
736             continue;
737
738 /****************************************************************************
739  * forward-socks4 url-pattern socks-proxy[:port] (.|http-proxy[:port])
740  ****************************************************************************/
741          case hash_forward_socks4:
742             vec_count = ssplit(arg, " \t", vec, SZ(vec), 1, 1);
743
744             if (vec_count != 3)
745             {
746                log_error(LOG_LEVEL_ERROR, "Wrong number of parameters for "
747                      "forward-socks4 directive in configuration file.");
748                config->proxy_args = strsav( config->proxy_args,
749                   "<br>\nWARNING: Wrong number of parameters for "
750                   "forward-socks4 directive in configuration file.");
751                continue;
752             }
753
754             /* allocate a new node */
755             cur_fwd = zalloc(sizeof(*cur_fwd));
756             if (cur_fwd == NULL)
757             {
758                log_error(LOG_LEVEL_FATAL, "can't allocate memory for configuration");
759                /* Never get here - LOG_LEVEL_FATAL causes program exit */
760                continue;
761             }
762
763             cur_fwd->type = SOCKS_4;
764
765             /* Save the URL pattern */
766             if (create_url_spec(cur_fwd->url, vec[0]))
767             {
768                log_error(LOG_LEVEL_ERROR, "Bad URL specifier for forward-socks4 "
769                      "directive in configuration file.");
770                config->proxy_args = strsav( config->proxy_args,
771                   "<br>\nWARNING: Bad URL specifier for "
772                   "forward-socks4 directive in configuration file.");
773                continue;
774             }
775
776             /* Parse the SOCKS proxy host[:port] */
777             p = vec[1];
778
779             if (strcmp(p, ".") != 0)
780             {
781                cur_fwd->gateway_host = strdup(p);
782
783                if ((p = strchr(cur_fwd->gateway_host, ':')))
784                {
785                   *p++ = '\0';
786                   cur_fwd->gateway_port = atoi(p);
787                }
788                if (cur_fwd->gateway_port <= 0)
789                {
790                   cur_fwd->gateway_port = 1080;
791                }
792             }
793
794             /* Parse the parent HTTP proxy host[:port] */
795             p = vec[2];
796
797             if (strcmp(p, ".") != 0)
798             {
799                cur_fwd->forward_host = strdup(p);
800
801                if ((p = strchr(cur_fwd->forward_host, ':')))
802                {
803                   *p++ = '\0';
804                   cur_fwd->forward_port = atoi(p);
805                }
806
807                if (cur_fwd->forward_port <= 0)
808                {
809                   cur_fwd->forward_port = 8000;
810                }
811             }
812
813             /* Add to list. */
814             cur_fwd->next = config->forward;
815             config->forward = cur_fwd;
816             
817             continue;
818
819 /****************************************************************************
820  * forward-socks4a url-pattern socks-proxy[:port] (.|http-proxy[:port])
821  ****************************************************************************/
822          case hash_forward_socks4a:
823             vec_count = ssplit(arg, " \t", vec, SZ(vec), 1, 1);
824
825             if (vec_count != 3)
826             {
827                log_error(LOG_LEVEL_ERROR, "Wrong number of parameters for "
828                      "forward-socks4a directive in configuration file.");
829                config->proxy_args = strsav( config->proxy_args,
830                   "<br>\nWARNING: Wrong number of parameters for "
831                   "forward-socks4a directive in configuration file.");
832                continue;
833             }
834
835             /* allocate a new node */
836             cur_fwd = zalloc(sizeof(*cur_fwd));
837             if (cur_fwd == NULL)
838             {
839                log_error(LOG_LEVEL_FATAL, "can't allocate memory for configuration");
840                /* Never get here - LOG_LEVEL_FATAL causes program exit */
841                continue;
842             }
843
844             cur_fwd->type = SOCKS_4A;
845
846             /* Save the URL pattern */
847             if (create_url_spec(cur_fwd->url, vec[0]))
848             {
849                log_error(LOG_LEVEL_ERROR, "Bad URL specifier for forward-socks4a "
850                      "directive in configuration file.");
851                config->proxy_args = strsav( config->proxy_args,
852                   "<br>\nWARNING: Bad URL specifier for "
853                   "forward-socks4a directive in configuration file.");
854                continue;
855             }
856
857             /* Parse the SOCKS proxy host[:port] */
858             p = vec[1];
859
860             cur_fwd->gateway_host = strdup(p);
861
862             if ((p = strchr(cur_fwd->gateway_host, ':')))
863             {
864                *p++ = '\0';
865                cur_fwd->gateway_port = atoi(p);
866             }
867             if (cur_fwd->gateway_port <= 0)
868             {
869                cur_fwd->gateway_port = 1080;
870             }
871
872             /* Parse the parent HTTP proxy host[:port] */
873             p = vec[2];
874
875             if (strcmp(p, ".") != 0)
876             {
877                cur_fwd->forward_host = strdup(p);
878
879                if ((p = strchr(cur_fwd->forward_host, ':')))
880                {
881                   *p++ = '\0';
882                   cur_fwd->forward_port = atoi(p);
883                }
884
885                if (cur_fwd->forward_port <= 0)
886                {
887                   cur_fwd->forward_port = 8000;
888                }
889             }
890
891             /* Add to list. */
892             cur_fwd->next = config->forward;
893             config->forward = cur_fwd;
894             
895             continue;
896
897 /****************************************************************************
898  * jarfile jar-file-name
899  * In logdir by default
900  ****************************************************************************/
901 #ifdef FEATURE_COOKIE_JAR
902          case hash_jarfile :
903             freez((char *)config->jarfile);
904             config->jarfile = make_path(config->logdir, arg);
905             continue;
906 #endif /* def FEATURE_COOKIE_JAR */
907
908 /****************************************************************************
909  * listen-address [ip][:port]
910  ****************************************************************************/
911          case hash_listen_address :
912             freez((char *)config->haddr);
913             config->haddr = strdup(arg);
914             continue;
915
916 /****************************************************************************
917  * logdir directory-name
918  ****************************************************************************/
919          case hash_logdir :
920             freez((char *)config->logdir);
921             config->logdir = strdup(arg);
922             continue;            
923
924 /****************************************************************************
925  * logfile log-file-name
926  * In logdir by default
927  ****************************************************************************/
928          case hash_logfile :
929             freez((char *)config->logfile);
930             config->logfile = make_path(config->logdir, arg);
931             continue;
932
933 /****************************************************************************
934  * permit-access source-ip[/significant-bits] [dest-ip[/significant-bits]]
935  ****************************************************************************/
936 #ifdef FEATURE_ACL
937          case hash_permit_access:
938             vec_count = ssplit(arg, " \t", vec, SZ(vec), 1, 1);
939
940             if ((vec_count != 1) && (vec_count != 2))
941             {
942                log_error(LOG_LEVEL_ERROR, "Wrong number of parameters for "
943                      "permit-access directive in configuration file.");
944                config->proxy_args = strsav( config->proxy_args,
945                   "<br>\nWARNING: Wrong number of parameters for "
946                   "permit-access directive in configuration file.<br><br>\n");
947
948                continue;
949             }
950
951             /* allocate a new node */
952             cur_acl = (struct access_control_list *) zalloc(sizeof(*cur_acl));
953
954             if (cur_acl == NULL)
955             {
956                log_error(LOG_LEVEL_FATAL, "can't allocate memory for configuration");
957                /* Never get here - LOG_LEVEL_FATAL causes program exit */
958                continue;
959             }
960             cur_acl->action = ACL_PERMIT;
961
962             if (acl_addr(vec[0], cur_acl->src) < 0)
963             {
964                log_error(LOG_LEVEL_ERROR, "Invalid source IP for permit-access "
965                      "directive in configuration file: \"%s\"", vec[0]);
966                config->proxy_args = strsav( config->proxy_args,
967                   "<br>\nWARNING: Invalid source IP for permit-access directive"
968                   " in configuration file: \"");
969                config->proxy_args = strsav( config->proxy_args,
970                   vec[0]);
971                config->proxy_args = strsav( config->proxy_args,
972                   "\"<br><br>\n");
973                freez(cur_acl);
974                continue;
975             }
976             if (vec_count == 2)
977             {
978                if (acl_addr(vec[1], cur_acl->dst) < 0)
979                {
980                   log_error(LOG_LEVEL_ERROR, "Invalid destination IP for "
981                         "permit-access directive in configuration file: \"%s\"",
982                         vec[0]);
983                   config->proxy_args = strsav( config->proxy_args,
984                      "<br>\nWARNING: Invalid destination IP for permit-access directive"
985                      " in configuration file: \"");
986                   config->proxy_args = strsav( config->proxy_args,
987                      vec[0]);
988                   config->proxy_args = strsav( config->proxy_args,
989                      "\"<br><br>\n");
990                   freez(cur_acl);
991                   continue;
992                }
993             }
994
995             /*
996              * Add it to the list.  Note we reverse the list to get the
997              * behaviour the user expects.  With both the ACL and
998              * actions file, the last match wins.  However, the internal
999              * implementations are different:  The actions file is stored
1000              * in the same order as the file, and scanned completely.
1001              * With the ACL, we reverse the order as we load it, then 
1002              * when we scan it we stop as soon as we get a match.
1003              */
1004             cur_acl->next  = config->acl;
1005             config->acl = cur_acl;
1006
1007             continue;
1008 #endif /* def FEATURE_ACL */
1009
1010 /****************************************************************************
1011  * proxy-info-url url
1012  ****************************************************************************/
1013          case hash_proxy_info_url :
1014             freez((char *)config->proxy_info_url);
1015             config->proxy_info_url = strdup(arg);
1016             continue;
1017
1018 /****************************************************************************
1019  * re_filterfile file-name
1020  * In confdir by default.
1021  ****************************************************************************/
1022          case hash_re_filterfile :
1023             freez((char *)config->re_filterfile);
1024             config->re_filterfile = make_path(config->confdir, arg);
1025             continue;
1026
1027 /****************************************************************************
1028  * single-threaded
1029  ****************************************************************************/
1030          case hash_single_threaded :
1031             config->multi_threaded = 0;
1032             continue;
1033
1034 /****************************************************************************
1035  * toggle (0|1)
1036  ****************************************************************************/
1037 #ifdef FEATURE_TOGGLE
1038          case hash_toggle :
1039             g_bToggleIJB = atoi(arg);
1040             continue;
1041 #endif /* def FEATURE_TOGGLE */
1042
1043 /****************************************************************************
1044  * trust-info-url url
1045  ****************************************************************************/
1046 #ifdef FEATURE_TRUST
1047          case hash_trust_info_url :
1048             enlist(config->trust_info, arg);
1049             continue;
1050 #endif /* def FEATURE_TRUST */
1051
1052 /****************************************************************************
1053  * trustfile filename
1054  * (In confdir by default.)
1055  ****************************************************************************/
1056 #ifdef FEATURE_TRUST
1057          case hash_trustfile :
1058             freez((char *)config->trustfile);
1059             config->trustfile = make_path(config->confdir, arg);
1060             continue;
1061 #endif /* def FEATURE_TRUST */
1062
1063
1064 /****************************************************************************
1065  * Win32 Console options:
1066  ****************************************************************************/
1067
1068 /****************************************************************************
1069  * hide-console
1070  ****************************************************************************/
1071 #ifdef _WIN_CONSOLE
1072          case hash_hide_console :
1073             hideConsole = 1;
1074             continue;
1075 #endif /*def _WIN_CONSOLE*/
1076
1077
1078 /****************************************************************************
1079  * Win32 GUI options:
1080  ****************************************************************************/
1081
1082 #if defined(_WIN32) && ! defined(_WIN_CONSOLE)
1083 /****************************************************************************
1084  * activity-animation (0|1)
1085  ****************************************************************************/
1086          case hash_activity_animation :
1087             g_bShowActivityAnimation = atoi(arg);
1088             continue;
1089
1090 /****************************************************************************
1091  *  close-button-minimizes (0|1)
1092  ****************************************************************************/
1093          case hash_close_button_minimizes :
1094             g_bCloseHidesWindow = atoi(arg);
1095             continue;
1096
1097 /****************************************************************************
1098  * log-buffer-size (0|1)
1099  ****************************************************************************/
1100          case hash_log_buffer_size :
1101             g_bLimitBufferSize = atoi(arg);
1102             continue;
1103
1104 /****************************************************************************
1105  * log-font-name fontnane
1106  ****************************************************************************/
1107          case hash_log_font_name :
1108             strcpy( g_szFontFaceName, arg );
1109             continue;
1110
1111 /****************************************************************************
1112  * log-font-size n
1113  ****************************************************************************/
1114          case hash_log_font_size :
1115             g_nFontSize = atoi(arg);
1116             continue;
1117
1118 /****************************************************************************
1119  * log-highlight-messages (0|1)
1120  ****************************************************************************/
1121          case hash_log_highlight_messages :
1122             g_bHighlightMessages = atoi(arg);
1123             continue;
1124
1125 /****************************************************************************
1126  * log-max-lines n
1127  ****************************************************************************/
1128          case hash_log_max_lines :
1129             g_nMaxBufferLines = atoi(arg);
1130             continue;
1131
1132 /****************************************************************************
1133  * log-messages (0|1)
1134  ****************************************************************************/
1135          case hash_log_messages :
1136             g_bLogMessages = atoi(arg);
1137             continue;
1138
1139 /****************************************************************************
1140  * show-on-task-bar (0|1)
1141  ****************************************************************************/
1142          case hash_show_on_task_bar :
1143             g_bShowOnTaskBar = atoi(arg);
1144             continue;
1145
1146 #endif /* defined(_WIN32) && ! defined(_WIN_CONSOLE) */
1147
1148
1149 /****************************************************************************/
1150 /* Warnings about unsupported features                                      */
1151 /****************************************************************************/
1152 #ifndef FEATURE_ACL
1153          case hash_deny_access:
1154 #endif /* ndef FEATURE_ACL */
1155 #ifndef FEATURE_CGI_EDIT_ACTIONS
1156          case hash_enable_edit_actions:
1157          case hash_enable_remote_toggle:
1158 #endif /* def FEATURE_CGI_EDIT_ACTIONS */
1159 #ifndef FEATURE_COOKIE_JAR
1160          case hash_jarfile :
1161 #endif /* ndef FEATURE_COOKIE_JAR */
1162 #ifndef FEATURE_ACL
1163          case hash_permit_access:
1164 #endif /* ndef FEATURE_ACL */
1165 #ifndef FEATURE_TOGGLE
1166          case hash_toggle :
1167 #endif /* ndef FEATURE_TOGGLE */
1168 #ifndef FEATURE_TRUST
1169          case hash_trustfile :
1170          case hash_trust_info_url :
1171 #endif /* ndef FEATURE_TRUST */
1172
1173 #ifndef _WIN_CONSOLE
1174          case hash_hide_console :
1175 #endif /* ndef _WIN_CONSOLE */
1176
1177 #if defined(_WIN_CONSOLE) || ! defined(_WIN32)
1178          case hash_activity_animation :
1179          case hash_close_button_minimizes :
1180          case hash_log_buffer_size :
1181          case hash_log_font_name :
1182          case hash_log_font_size :
1183          case hash_log_highlight_messages :
1184          case hash_log_max_lines :
1185          case hash_log_messages :
1186          case hash_show_on_task_bar :
1187 #endif /* defined(_WIN_CONSOLE) || ! defined(_WIN32) */
1188             /* These warnings are annoying - so hide them. -- Jon */
1189             /* log_error(LOG_LEVEL_INFO, "Unsupported directive \"%s\" ignored.", cmd); */
1190             continue;
1191
1192 /****************************************************************************/
1193          default :
1194 /****************************************************************************/
1195             /*
1196              * I decided that I liked this better as a warning than an
1197              * error.  To change back to an error, just change log level
1198              * to LOG_LEVEL_FATAL.
1199              */
1200             log_error(LOG_LEVEL_ERROR, "Unrecognized directive (%luul) in "
1201                   "configuration file: \"%s\"", hash_string( cmd ), buf);
1202             config->proxy_args = strsav( config->proxy_args, "<br>\nWARNING: unrecognized directive : ");
1203             config->proxy_args = strsav( config->proxy_args, buf);
1204             config->proxy_args = strsav( config->proxy_args, "<br><br>\n");
1205             continue;
1206
1207 /****************************************************************************/
1208       } /* end switch( hash_string(cmd) ) */
1209    } /* end while ( read_config_line(...) ) */
1210
1211    fclose(configfp);
1212
1213    init_error_log(Argv[0], config->logfile, config->debug);
1214
1215    if (config->actions_file)
1216    {
1217       add_loader(load_actions_file, config);
1218    }
1219
1220    if (config->re_filterfile)
1221    {
1222       add_loader(load_re_filterfile, config);
1223    }
1224
1225 #ifdef FEATURE_TRUST
1226    if (config->trustfile)
1227    {
1228       add_loader(load_trustfile, config);
1229    }
1230 #endif /* def FEATURE_TRUST */
1231
1232 #ifdef FEATURE_COOKIE_JAR
1233    if ( NULL != config->jarfile )
1234    {
1235       if ( NULL == (config->jar = fopen(config->jarfile, "a")) )
1236       {
1237          log_error(LOG_LEVEL_FATAL, "can't open jarfile '%s': %E", config->jarfile);
1238          /* Never get here - LOG_LEVEL_FATAL causes program exit */
1239       }
1240       setbuf(config->jar, NULL);
1241    }
1242 #endif /* def FEATURE_COOKIE_JAR */
1243
1244    if ( NULL == config->haddr )
1245    {
1246       config->haddr = strdup( HADDR_DEFAULT );
1247    }
1248
1249    if ( NULL != config->haddr )
1250    {
1251       if ((p = strchr(config->haddr, ':')))
1252       {
1253          *p++ = '\0';
1254          if (*p)
1255          {
1256             config->hport = atoi(p);
1257          }
1258       }
1259
1260       if (config->hport <= 0)
1261       {
1262          *--p = ':';
1263          log_error(LOG_LEVEL_FATAL, "invalid bind port spec %s", config->haddr);
1264          /* Never get here - LOG_LEVEL_FATAL causes program exit */
1265       }
1266       if (*config->haddr == '\0')
1267       {
1268          config->haddr = NULL;
1269       }
1270    }
1271
1272    /*
1273     * Want to run all the loaders once now.
1274     *
1275     * Need to set up a fake csp, so they can get to the config.
1276     */
1277    fake_csp = (struct client_state *) zalloc (sizeof(*fake_csp));
1278    fake_csp->config = config;
1279
1280    if (run_loader(fake_csp))
1281    {
1282       freez(fake_csp);
1283       log_error(LOG_LEVEL_FATAL, "A loader failed while loading config file. Exiting.");
1284       /* Never get here - LOG_LEVEL_FATAL causes program exit */
1285    }
1286    freez(fake_csp);
1287
1288 /* FIXME: this is a kludge for win32 */
1289 #if defined(_WIN32) && !defined (_WIN_CONSOLE)
1290
1291    g_actions_file     = config->actions_file;
1292    g_re_filterfile    = config->re_filterfile;
1293
1294 #ifdef FEATURE_TRUST
1295    g_trustfile        = config->trustfile;
1296 #endif /* def FEATURE_TRUST */
1297    
1298
1299 #endif /* defined(_WIN32) && !defined (_WIN_CONSOLE) */
1300 /* FIXME: end kludge */
1301
1302
1303    config->need_bind = 1;
1304
1305    if (current_configfile)
1306    {
1307       struct configuration_spec * oldcfg = (struct configuration_spec *)
1308                                            current_configfile->f;
1309       /*
1310        * Check if config->haddr,hport == oldcfg->haddr,hport
1311        *
1312        * The following could be written more compactly as a single,
1313        * (unreadably long) if statement.
1314        */
1315       config->need_bind = 0;
1316       if (config->hport != oldcfg->hport)
1317       {
1318          config->need_bind = 1;
1319       }
1320       else if (config->haddr == NULL)
1321       {
1322          if (oldcfg->haddr != NULL)
1323          {
1324             config->need_bind = 1;
1325          }
1326       }
1327       else if (oldcfg->haddr == NULL)
1328       {
1329          config->need_bind = 1;
1330       }
1331       else if (0 != strcmp(config->haddr, oldcfg->haddr))
1332       {
1333          config->need_bind = 1;
1334       }
1335
1336       current_configfile->unloader = unload_configfile;
1337    }
1338
1339    fs->next = files->next;
1340    files->next = fs;
1341
1342    current_configfile = fs;
1343
1344    return (config);
1345 }
1346
1347
1348 /*********************************************************************
1349  *
1350  * Function    :  savearg
1351  *
1352  * Description :  Called from `load_config'.  It saves each non-empty
1353  *                and non-comment line from config into a list.  This
1354  *                list is used to create the show-proxy-args page.
1355  *
1356  * Parameters  :
1357  *          1  :  c = config setting that was found
1358  *          2  :  o = the setting's argument (if any)
1359  *
1360  * Returns     :  N/A
1361  *
1362  *********************************************************************/
1363 static void savearg(char *c, char *o, struct configuration_spec * config)
1364 {
1365    char buf[BUFFER_SIZE];
1366
1367    *buf = '\0';
1368
1369    if ( ( NULL != c ) && ( '\0' != *c ) )
1370    {
1371       if ((c = html_encode(c)))
1372       {
1373          sprintf(buf, "<a href=\"" REDIRECT_URL "option#%s\">%s</a> ", c, c);
1374       }
1375       freez(c);
1376    }
1377    if ( ( NULL != o ) && ( '\0' != *o ) )
1378    {
1379       if ((o = html_encode(o)))
1380       {
1381          if (strncmpic(o, "http://", 7) == 0)
1382          {
1383             strcat(buf, "<a href=\"");
1384             strcat(buf, o);
1385             strcat(buf, "\">");
1386             strcat(buf, o);
1387             strcat(buf, "</a>");
1388          }
1389          else
1390          {
1391             strcat(buf, o);
1392          }
1393       }
1394       freez(o);
1395    }
1396
1397    strcat(buf, "<br>\n");
1398
1399    config->proxy_args = strsav(config->proxy_args, buf);
1400
1401 }
1402
1403
1404 /*
1405   Local Variables:
1406   tab-width: 3
1407   end:
1408 */