Bump version to 3.0.29 UNRELEASED
[privoxy.git] / loadcfg.c
1 /*********************************************************************
2  *
3  * File        :  $Source: /cvsroot/ijbswa/current/loadcfg.c,v $
4  *
5  * Purpose     :  Loads settings from the configuration file into
6  *                global variables.  This file contains both the
7  *                routine to load the configuration and the global
8  *                variables it writes to.
9  *
10  * Copyright   :  Written by and Copyright (C) 2001-2017 the
11  *                Privoxy team. http://www.privoxy.org/
12  *
13  *                Based on the Internet Junkbuster originally written
14  *                by and Copyright (C) 1997 Anonymous Coders and
15  *                Junkbusters Corporation.  http://www.junkbusters.com
16  *
17  *                This program is free software; you can redistribute it
18  *                and/or modify it under the terms of the GNU General
19  *                Public License as published by the Free Software
20  *                Foundation; either version 2 of the License, or (at
21  *                your option) any later version.
22  *
23  *                This program is distributed in the hope that it will
24  *                be useful, but WITHOUT ANY WARRANTY; without even the
25  *                implied warranty of MERCHANTABILITY or FITNESS FOR A
26  *                PARTICULAR PURPOSE.  See the GNU General Public
27  *                License for more details.
28  *
29  *                The GNU General Public License should be included with
30  *                this file.  If not, you can view it at
31  *                http://www.gnu.org/copyleft/gpl.html
32  *                or write to the Free Software Foundation, Inc., 59
33  *                Temple Place - Suite 330, Boston, MA  02111-1307, USA.
34  *
35  *********************************************************************/
36
37
38 #include "config.h"
39
40 #include <stdio.h>
41 #include <sys/types.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <signal.h>
45 #include <fcntl.h>
46 #include <errno.h>
47 #include <ctype.h>
48 #include <assert.h>
49
50 #ifdef _WIN32
51
52 # ifndef STRICT
53 #  define STRICT
54 # endif
55 # include <winsock2.h>
56 # include <windows.h>
57
58 # include "win32.h"
59 # ifndef _WIN_CONSOLE
60 #  include "w32log.h"
61 # endif /* ndef _WIN_CONSOLE */
62
63 #else /* ifndef _WIN32 */
64
65 #ifndef __OS2__
66 # include <unistd.h>
67 # include <sys/wait.h>
68 #endif
69 # include <sys/time.h>
70 # include <sys/stat.h>
71 # include <signal.h>
72
73 #endif
74
75 #include "project.h"
76 #include "loadcfg.h"
77 #include "list.h"
78 #include "jcc.h"
79 #include "filters.h"
80 #include "loaders.h"
81 #include "miscutil.h"
82 #include "errlog.h"
83 #include "ssplit.h"
84 #include "encode.h"
85 #include "urlmatch.h"
86 #include "cgi.h"
87 #include "gateway.h"
88 #ifdef FEATURE_CLIENT_TAGS
89 #include "client-tags.h"
90 #endif
91
92 /*
93  * Default number of seconds after which an
94  * open connection will no longer be reused.
95  */
96 #define DEFAULT_KEEP_ALIVE_TIMEOUT 180
97
98 /*
99  * Default backlog passed to listen().
100  */
101 #define DEFAULT_LISTEN_BACKLOG 128
102
103 #ifdef FEATURE_TOGGLE
104 /* Privoxy is enabled by default. */
105 int global_toggle_state = 1;
106 #endif /* def FEATURE_TOGGLE */
107
108 /* The filename of the configfile */
109 const char *configfile  = NULL;
110
111 /*
112  * CGI functions will later need access to the invocation args,
113  * so we will make argc and argv global.
114  */
115 int Argc = 0;
116 char * const * Argv = NULL;
117
118 static struct file_list *current_configfile = NULL;
119
120
121 /*
122  * This takes the "cryptic" hash of each keyword and aliases them to
123  * something a little more readable.  This also makes changing the
124  * hash values easier if they should change or the hash algorthm changes.
125  * Use the included "hash" program to find out what the hash will be
126  * for any string supplied on the command line.  (Or just put it in the
127  * config file and read the number from the error message in the log).
128  *
129  * Please keep this list sorted alphabetically (but with the Windows
130  * console and GUI specific options last).
131  */
132
133 #define hash_actions_file                1196306641U /* "actionsfile" */
134 #define hash_accept_intercepted_requests 1513024973U /* "accept-intercepted-requests" */
135 #define hash_admin_address               4112573064U /* "admin-address" */
136 #define hash_allow_cgi_request_crunching  258915987U /* "allow-cgi-request-crunching" */
137 #define hash_buffer_limit                1881726070U /* "buffer-limit */
138 #define hash_client_header_order         2701453514U /* "client-header-order" */
139 #define hash_client_specific_tag         3353703383U /* "client-specific-tag" */
140 #define hash_client_tag_lifetime          647957580U /* "client-tag-lifetime" */
141 #define hash_compression_level           2464423563U /* "compression-level" */
142 #define hash_confdir                        1978389U /* "confdir" */
143 #define hash_connection_sharing          1348841265U /* "connection-sharing" */
144 #define hash_debug                            78263U /* "debug" */
145 #define hash_default_server_timeout      2530089913U /* "default-server-timeout" */
146 #define hash_deny_access                 1227333715U /* "deny-access" */
147 #define hash_enable_accept_filter        2909040407U /* "enable-accept-filter" */
148 #define hash_enable_edit_actions         2517097536U /* "enable-edit-actions" */
149 #define hash_enable_compression          3943696946U /* "enable-compression" */
150 #define hash_enable_proxy_authentication_forwarding 4040610791U /* enable-proxy-authentication-forwarding */
151 #define hash_enable_remote_toggle        2979744683U /* "enable-remote-toggle" */
152 #define hash_enable_remote_http_toggle    110543988U /* "enable-remote-http-toggle" */
153 #define hash_enforce_blocks              1862427469U /* "enforce-blocks" */
154 #define hash_filterfile                   250887266U /* "filterfile" */
155 #define hash_forward                        2029845U /* "forward" */
156 #define hash_forward_socks4              3963965521U /* "forward-socks4" */
157 #define hash_forward_socks4a             2639958518U /* "forward-socks4a" */
158 #define hash_forward_socks5              3963965522U /* "forward-socks5" */
159 #define hash_forward_socks5t             2639958542U /* "forward-socks5t" */
160 #define hash_forwarded_connect_retries    101465292U /* "forwarded-connect-retries" */
161 #define hash_handle_as_empty_returns_ok  1444873247U /* "handle-as-empty-doc-returns-ok" */
162 #define hash_hostname                      10308071U /* "hostname" */
163 #define hash_keep_alive_timeout          3878599515U /* "keep-alive-timeout" */
164 #define hash_listen_address              1255650842U /* "listen-address" */
165 #define hash_listen_backlog              1255655735U /* "listen-backlog" */
166 #define hash_logdir                          422889U /* "logdir" */
167 #define hash_logfile                        2114766U /* "logfile" */
168 #define hash_max_client_connections      3595884446U /* "max-client-connections" */
169 #define hash_permit_access               3587953268U /* "permit-access" */
170 #define hash_proxy_info_url              3903079059U /* "proxy-info-url" */
171 #define hash_receive_buffer_size         2880297454U /* "receive-buffer-size */
172 #define hash_single_threaded             4250084780U /* "single-threaded" */
173 #define hash_socket_timeout              1809001761U /* "socket-timeout" */
174 #define hash_split_large_cgi_forms        671658948U /* "split-large-cgi-forms" */
175 #define hash_suppress_blocklists         1948693308U /* "suppress-blocklists" */
176 #define hash_templdir                      11067889U /* "templdir" */
177 #define hash_temporary_directory         1824125181U /* "temporary-directory" */
178 #define hash_tolerate_pipelining         1360286620U /* "tolerate-pipelining" */
179 #define hash_toggle                          447966U /* "toggle" */
180 #define hash_trust_info_url               430331967U /* "trust-info-url" */
181 #define hash_trust_x_forwarded_for       2971537414U /* "trust-x-forwarded-for" */
182 #define hash_trusted_cgi_referrer        4270883427U /* "trusted-cgi-referrer" */
183 #define hash_trustfile                     56494766U /* "trustfile" */
184 #define hash_usermanual                  1416668518U /* "user-manual" */
185 #define hash_activity_animation          1817904738U /* "activity-animation" */
186 #define hash_close_button_minimizes      3651284693U /* "close-button-minimizes" */
187 #define hash_hide_console                2048809870U /* "hide-console" */
188 #define hash_log_buffer_size             2918070425U /* "log-buffer-size" */
189 #define hash_log_font_name               2866730124U /* "log-font-name" */
190 #define hash_log_font_size               2866731014U /* "log-font-size" */
191 #define hash_log_highlight_messages      4032101240U /* "log-highlight-messages" */
192 #define hash_log_max_lines               2868344173U /* "log-max-lines" */
193 #define hash_log_messages                2291744899U /* "log-messages" */
194 #define hash_show_on_task_bar             215410365U /* "show-on-task-bar" */
195
196
197 static void savearg(char *command, char *argument, struct configuration_spec * config);
198 #ifdef FEATURE_CLIENT_TAGS
199 static void free_client_specific_tags(struct client_tag_spec *tag_list);
200 #endif
201
202 /*********************************************************************
203  *
204  * Function    :  unload_configfile
205  *
206  * Description :  Free the config structure and all components.
207  *
208  * Parameters  :
209  *          1  :  data: struct configuration_spec to unload
210  *
211  * Returns     :  N/A
212  *
213  *********************************************************************/
214 static void unload_configfile (void * data)
215 {
216    struct configuration_spec * config = (struct configuration_spec *)data;
217    struct forward_spec *cur_fwd = config->forward;
218    int i;
219
220 #ifdef FEATURE_ACL
221    struct access_control_list *cur_acl = config->acl;
222
223    while (cur_acl != NULL)
224    {
225       struct access_control_list * next_acl = cur_acl->next;
226       free(cur_acl);
227       cur_acl = next_acl;
228    }
229    config->acl = NULL;
230 #endif /* def FEATURE_ACL */
231
232    while (cur_fwd != NULL)
233    {
234       struct forward_spec * next_fwd = cur_fwd->next;
235       free_pattern_spec(cur_fwd->url);
236
237       freez(cur_fwd->gateway_host);
238       freez(cur_fwd->forward_host);
239       free(cur_fwd);
240       cur_fwd = next_fwd;
241    }
242    config->forward = NULL;
243
244    freez(config->confdir);
245    freez(config->logdir);
246    freez(config->templdir);
247    freez(config->hostname);
248 #ifdef FEATURE_EXTERNAL_FILTERS
249    freez(config->temporary_directory);
250 #endif
251
252    for (i = 0; i < MAX_LISTENING_SOCKETS; i++)
253    {
254       freez(config->haddr[i]);
255    }
256    freez(config->logfile);
257
258    for (i = 0; i < MAX_AF_FILES; i++)
259    {
260       freez(config->actions_file_short[i]);
261       freez(config->actions_file[i]);
262       freez(config->re_filterfile_short[i]);
263       freez(config->re_filterfile[i]);
264    }
265
266    list_remove_all(config->ordered_client_headers);
267
268    freez(config->admin_address);
269    freez(config->proxy_info_url);
270    freez(config->proxy_args);
271    freez(config->usermanual);
272    freez(config->trusted_cgi_referrer);
273
274 #ifdef FEATURE_TRUST
275    freez(config->trustfile);
276    list_remove_all(config->trust_info);
277 #endif /* def FEATURE_TRUST */
278
279 #ifdef FEATURE_CLIENT_TAGS
280    free_client_specific_tags(config->client_tags);
281 #endif
282
283    freez(config);
284 }
285
286
287 #ifdef FEATURE_GRACEFUL_TERMINATION
288 /*********************************************************************
289  *
290  * Function    :  unload_current_config_file
291  *
292  * Description :  Unloads current config file - reset to state at
293  *                beginning of program.
294  *
295  * Parameters  :  None
296  *
297  * Returns     :  N/A
298  *
299  *********************************************************************/
300 void unload_current_config_file(void)
301 {
302    if (current_configfile)
303    {
304       current_configfile->unloader = unload_configfile;
305       current_configfile = NULL;
306    }
307 }
308 #endif
309
310
311 #ifdef FEATURE_CLIENT_TAGS
312 /*********************************************************************
313  *
314  * Function    :  register_tag
315  *
316  * Description :  Registers a client-specific-tag and its description
317  *
318  * Parameters  :
319  *          1  :  config: The tag list
320  *          2  :  name:  The name of the client-specific-tag
321  *          3  :  description: The human-readable description for the tag
322  *
323  * Returns     :  N/A
324  *
325  *********************************************************************/
326 static void register_tag(struct client_tag_spec *tag_list,
327    const char *name, const char *description)
328 {
329    struct client_tag_spec *new_tag;
330    struct client_tag_spec *last_tag;
331
332    last_tag = tag_list;
333    while (last_tag->next != NULL)
334    {
335       last_tag = last_tag->next;
336    }
337    if (last_tag->name == NULL)
338    {
339       /* First entry */
340       new_tag = last_tag;
341    }
342    else
343    {
344       new_tag = zalloc_or_die(sizeof(struct client_tag_spec));
345    }
346    new_tag->name = strdup_or_die(name);
347    new_tag->description = strdup_or_die(description);
348    if (new_tag != last_tag)
349    {
350       last_tag->next = new_tag;
351    }
352 }
353
354
355 /*********************************************************************
356  *
357  * Function    :  free_client_specific_tags
358  *
359  * Description :  Frees client-specific tags and their descriptions
360  *
361  * Parameters  :
362  *          1  :  tag_list: The tag list to free
363  *
364  * Returns     :  N/A
365  *
366  *********************************************************************/
367 static void free_client_specific_tags(struct client_tag_spec *tag_list)
368 {
369    struct client_tag_spec *this_tag;
370    struct client_tag_spec *next_tag;
371
372    next_tag = tag_list;
373    do
374    {
375       this_tag = next_tag;
376       next_tag = next_tag->next;
377
378       freez(this_tag->name);
379       freez(this_tag->description);
380
381       if (this_tag != tag_list)
382       {
383          freez(this_tag);
384       }
385    } while (next_tag != NULL);
386 }
387 #endif /* def FEATURE_CLIENT_TAGS */
388
389
390 /*********************************************************************
391  *
392  * Function    :  parse_numeric_value
393  *
394  * Description :  Parse the value of a directive that can only have
395  *                a single numeric value. Terminates with a fatal error
396  *                if the value is NULL or not numeric.
397  *
398  * Parameters  :
399  *          1  :  name:  The name of the directive. Used for log messages.
400  *          2  :  value: The value to parse
401  *
402  *
403  * Returns     :  The numerical value as integer
404  *
405  *********************************************************************/
406 static int parse_numeric_value(const char *name, const char *value)
407 {
408    int number;
409    char *endptr;
410
411    assert(name != NULL);
412    assert(value != NULL);
413
414    if ((value == NULL) || (*value == '\0'))
415    {
416       log_error(LOG_LEVEL_FATAL, "Directive %s used without argument", name);
417    }
418
419    number = (int)strtol(value, &endptr, 0);
420    if (*endptr != '\0')
421    {
422       log_error(LOG_LEVEL_FATAL,
423          "Directive '%s' used with non-numerical value: '%s'", name, value);
424    }
425
426    return number;
427
428 }
429
430
431 /*********************************************************************
432  *
433  * Function    :  parse_toggle_value
434  *
435  * Description :  Parse the value of a directive that can only be
436  *                enabled or disabled. Terminates with a fatal error
437  *                if the value is NULL or something other than 0 or 1.
438  *
439  * Parameters  :
440  *          1  :  name:  The name of the directive. Used for log messages.
441  *          2  :  value: The value to parse
442  *
443  *
444  * Returns     :  The numerical toggle state
445  *
446  *********************************************************************/
447 static int parse_toggle_state(const char *name, const char *value)
448 {
449    int toggle_state;
450    assert(name != NULL);
451    assert(value != NULL);
452
453    if ((value == NULL) || (*value == '\0'))
454    {
455       log_error(LOG_LEVEL_FATAL, "Directive %s used without argument", name);
456    }
457
458    toggle_state = atoi(value);
459
460    /*
461     * Also check the length as atoi() doesn't mind
462     * garbage after a valid integer, but we do.
463     */
464    if (((toggle_state != 0) && (toggle_state != 1)) || (strlen(value) != 1))
465    {
466       log_error(LOG_LEVEL_FATAL,
467          "Directive %s used with invalid argument '%s'. Use either '0' or '1'.",
468          name, value);
469    }
470
471    return toggle_state;
472
473 }
474
475
476 /*********************************************************************
477  *
478  * Function    :  parse_client_header_order
479  *
480  * Description :  Parse the value of the header-order directive
481  *
482  * Parameters  :
483  *          1  :  ordered_header_list:  List to insert the ordered
484  *                                      headers into.
485  *          2  :  ordered_headers:  The ordered header names separated
486  *                                  by spaces or tabs.
487  *
488  *
489  * Returns     :  N/A
490  *
491  *********************************************************************/
492 static void parse_client_header_order(struct list *ordered_header_list, const char *ordered_headers)
493 {
494    char *original_headers_copy;
495    char **vector;
496    size_t max_segments;
497    int number_of_headers;
498    int i;
499
500    assert(ordered_header_list != NULL);
501    assert(ordered_headers != NULL);
502
503    if (ordered_headers == NULL)
504    {
505       log_error(LOG_LEVEL_FATAL, "header-order used without argument");
506    }
507
508    /*
509     * XXX: This estimate is guaranteed to be high enough as we
510     *      let ssplit() ignore empty fields, but also a bit wasteful.
511     *      The same hack is used in get_last_url() so it looks like
512     *      a real solution is needed.
513     */
514    max_segments = strlen(ordered_headers) / 2;
515    if (max_segments == 0)
516    {
517       max_segments = 1;
518    }
519    vector = malloc_or_die(max_segments * sizeof(char *));
520
521    original_headers_copy = strdup_or_die(ordered_headers);
522
523    number_of_headers = ssplit(original_headers_copy, "\t ", vector, max_segments);
524    if (number_of_headers == -1)
525    {
526       log_error(LOG_LEVEL_FATAL, "Failed to split ordered headers");
527    }
528
529    for (i = 0; i < number_of_headers; i++)
530    {
531       if (JB_ERR_OK != enlist(ordered_header_list, vector[i]))
532       {
533          log_error(LOG_LEVEL_FATAL,
534             "Failed to enlist ordered header: %s", vector[i]);
535       }
536    }
537
538    freez(vector);
539    freez(original_headers_copy);
540
541    return;
542
543 }
544
545
546 /*********************************************************************
547  *
548  * Function    :  load_config
549  *
550  * Description :  Load the config file and all parameters.
551  *
552  *                XXX: more than thousand lines long
553  *                and thus in serious need of refactoring.
554  *
555  * Parameters  :  None
556  *
557  * Returns     :  The configuration_spec, or NULL on error.
558  *
559  *********************************************************************/
560 struct configuration_spec * load_config(void)
561 {
562    char *buf = NULL;
563    char *p, *q;
564    FILE *configfp = NULL;
565    struct configuration_spec * config = NULL;
566    struct client_state * fake_csp;
567    struct file_list *fs;
568    unsigned long linenum = 0;
569    int i;
570    char *logfile = NULL;
571
572    if (!check_file_changed(current_configfile, configfile, &fs))
573    {
574       /* No need to load */
575       return ((struct configuration_spec *)current_configfile->f);
576    }
577    if (NULL == fs)
578    {
579       log_error(LOG_LEVEL_FATAL,
580          "can't check configuration file '%s':  %E", configfile);
581       return NULL;
582    }
583
584    if (NULL != current_configfile)
585    {
586       log_error(LOG_LEVEL_INFO, "Reloading configuration file '%s'", configfile);
587    }
588
589 #ifdef FEATURE_TOGGLE
590    global_toggle_state = 1;
591 #endif /* def FEATURE_TOGGLE */
592
593    fs->f = config = zalloc_or_die(sizeof(*config));
594
595    /*
596     * This is backwards from how it's usually done.
597     * Following the usual pattern, "fs" would be stored in a member
598     * variable in "csp", and then we'd access "config" from "fs->f",
599     * using a cast.  However, "config" is used so often that a
600     * cast each time would be very ugly, and the extra indirection
601     * would waste CPU cycles.  Therefore we store "config" in
602     * "csp->config", and "fs" in "csp->config->config_file_list".
603     */
604    config->config_file_list = fs;
605
606    /*
607     * Set to defaults
608     */
609    config->multi_threaded            = 1;
610    config->buffer_limit              = 4096 * 1024;
611    config->receive_buffer_size       = BUFFER_SIZE;
612    config->usermanual                = strdup_or_die(USER_MANUAL_URL);
613    config->proxy_args                = strdup_or_die("");
614    config->forwarded_connect_retries = 0;
615 #ifdef FEATURE_CLIENT_TAGS
616    config->client_tag_lifetime       = 60;
617 #endif
618    config->trust_x_forwarded_for     = 0;
619 #if defined(FEATURE_ACCEPT_FILTER) && defined(SO_ACCEPTFILTER)
620    config->enable_accept_filter      = 0;
621 #endif
622    config->listen_backlog            = DEFAULT_LISTEN_BACKLOG;
623    config->trusted_cgi_referrer      = NULL;
624    /*
625     * 128 client sockets ought to be enough for everybody who can't
626     * be bothered to read the documentation to figure out how to
627     * increase the limit.
628     */
629    config->max_client_connections    = 128;
630    config->socket_timeout            = 300; /* XXX: Should be a macro. */
631 #ifdef FEATURE_CONNECTION_KEEP_ALIVE
632    config->default_server_timeout    = 0;
633    config->keep_alive_timeout        = DEFAULT_KEEP_ALIVE_TIMEOUT;
634    config->feature_flags            &= ~RUNTIME_FEATURE_CONNECTION_KEEP_ALIVE;
635    config->feature_flags            &= ~RUNTIME_FEATURE_CONNECTION_SHARING;
636 #endif
637    config->feature_flags            &= ~RUNTIME_FEATURE_CGI_TOGGLE;
638    config->feature_flags            &= ~RUNTIME_FEATURE_SPLIT_LARGE_FORMS;
639    config->feature_flags            &= ~RUNTIME_FEATURE_ACCEPT_INTERCEPTED_REQUESTS;
640    config->feature_flags            &= ~RUNTIME_FEATURE_EMPTY_DOC_RETURNS_OK;
641    config->feature_flags            &= ~RUNTIME_FEATURE_FORWARD_PROXY_AUTHENTICATION_HEADERS;
642 #ifdef FEATURE_COMPRESSION
643    config->feature_flags            &= ~RUNTIME_FEATURE_COMPRESSION;
644    /*
645     * XXX: Run some benchmarks to see if there are better default values.
646     */
647    config->compression_level         = 1;
648 #endif
649    config->feature_flags            &= ~RUNTIME_FEATURE_TOLERATE_PIPELINING;
650
651    configfp = fopen(configfile, "r");
652    if (NULL == configfp)
653    {
654       log_error(LOG_LEVEL_FATAL,
655          "can't open configuration file '%s':  %E", configfile);
656       /* Never get here - LOG_LEVEL_FATAL causes program exit */
657    }
658
659    while (read_config_line(configfp, &linenum, &buf) != NULL)
660    {
661       char cmd[BUFFER_SIZE];
662       char arg[BUFFER_SIZE];
663       char tmp[BUFFER_SIZE];
664 #ifdef FEATURE_ACL
665       struct access_control_list *cur_acl;
666 #endif /* def FEATURE_ACL */
667       struct forward_spec *cur_fwd;
668       int vec_count;
669       char *vec[3];
670       unsigned int directive_hash;
671
672       strlcpy(tmp, buf, sizeof(tmp));
673
674       /* Copy command (i.e. up to space or tab) into cmd */
675       p = buf;
676       q = cmd;
677       while (*p && (*p != ' ') && (*p != '\t'))
678       {
679          *q++ = *p++;
680       }
681       *q = '\0';
682
683       /* Skip over the whitespace in buf */
684       while (*p && ((*p == ' ') || (*p == '\t')))
685       {
686          p++;
687       }
688
689       /* Copy the argument into arg */
690       if (strlcpy(arg, p, sizeof(arg)) >= sizeof(arg))
691       {
692          log_error(LOG_LEVEL_FATAL, "Config line too long: %s", buf);
693       }
694
695       /* Should never happen, but check this anyway */
696       if (*cmd == '\0')
697       {
698          freez(buf);
699          continue;
700       }
701
702       /* Make sure the command field is lower case */
703       for (p = cmd; *p; p++)
704       {
705          if (privoxy_isupper(*p))
706          {
707             *p = (char)privoxy_tolower(*p);
708          }
709       }
710
711       directive_hash = hash_string(cmd);
712       switch (directive_hash)
713       {
714 /* *************************************************************************
715  * actionsfile actions-file-name
716  * In confdir by default
717  * *************************************************************************/
718          case hash_actions_file :
719             i = 0;
720             while ((i < MAX_AF_FILES) && (NULL != config->actions_file[i]))
721             {
722                i++;
723             }
724
725             if (i >= MAX_AF_FILES)
726             {
727                log_error(LOG_LEVEL_FATAL, "Too many 'actionsfile' directives in config file - limit is %d.\n"
728                   "(You can increase this limit by changing MAX_AF_FILES in project.h and recompiling).",
729                   MAX_AF_FILES);
730             }
731             config->actions_file_short[i] = strdup_or_die(arg);
732             config->actions_file[i] = make_path(config->confdir, arg);
733
734             break;
735 /* *************************************************************************
736  * accept-intercepted-requests
737  * *************************************************************************/
738          case hash_accept_intercepted_requests:
739             if (parse_toggle_state(cmd, arg) == 1)
740             {
741                config->feature_flags |= RUNTIME_FEATURE_ACCEPT_INTERCEPTED_REQUESTS;
742             }
743             else
744             {
745                config->feature_flags &= ~RUNTIME_FEATURE_ACCEPT_INTERCEPTED_REQUESTS;
746             }
747             break;
748
749 /* *************************************************************************
750  * admin-address email-address
751  * *************************************************************************/
752          case hash_admin_address :
753             freez(config->admin_address);
754             config->admin_address = strdup_or_die(arg);
755             break;
756
757 /* *************************************************************************
758  * allow-cgi-request-crunching
759  * *************************************************************************/
760          case hash_allow_cgi_request_crunching:
761             if (parse_toggle_state(cmd, arg) == 1)
762             {
763                config->feature_flags |= RUNTIME_FEATURE_CGI_CRUNCHING;
764             }
765             else
766             {
767                config->feature_flags &= ~RUNTIME_FEATURE_CGI_CRUNCHING;
768             }
769             break;
770
771 /* *************************************************************************
772  * buffer-limit n
773  * *************************************************************************/
774          case hash_buffer_limit :
775             config->buffer_limit = (size_t)(1024 * parse_numeric_value(cmd, arg));
776             break;
777
778 /* *************************************************************************
779  * client-header-order header-1 header-2 ... header-n
780  * *************************************************************************/
781          case hash_client_header_order:
782             list_remove_all(config->ordered_client_headers);
783             parse_client_header_order(config->ordered_client_headers, arg);
784             break;
785
786 /* *************************************************************************
787  * client-specific-tag tag-name description
788  * *************************************************************************/
789 #ifdef FEATURE_CLIENT_TAGS
790          case hash_client_specific_tag:
791             {
792                char *name;
793                char *description;
794
795                name = arg;
796                description = strstr(arg, " ");
797                if (description == NULL)
798                {
799                   log_error(LOG_LEVEL_FATAL,
800                      "client-specific-tag '%s' lacks a description.", name);
801                }
802                *description = '\0';
803                /*
804                 * The length is limited because we don't want truncated
805                 * HTML caused by the cgi interface using static buffer
806                 * sizes.
807                 */
808                if (strlen(name) > CLIENT_TAG_LENGTH_MAX)
809                {
810                   log_error(LOG_LEVEL_FATAL,
811                      "client-specific-tag '%s' is longer than %d characters.",
812                      name, CLIENT_TAG_LENGTH_MAX);
813                }
814                description++;
815                register_tag(config->client_tags, name, description);
816             }
817             break;
818 #endif /* def FEATURE_CLIENT_TAGS */
819
820 /* *************************************************************************
821  * client-tag-lifetime ttl
822  * *************************************************************************/
823 #ifdef FEATURE_CLIENT_TAGS
824          case hash_client_tag_lifetime:
825          {
826             int ttl = parse_numeric_value(cmd, arg);
827             if (0 <= ttl)
828             {
829                config->client_tag_lifetime = (unsigned)ttl;
830             }
831             else
832             {
833                log_error(LOG_LEVEL_FATAL,
834                   "client-tag-lifetime can't be negative.");
835             }
836             break;
837          }
838 #endif /* def FEATURE_CLIENT_TAGS */
839
840 /* *************************************************************************
841  * confdir directory-name
842  * *************************************************************************/
843          case hash_confdir :
844             freez(config->confdir);
845             config->confdir = make_path(NULL, arg);
846             break;
847
848 /* *************************************************************************
849  * compression-level 0-9
850  * *************************************************************************/
851 #ifdef FEATURE_COMPRESSION
852          case hash_compression_level :
853          {
854             int compression_level = parse_numeric_value(cmd, arg);
855             if (-1 <= compression_level && compression_level <= 9)
856             {
857                config->compression_level = compression_level;
858             }
859             else
860             {
861                log_error(LOG_LEVEL_FATAL,
862                   "Invalid compression-level value: %s", arg);
863             }
864             break;
865          }
866 #endif
867
868 /* *************************************************************************
869  * connection-sharing (0|1)
870  * *************************************************************************/
871 #ifdef FEATURE_CONNECTION_SHARING
872          case hash_connection_sharing :
873             if (parse_toggle_state(cmd, arg) == 1)
874             {
875                config->feature_flags |= RUNTIME_FEATURE_CONNECTION_SHARING;
876             }
877             else
878             {
879                config->feature_flags &= ~RUNTIME_FEATURE_CONNECTION_SHARING;
880             }
881             break;
882 #endif
883
884 /* *************************************************************************
885  * debug n
886  * Specifies debug level, multiple values are ORed together.
887  * *************************************************************************/
888          case hash_debug :
889             config->debug |= parse_numeric_value(cmd, arg);
890             break;
891
892 /* *************************************************************************
893  * default-server-timeout timeout
894  * *************************************************************************/
895 #ifdef FEATURE_CONNECTION_KEEP_ALIVE
896          case hash_default_server_timeout :
897          {
898             int timeout = parse_numeric_value(cmd, arg);
899             if (0 <= timeout)
900             {
901                config->default_server_timeout = (unsigned int)timeout;
902             }
903             else
904             {
905                log_error(LOG_LEVEL_FATAL,
906                   "Invalid default-server-timeout value: %s", arg);
907             }
908             break;
909          }
910 #endif
911
912 /* *************************************************************************
913  * deny-access source-ip[/significant-bits] [dest-ip[/significant-bits]]
914  * *************************************************************************/
915 #ifdef FEATURE_ACL
916          case hash_deny_access:
917             strlcpy(tmp, arg, sizeof(tmp));
918             vec_count = ssplit(tmp, " \t", vec, SZ(vec));
919
920             if ((vec_count != 1) && (vec_count != 2))
921             {
922                log_error(LOG_LEVEL_ERROR, "Wrong number of parameters for "
923                      "deny-access directive in configuration file.");
924                string_append(&config->proxy_args,
925                   "<br>\nWARNING: Wrong number of parameters for "
926                   "deny-access directive in configuration file.<br><br>\n");
927                break;
928             }
929
930             /* allocate a new node */
931             cur_acl = zalloc_or_die(sizeof(*cur_acl));
932             cur_acl->action = ACL_DENY;
933
934             if (acl_addr(vec[0], cur_acl->src) < 0)
935             {
936                log_error(LOG_LEVEL_ERROR, "Invalid source address, port or netmask "
937                   "for deny-access directive in configuration file: \"%s\"", vec[0]);
938                string_append(&config->proxy_args,
939                   "<br>\nWARNING: Invalid source address, port or netmask "
940                   "for deny-access directive in configuration file: \"");
941                string_append(&config->proxy_args,
942                   vec[0]);
943                string_append(&config->proxy_args,
944                   "\"<br><br>\n");
945                freez(cur_acl);
946                break;
947             }
948             if (vec_count == 2)
949             {
950                if (acl_addr(vec[1], cur_acl->dst) < 0)
951                {
952                   log_error(LOG_LEVEL_ERROR, "Invalid destination address, port or netmask "
953                      "for deny-access directive in configuration file: \"%s\"", vec[1]);
954                   string_append(&config->proxy_args,
955                      "<br>\nWARNING: Invalid destination address, port or netmask "
956                      "for deny-access directive in configuration file: \"");
957                   string_append(&config->proxy_args,
958                      vec[1]);
959                   string_append(&config->proxy_args,
960                      "\"<br><br>\n");
961                   freez(cur_acl);
962                   break;
963                }
964             }
965 #ifdef HAVE_RFC2553
966             else
967             {
968                cur_acl->wildcard_dst = 1;
969             }
970 #endif /* def HAVE_RFC2553 */
971
972             /*
973              * Add it to the list.  Note we reverse the list to get the
974              * behaviour the user expects.  With both the ACL and
975              * actions file, the last match wins.  However, the internal
976              * implementations are different:  The actions file is stored
977              * in the same order as the file, and scanned completely.
978              * With the ACL, we reverse the order as we load it, then
979              * when we scan it we stop as soon as we get a match.
980              */
981             cur_acl->next  = config->acl;
982             config->acl = cur_acl;
983
984             break;
985 #endif /* def FEATURE_ACL */
986
987 #if defined(FEATURE_ACCEPT_FILTER) && defined(SO_ACCEPTFILTER)
988 /* *************************************************************************
989  * enable-accept-filter 0|1
990  * *************************************************************************/
991          case hash_enable_accept_filter :
992             config->enable_accept_filter = parse_toggle_state(cmd, arg);
993             break;
994 #endif /* defined(FEATURE_ACCEPT_FILTER) && defined(SO_ACCEPTFILTER) */
995
996 /* *************************************************************************
997  * enable-edit-actions 0|1
998  * *************************************************************************/
999 #ifdef FEATURE_CGI_EDIT_ACTIONS
1000          case hash_enable_edit_actions:
1001             if (parse_toggle_state(cmd, arg) == 1)
1002             {
1003                config->feature_flags |= RUNTIME_FEATURE_CGI_EDIT_ACTIONS;
1004             }
1005             else
1006             {
1007                config->feature_flags &= ~RUNTIME_FEATURE_CGI_EDIT_ACTIONS;
1008             }
1009             break;
1010 #endif /* def FEATURE_CGI_EDIT_ACTIONS */
1011
1012 /* *************************************************************************
1013  * enable-compression 0|1
1014  * *************************************************************************/
1015 #ifdef FEATURE_COMPRESSION
1016          case hash_enable_compression:
1017             if (parse_toggle_state(cmd, arg) == 1)
1018             {
1019                config->feature_flags |= RUNTIME_FEATURE_COMPRESSION;
1020             }
1021             else
1022             {
1023                config->feature_flags &= ~RUNTIME_FEATURE_COMPRESSION;
1024             }
1025             break;
1026 #endif /* def FEATURE_COMPRESSION */
1027
1028 /* *************************************************************************
1029  * enable-proxy-authentication-forwarding 0|1
1030  * *************************************************************************/
1031          case hash_enable_proxy_authentication_forwarding:
1032             if (parse_toggle_state(cmd, arg) == 1)
1033             {
1034                config->feature_flags |= RUNTIME_FEATURE_FORWARD_PROXY_AUTHENTICATION_HEADERS;
1035             }
1036             else
1037             {
1038                config->feature_flags &= ~RUNTIME_FEATURE_FORWARD_PROXY_AUTHENTICATION_HEADERS;
1039             }
1040             break;
1041
1042 /* *************************************************************************
1043  * enable-remote-toggle 0|1
1044  * *************************************************************************/
1045 #ifdef FEATURE_TOGGLE
1046          case hash_enable_remote_toggle:
1047             if (parse_toggle_state(cmd, arg) == 1)
1048             {
1049                config->feature_flags |= RUNTIME_FEATURE_CGI_TOGGLE;
1050             }
1051             else
1052             {
1053                config->feature_flags &= ~RUNTIME_FEATURE_CGI_TOGGLE;
1054             }
1055             break;
1056 #endif /* def FEATURE_TOGGLE */
1057
1058 /* *************************************************************************
1059  * enable-remote-http-toggle 0|1
1060  * *************************************************************************/
1061          case hash_enable_remote_http_toggle:
1062             if (parse_toggle_state(cmd, arg) == 1)
1063             {
1064                config->feature_flags |= RUNTIME_FEATURE_HTTP_TOGGLE;
1065             }
1066             else
1067             {
1068                config->feature_flags &= ~RUNTIME_FEATURE_HTTP_TOGGLE;
1069             }
1070             break;
1071
1072 /* *************************************************************************
1073  * enforce-blocks 0|1
1074  * *************************************************************************/
1075          case hash_enforce_blocks:
1076 #ifdef FEATURE_FORCE_LOAD
1077             if (parse_toggle_state(cmd, arg) == 1)
1078             {
1079                config->feature_flags |= RUNTIME_FEATURE_ENFORCE_BLOCKS;
1080             }
1081             else
1082             {
1083                config->feature_flags &= ~RUNTIME_FEATURE_ENFORCE_BLOCKS;
1084             }
1085 #else
1086             log_error(LOG_LEVEL_ERROR, "Ignoring directive 'enforce-blocks'. "
1087                "FEATURE_FORCE_LOAD is disabled, blocks will always be enforced.");
1088 #endif /* def FEATURE_FORCE_LOAD */
1089             break;
1090
1091 /* *************************************************************************
1092  * filterfile file-name
1093  * In confdir by default.
1094  * *************************************************************************/
1095          case hash_filterfile :
1096             i = 0;
1097             while ((i < MAX_AF_FILES) && (NULL != config->re_filterfile[i]))
1098             {
1099                i++;
1100             }
1101
1102             if (i >= MAX_AF_FILES)
1103             {
1104                log_error(LOG_LEVEL_FATAL, "Too many 'filterfile' directives in config file - limit is %d.\n"
1105                   "(You can increase this limit by changing MAX_AF_FILES in project.h and recompiling).",
1106                   MAX_AF_FILES);
1107             }
1108             config->re_filterfile_short[i] = strdup_or_die(arg);
1109             config->re_filterfile[i] = make_path(config->confdir, arg);
1110
1111             break;
1112
1113 /* *************************************************************************
1114  * forward url-pattern (.|http-proxy-host[:port])
1115  * *************************************************************************/
1116          case hash_forward:
1117             strlcpy(tmp, arg, sizeof(tmp));
1118             vec_count = ssplit(tmp, " \t", vec, SZ(vec));
1119
1120             if (vec_count != 2)
1121             {
1122                log_error(LOG_LEVEL_ERROR, "Wrong number of parameters for forward "
1123                      "directive in configuration file.");
1124                string_append(&config->proxy_args,
1125                   "<br>\nWARNING: Wrong number of parameters for "
1126                   "forward directive in configuration file.");
1127                break;
1128             }
1129
1130             /* allocate a new node */
1131             cur_fwd = zalloc_or_die(sizeof(*cur_fwd));
1132             cur_fwd->type = SOCKS_NONE;
1133
1134             /* Save the URL pattern */
1135             if (create_pattern_spec(cur_fwd->url, vec[0]))
1136             {
1137                log_error(LOG_LEVEL_ERROR, "Bad URL specifier for forward "
1138                      "directive in configuration file.");
1139                string_append(&config->proxy_args,
1140                   "<br>\nWARNING: Bad URL specifier for "
1141                   "forward directive in configuration file.");
1142                freez(cur_fwd);
1143                break;
1144             }
1145
1146             /* Parse the parent HTTP proxy host:port */
1147             p = vec[1];
1148
1149             if (strcmp(p, ".") != 0)
1150             {
1151                cur_fwd->forward_port = 8000;
1152                parse_forwarder_address(p, &cur_fwd->forward_host,
1153                   &cur_fwd->forward_port);
1154             }
1155
1156             /* Add to list. */
1157             cur_fwd->next = config->forward;
1158             config->forward = cur_fwd;
1159
1160             break;
1161
1162 /* *************************************************************************
1163  * forward-socks4 url-pattern socks-proxy[:port] (.|http-proxy[:port])
1164  * *************************************************************************/
1165          case hash_forward_socks4:
1166             strlcpy(tmp, arg, sizeof(tmp));
1167             vec_count = ssplit(tmp, " \t", vec, SZ(vec));
1168
1169             if (vec_count != 3)
1170             {
1171                log_error(LOG_LEVEL_ERROR, "Wrong number of parameters for "
1172                      "forward-socks4 directive in configuration file.");
1173                string_append(&config->proxy_args,
1174                   "<br>\nWARNING: Wrong number of parameters for "
1175                   "forward-socks4 directive in configuration file.");
1176                break;
1177             }
1178
1179             /* allocate a new node */
1180             cur_fwd = zalloc_or_die(sizeof(*cur_fwd));
1181             cur_fwd->type = SOCKS_4;
1182
1183             /* Save the URL pattern */
1184             if (create_pattern_spec(cur_fwd->url, vec[0]))
1185             {
1186                log_error(LOG_LEVEL_ERROR, "Bad URL specifier for forward-socks4 "
1187                      "directive in configuration file.");
1188                string_append(&config->proxy_args,
1189                   "<br>\nWARNING: Bad URL specifier for "
1190                   "forward-socks4 directive in configuration file.");
1191                freez(cur_fwd);
1192                break;
1193             }
1194
1195             /* Parse the SOCKS proxy host[:port] */
1196             p = vec[1];
1197
1198             /* XXX: This check looks like a bug. */
1199             if (strcmp(p, ".") != 0)
1200             {
1201                cur_fwd->gateway_port = 1080;
1202                parse_forwarder_address(p, &cur_fwd->gateway_host,
1203                   &cur_fwd->gateway_port);
1204             }
1205
1206             /* Parse the parent HTTP proxy host[:port] */
1207             p = vec[2];
1208
1209             if (strcmp(p, ".") != 0)
1210             {
1211                cur_fwd->forward_port = 8000;
1212                parse_forwarder_address(p, &cur_fwd->forward_host,
1213                   &cur_fwd->forward_port);
1214             }
1215
1216             /* Add to list. */
1217             cur_fwd->next = config->forward;
1218             config->forward = cur_fwd;
1219
1220             break;
1221
1222 /* *************************************************************************
1223  * forward-socks4a url-pattern socks-proxy[:port] (.|http-proxy[:port])
1224  * *************************************************************************/
1225          case hash_forward_socks4a:
1226          case hash_forward_socks5:
1227          case hash_forward_socks5t:
1228             strlcpy(tmp, arg, sizeof(tmp));
1229             vec_count = ssplit(tmp, " \t", vec, SZ(vec));
1230
1231             if (vec_count != 3)
1232             {
1233                log_error(LOG_LEVEL_ERROR,
1234                   "Wrong number of parameters for %s in configuration file.",
1235                   cmd);
1236                string_append(&config->proxy_args,
1237                   "<br>\nWARNING: Wrong number of parameters for ");
1238                string_append(&config->proxy_args, cmd);
1239                string_append(&config->proxy_args,
1240                   "directive in configuration file.");
1241                break;
1242             }
1243
1244             /* allocate a new node */
1245             cur_fwd = zalloc_or_die(sizeof(*cur_fwd));
1246
1247             if (directive_hash == hash_forward_socks4a)
1248             {
1249                cur_fwd->type = SOCKS_4A;
1250             }
1251             else if (directive_hash == hash_forward_socks5)
1252             {
1253                cur_fwd->type = SOCKS_5;
1254             }
1255             else
1256             {
1257                assert(directive_hash == hash_forward_socks5t);
1258                cur_fwd->type = SOCKS_5T;
1259             }
1260
1261             /* Save the URL pattern */
1262             if (create_pattern_spec(cur_fwd->url, vec[0]))
1263             {
1264                log_error(LOG_LEVEL_ERROR,
1265                   "Bad URL specifier for %s in configuration file.",
1266                   cmd);
1267                string_append(&config->proxy_args,
1268                   "<br>\nWARNING: Bad URL specifier for ");
1269                string_append(&config->proxy_args, cmd);
1270                string_append(&config->proxy_args,
1271                   "directive in configuration file.");
1272                freez(cur_fwd);
1273                break;
1274             }
1275
1276             /* Parse the SOCKS proxy host[:port] */
1277             p = vec[1];
1278
1279             cur_fwd->gateway_port = 1080;
1280             parse_forwarder_address(p, &cur_fwd->gateway_host,
1281                &cur_fwd->gateway_port);
1282
1283             /* Parse the parent HTTP proxy host[:port] */
1284             p = vec[2];
1285
1286             if (strcmp(p, ".") != 0)
1287             {
1288                cur_fwd->forward_port = 8000;
1289                parse_forwarder_address(p, &cur_fwd->forward_host,
1290                   &cur_fwd->forward_port);
1291             }
1292
1293             /* Add to list. */
1294             cur_fwd->next = config->forward;
1295             config->forward = cur_fwd;
1296
1297             break;
1298
1299 /* *************************************************************************
1300  * forwarded-connect-retries n
1301  * *************************************************************************/
1302          case hash_forwarded_connect_retries :
1303             config->forwarded_connect_retries = parse_numeric_value(cmd, arg);
1304             break;
1305
1306 /* *************************************************************************
1307  * handle-as-empty-doc-returns-ok 0|1
1308  *
1309  * Workaround for firefox hanging on blocked javascript pages.
1310  *   Block with the "+handle-as-empty-document" flag and set the
1311  *   "handle-as-empty-doc-returns-ok" run-time config flag so that
1312  *   Privoxy returns a 200/OK status instead of a 403/Forbidden status
1313  *   to the browser for blocked pages.
1314  ***************************************************************************/
1315          case hash_handle_as_empty_returns_ok:
1316             if (parse_toggle_state(cmd, arg) == 1)
1317             {
1318                config->feature_flags |= RUNTIME_FEATURE_EMPTY_DOC_RETURNS_OK;
1319             }
1320             else
1321             {
1322                config->feature_flags &= ~RUNTIME_FEATURE_EMPTY_DOC_RETURNS_OK;
1323             }
1324             break;
1325
1326 /* *************************************************************************
1327  * hostname hostname-to-show-on-cgi-pages
1328  * *************************************************************************/
1329          case hash_hostname :
1330             freez(config->hostname);
1331             config->hostname = strdup_or_die(arg);
1332             break;
1333
1334 /* *************************************************************************
1335  * keep-alive-timeout timeout
1336  * *************************************************************************/
1337 #ifdef FEATURE_CONNECTION_KEEP_ALIVE
1338          case hash_keep_alive_timeout :
1339          {
1340             int timeout = parse_numeric_value(cmd, arg);
1341             if (0 < timeout)
1342             {
1343                config->feature_flags |= RUNTIME_FEATURE_CONNECTION_KEEP_ALIVE;
1344                config->keep_alive_timeout = (unsigned int)timeout;
1345             }
1346             else
1347             {
1348                config->feature_flags &= ~RUNTIME_FEATURE_CONNECTION_KEEP_ALIVE;
1349             }
1350             break;
1351          }
1352 #endif
1353
1354 /* *************************************************************************
1355  * listen-address [ip][:port]
1356  * *************************************************************************/
1357          case hash_listen_address :
1358             i = 0;
1359             while ((i < MAX_LISTENING_SOCKETS) && (NULL != config->haddr[i]))
1360             {
1361                i++;
1362             }
1363
1364             if (i >= MAX_LISTENING_SOCKETS)
1365             {
1366                log_error(LOG_LEVEL_FATAL, "Too many 'listen-address' directives in config file - limit is %d.\n"
1367                   "(You can increase this limit by changing MAX_LISTENING_SOCKETS in project.h and recompiling).",
1368                   MAX_LISTENING_SOCKETS);
1369             }
1370             config->haddr[i] = strdup_or_die(arg);
1371             break;
1372
1373 /* *************************************************************************
1374  * listen-backlog n
1375  * *************************************************************************/
1376          case hash_listen_backlog :
1377             /*
1378              * We don't enfore an upper or lower limit because on
1379              * many platforms all values are valid and negative
1380              * number mean "use the highest value allowed".
1381              */
1382             config->listen_backlog = parse_numeric_value(cmd, arg);
1383             break;
1384
1385 /* *************************************************************************
1386  * logdir directory-name
1387  * *************************************************************************/
1388          case hash_logdir :
1389             freez(config->logdir);
1390             config->logdir = make_path(NULL, arg);
1391             break;
1392
1393 /* *************************************************************************
1394  * logfile log-file-name
1395  * In logdir by default
1396  * *************************************************************************/
1397          case hash_logfile :
1398             if (daemon_mode)
1399             {
1400                logfile = make_path(config->logdir, arg);
1401                if (NULL == logfile)
1402                {
1403                   log_error(LOG_LEVEL_FATAL, "Out of memory while creating logfile path");
1404                }
1405             }
1406             break;
1407
1408 /* *************************************************************************
1409  * max-client-connections number
1410  * *************************************************************************/
1411          case hash_max_client_connections :
1412          {
1413             int max_client_connections = parse_numeric_value(cmd, arg);
1414
1415 #if !defined(_WIN32) && !defined(HAVE_POLL)
1416             /*
1417              * Reject values below 1 for obvious reasons and values above
1418              * FD_SETSIZE/2 because Privoxy needs two sockets to serve
1419              * client connections that need forwarding.
1420              *
1421              * We ignore the fact that the first three file descriptors
1422              * are usually set to /dev/null, one is used for logging
1423              * and yet another file descriptor is required to load
1424              * config files.
1425              */
1426             if ((max_client_connections < 1) || (FD_SETSIZE/2 < max_client_connections))
1427             {
1428                log_error(LOG_LEVEL_FATAL, "max-client-connections value %d"
1429                   " is invalid. Value needs to be above 1 and below %d"
1430                   " (FD_SETSIZE/2).", max_client_connections, FD_SETSIZE/2);
1431             }
1432 #else
1433             /*
1434              * The Windows libc uses FD_SETSIZE for an array used
1435              * by select(), but has no problems with file descriptors
1436              * above the limit as long as no more than FD_SETSIZE are
1437              * passed to select().
1438              * https://msdn.microsoft.com/en-us/library/windows/desktop/ms739169%28v=vs.85%29.aspx
1439              *
1440              * On platforms were we use poll() we don't have to enforce
1441              * an upper connection limit either.
1442              *
1443              * XXX: Do OS/2 etc. belong here as well?
1444              */
1445             if (max_client_connections < 1)
1446             {
1447                log_error(LOG_LEVEL_FATAL, "max-client-connections value"
1448                   " has to be a number above 1. %d is invalid.",
1449                   max_client_connections);
1450             }
1451 #endif
1452             config->max_client_connections = max_client_connections;
1453             break;
1454          }
1455
1456 /* *************************************************************************
1457  * permit-access source-ip[/significant-bits] [dest-ip[/significant-bits]]
1458  * *************************************************************************/
1459 #ifdef FEATURE_ACL
1460          case hash_permit_access:
1461             strlcpy(tmp, arg, sizeof(tmp));
1462             vec_count = ssplit(tmp, " \t", vec, SZ(vec));
1463
1464             if ((vec_count != 1) && (vec_count != 2))
1465             {
1466                log_error(LOG_LEVEL_ERROR, "Wrong number of parameters for "
1467                      "permit-access directive in configuration file.");
1468                string_append(&config->proxy_args,
1469                   "<br>\nWARNING: Wrong number of parameters for "
1470                   "permit-access directive in configuration file.<br><br>\n");
1471
1472                break;
1473             }
1474
1475             /* allocate a new node */
1476             cur_acl = zalloc_or_die(sizeof(*cur_acl));
1477             cur_acl->action = ACL_PERMIT;
1478
1479             if (acl_addr(vec[0], cur_acl->src) < 0)
1480             {
1481                log_error(LOG_LEVEL_ERROR, "Invalid source address, port or netmask "
1482                   "for permit-access directive in configuration file: \"%s\"", vec[0]);
1483                string_append(&config->proxy_args,
1484                   "<br>\nWARNING: Invalid source address, port or netmask for "
1485                   "permit-access directive in configuration file: \"");
1486                string_append(&config->proxy_args,
1487                   vec[0]);
1488                string_append(&config->proxy_args,
1489                   "\"<br><br>\n");
1490                freez(cur_acl);
1491                break;
1492             }
1493             if (vec_count == 2)
1494             {
1495                if (acl_addr(vec[1], cur_acl->dst) < 0)
1496                {
1497                   log_error(LOG_LEVEL_ERROR, "Invalid destination address, port or netmask "
1498                      "for permit-access directive in configuration file: \"%s\"", vec[1]);
1499                   string_append(&config->proxy_args,
1500                      "<br>\nWARNING: Invalid destination address, port or netmask for "
1501                      "permit-access directive in configuration file: \"");
1502                   string_append(&config->proxy_args,
1503                      vec[1]);
1504                   string_append(&config->proxy_args,
1505                      "\"<br><br>\n");
1506                   freez(cur_acl);
1507                   break;
1508                }
1509             }
1510 #ifdef HAVE_RFC2553
1511             else
1512             {
1513                cur_acl->wildcard_dst = 1;
1514             }
1515 #endif /* def HAVE_RFC2553 */
1516
1517             /*
1518              * Add it to the list.  Note we reverse the list to get the
1519              * behaviour the user expects.  With both the ACL and
1520              * actions file, the last match wins.  However, the internal
1521              * implementations are different:  The actions file is stored
1522              * in the same order as the file, and scanned completely.
1523              * With the ACL, we reverse the order as we load it, then
1524              * when we scan it we stop as soon as we get a match.
1525              */
1526             cur_acl->next  = config->acl;
1527             config->acl = cur_acl;
1528
1529             break;
1530 #endif /* def FEATURE_ACL */
1531
1532 /* *************************************************************************
1533  * proxy-info-url url
1534  * *************************************************************************/
1535          case hash_proxy_info_url :
1536             freez(config->proxy_info_url);
1537             config->proxy_info_url = strdup_or_die(arg);
1538             break;
1539
1540
1541 /* *************************************************************************
1542  * receive-buffer-size n
1543  * *************************************************************************/
1544          case hash_receive_buffer_size :
1545             config->receive_buffer_size = (size_t)parse_numeric_value(cmd, arg);
1546             if (config->receive_buffer_size < BUFFER_SIZE)
1547             {
1548                log_error(LOG_LEVEL_INFO,
1549                   "receive-buffer-size %d seems low and may cause problems."
1550                   "Consider setting it to at least %d.",
1551                   config->receive_buffer_size, BUFFER_SIZE);
1552             }
1553             break;
1554
1555 /* *************************************************************************
1556  * single-threaded 0|1
1557  * *************************************************************************/
1558          case hash_single_threaded :
1559             config->multi_threaded =  0 == parse_toggle_state(cmd, arg);
1560             break;
1561
1562 /* *************************************************************************
1563  * socket-timeout numer_of_seconds
1564  * *************************************************************************/
1565          case hash_socket_timeout :
1566          {
1567             int socket_timeout = parse_numeric_value(cmd, arg);
1568             if (0 <= socket_timeout)
1569             {
1570                config->socket_timeout = socket_timeout;
1571             }
1572             else
1573             {
1574                log_error(LOG_LEVEL_FATAL, "Invalid socket-timeout: '%s'", arg);
1575             }
1576             break;
1577          }
1578
1579 /* *************************************************************************
1580  * split-large-cgi-forms
1581  * *************************************************************************/
1582          case hash_split_large_cgi_forms :
1583             if (parse_toggle_state(cmd, arg) == 1)
1584             {
1585                config->feature_flags |= RUNTIME_FEATURE_SPLIT_LARGE_FORMS;
1586             }
1587             else
1588             {
1589                config->feature_flags &= ~RUNTIME_FEATURE_SPLIT_LARGE_FORMS;
1590             }
1591             break;
1592
1593 /* *************************************************************************
1594  * templdir directory-name
1595  * *************************************************************************/
1596          case hash_templdir :
1597             freez(config->templdir);
1598             config->templdir = make_path(NULL, arg);
1599             break;
1600
1601 #ifdef FEATURE_EXTERNAL_FILTERS
1602 /* *************************************************************************
1603  * temporary-directory directory-name
1604  * *************************************************************************/
1605          case hash_temporary_directory :
1606             freez(config->temporary_directory);
1607             config->temporary_directory = make_path(NULL, arg);
1608             break;
1609 #endif
1610
1611 /* *************************************************************************
1612  * tolerate-pipelining (0|1)
1613  * *************************************************************************/
1614          case hash_tolerate_pipelining :
1615             if (parse_toggle_state(cmd, arg) == 1)
1616             {
1617                config->feature_flags |= RUNTIME_FEATURE_TOLERATE_PIPELINING;
1618             }
1619             else
1620             {
1621                config->feature_flags &= ~RUNTIME_FEATURE_TOLERATE_PIPELINING;
1622             }
1623             break;
1624
1625 /* *************************************************************************
1626  * toggle (0|1)
1627  * *************************************************************************/
1628 #ifdef FEATURE_TOGGLE
1629          case hash_toggle :
1630             global_toggle_state = parse_toggle_state(cmd, arg);
1631             break;
1632 #endif /* def FEATURE_TOGGLE */
1633
1634 /* *************************************************************************
1635  * trust-info-url url
1636  * *************************************************************************/
1637 #ifdef FEATURE_TRUST
1638          case hash_trust_info_url :
1639             enlist(config->trust_info, arg);
1640             break;
1641 #endif /* def FEATURE_TRUST */
1642
1643 /* *************************************************************************
1644  * trust-x-forwarded-for (0|1)
1645  * *************************************************************************/
1646          case hash_trust_x_forwarded_for :
1647             config->trust_x_forwarded_for = parse_toggle_state(cmd, arg);
1648             break;
1649
1650 /* *************************************************************************
1651  * trusted-cgi-referrer http://www.example.org/some/path.html
1652  * *************************************************************************/
1653          case hash_trusted_cgi_referrer :
1654             /*
1655              * We don't validate the specified referrer as
1656              * it's only used for string comparison.
1657              */
1658             freez(config->trusted_cgi_referrer);
1659             config->trusted_cgi_referrer = strdup_or_die(arg);
1660             break;
1661
1662 /* *************************************************************************
1663  * trustfile filename
1664  * (In confdir by default.)
1665  * *************************************************************************/
1666 #ifdef FEATURE_TRUST
1667          case hash_trustfile :
1668             freez(config->trustfile);
1669             config->trustfile = make_path(config->confdir, arg);
1670             break;
1671 #endif /* def FEATURE_TRUST */
1672
1673 /* *************************************************************************
1674  * usermanual url
1675  * *************************************************************************/
1676          case hash_usermanual :
1677             /*
1678              * XXX: If this isn't the first config directive, the
1679              * show-status page links to the website documentation
1680              * for the directives that were already parsed. Lame.
1681              */
1682             freez(config->usermanual);
1683             config->usermanual = strdup_or_die(arg);
1684             break;
1685
1686 /* *************************************************************************
1687  * Win32 Console options:
1688  * *************************************************************************/
1689
1690 /* *************************************************************************
1691  * hide-console
1692  * *************************************************************************/
1693 #ifdef _WIN_CONSOLE
1694          case hash_hide_console :
1695             hideConsole = 1;
1696             break;
1697 #endif /*def _WIN_CONSOLE*/
1698
1699
1700 /* *************************************************************************
1701  * Win32 GUI options:
1702  * *************************************************************************/
1703
1704 #if defined(_WIN32) && ! defined(_WIN_CONSOLE)
1705 /* *************************************************************************
1706  * activity-animation (0|1)
1707  * *************************************************************************/
1708          case hash_activity_animation :
1709             g_bShowActivityAnimation = parse_toggle_state(cmd, arg);
1710             break;
1711
1712 /* *************************************************************************
1713  *  close-button-minimizes (0|1)
1714  * *************************************************************************/
1715          case hash_close_button_minimizes :
1716             g_bCloseHidesWindow = parse_toggle_state(cmd, arg);
1717             break;
1718
1719 /* *************************************************************************
1720  * log-buffer-size (0|1)
1721  * *************************************************************************/
1722          case hash_log_buffer_size :
1723             g_bLimitBufferSize = parse_toggle_state(cmd, arg);
1724             break;
1725
1726 /* *************************************************************************
1727  * log-font-name fontname
1728  * *************************************************************************/
1729          case hash_log_font_name :
1730             if (strlcpy(g_szFontFaceName, arg,
1731                    sizeof(g_szFontFaceName)) >= sizeof(g_szFontFaceName))
1732             {
1733                log_error(LOG_LEVEL_FATAL,
1734                   "log-font-name argument '%s' is longer than %u characters.",
1735                   arg, sizeof(g_szFontFaceName)-1);
1736             }
1737             break;
1738
1739 /* *************************************************************************
1740  * log-font-size n
1741  * *************************************************************************/
1742          case hash_log_font_size :
1743             g_nFontSize = parse_numeric_value(cmd, arg);
1744             break;
1745
1746 /* *************************************************************************
1747  * log-highlight-messages (0|1)
1748  * *************************************************************************/
1749          case hash_log_highlight_messages :
1750             g_bHighlightMessages = parse_toggle_state(cmd, arg);
1751             break;
1752
1753 /* *************************************************************************
1754  * log-max-lines n
1755  * *************************************************************************/
1756          case hash_log_max_lines :
1757             g_nMaxBufferLines = parse_numeric_value(cmd, arg);
1758             break;
1759
1760 /* *************************************************************************
1761  * log-messages (0|1)
1762  * *************************************************************************/
1763          case hash_log_messages :
1764             g_bLogMessages = parse_toggle_state(cmd, arg);
1765             break;
1766
1767 /* *************************************************************************
1768  * show-on-task-bar (0|1)
1769  * *************************************************************************/
1770          case hash_show_on_task_bar :
1771             g_bShowOnTaskBar = parse_toggle_state(cmd, arg);
1772             break;
1773
1774 #endif /* defined(_WIN32) && ! defined(_WIN_CONSOLE) */
1775
1776
1777 /* *************************************************************************
1778  * Warnings about unsupported features
1779  * *************************************************************************/
1780 #ifndef FEATURE_ACL
1781          case hash_deny_access:
1782 #endif /* ndef FEATURE_ACL */
1783 #ifndef FEATURE_CGI_EDIT_ACTIONS
1784          case hash_enable_edit_actions:
1785 #endif /* ndef FEATURE_CGI_EDIT_ACTIONS */
1786 #ifndef FEATURE_TOGGLE
1787          case hash_enable_remote_toggle:
1788 #endif /* ndef FEATURE_TOGGLE */
1789 #ifndef FEATURE_ACL
1790          case hash_permit_access:
1791 #endif /* ndef FEATURE_ACL */
1792 #ifndef FEATURE_TOGGLE
1793          case hash_toggle :
1794 #endif /* ndef FEATURE_TOGGLE */
1795 #ifndef FEATURE_TRUST
1796          case hash_trustfile :
1797          case hash_trust_info_url :
1798 #endif /* ndef FEATURE_TRUST */
1799
1800 #ifndef _WIN_CONSOLE
1801          case hash_hide_console :
1802 #endif /* ndef _WIN_CONSOLE */
1803
1804 #if defined(_WIN_CONSOLE) || ! defined(_WIN32)
1805          case hash_activity_animation :
1806          case hash_close_button_minimizes :
1807          case hash_log_buffer_size :
1808          case hash_log_font_name :
1809          case hash_log_font_size :
1810          case hash_log_highlight_messages :
1811          case hash_log_max_lines :
1812          case hash_log_messages :
1813          case hash_show_on_task_bar :
1814 #endif /* defined(_WIN_CONSOLE) || ! defined(_WIN32) */
1815             /* These warnings are annoying - so hide them. -- Jon */
1816             /* log_error(LOG_LEVEL_INFO, "Unsupported directive \"%s\" ignored.", cmd); */
1817             break;
1818
1819 /* *************************************************************************/
1820          default :
1821 /* *************************************************************************/
1822             /*
1823              * I decided that I liked this better as a warning than an
1824              * error.  To change back to an error, just change log level
1825              * to LOG_LEVEL_FATAL.
1826              */
1827             log_error(LOG_LEVEL_ERROR, "Ignoring unrecognized directive "
1828                "'%s' (%uU) in line %lu in configuration file (%s).",
1829                buf, directive_hash, linenum, configfile);
1830             string_append(&config->proxy_args,
1831                " <strong class='warning'>Warning: Ignoring unrecognized directive:</strong>");
1832             break;
1833
1834 /* *************************************************************************/
1835       } /* end switch(hash_string(cmd)) */
1836
1837       /* Save the argument for the show-status page. */
1838       savearg(cmd, arg, config);
1839       freez(buf);
1840    } /* end while (read_config_line(...)) */
1841
1842    fclose(configfp);
1843
1844    set_debug_level(config->debug);
1845
1846    freez(config->logfile);
1847
1848    if (daemon_mode)
1849    {
1850       if (NULL != logfile)
1851       {
1852          config->logfile = logfile;
1853          init_error_log(Argv[0], config->logfile);
1854       }
1855       else
1856       {
1857          disable_logging();
1858       }
1859    }
1860
1861 #ifdef FEATURE_CONNECTION_KEEP_ALIVE
1862    if (config->default_server_timeout > config->keep_alive_timeout)
1863    {
1864       log_error(LOG_LEVEL_ERROR,
1865          "Reducing the default-server-timeout from %d to the keep-alive-timeout %d.",
1866          config->default_server_timeout, config->keep_alive_timeout);
1867       config->default_server_timeout = config->keep_alive_timeout;
1868    }
1869 #endif /* def FEATURE_CONNECTION_KEEP_ALIVE */
1870
1871 #ifdef FEATURE_CONNECTION_SHARING
1872    if (config->feature_flags & RUNTIME_FEATURE_CONNECTION_KEEP_ALIVE)
1873    {
1874       if (!config->multi_threaded)
1875       {
1876          /*
1877           * While we could use keep-alive without multiple threads
1878           * if we didn't bother with enforcing the connection timeout,
1879           * that might make Tor users sad, even though they shouldn't
1880           * enable the single-threaded option anyway.
1881           *
1882           * XXX: We could still use Proxy-Connection: keep-alive.
1883           */
1884          config->feature_flags &= ~RUNTIME_FEATURE_CONNECTION_KEEP_ALIVE;
1885          log_error(LOG_LEVEL_ERROR,
1886             "Config option single-threaded disables connection keep-alive.");
1887       }
1888    }
1889    else if ((config->feature_flags & RUNTIME_FEATURE_CONNECTION_SHARING))
1890    {
1891       log_error(LOG_LEVEL_ERROR, "Config option connection-sharing "
1892          "has no effect if keep-alive-timeout isn't set.");
1893       config->feature_flags &= ~RUNTIME_FEATURE_CONNECTION_SHARING;
1894    }
1895 #endif /* def FEATURE_CONNECTION_SHARING */
1896
1897    if (NULL == config->proxy_args)
1898    {
1899       log_error(LOG_LEVEL_FATAL, "Out of memory loading config - insufficient memory for config->proxy_args");
1900    }
1901
1902    if (config->re_filterfile[0])
1903    {
1904       add_loader(load_re_filterfiles, config);
1905    }
1906
1907    if (config->actions_file[0])
1908    {
1909       add_loader(load_action_files, config);
1910    }
1911
1912 #ifdef FEATURE_TRUST
1913    if (config->trustfile)
1914    {
1915       add_loader(load_trustfile, config);
1916    }
1917 #endif /* def FEATURE_TRUST */
1918
1919    if (NULL == config->haddr[0])
1920    {
1921       config->haddr[0] = strdup_or_die(HADDR_DEFAULT);
1922    }
1923
1924    for (i = 0; i < MAX_LISTENING_SOCKETS && NULL != config->haddr[i]; i++)
1925    {
1926       if ((*config->haddr[i] == '[')
1927          && (NULL != (p = strchr(config->haddr[i], ']')))
1928          && (p[1] == ':')
1929          && (0 < (config->hport[i] = atoi(p + 2))))
1930       {
1931          *p = '\0';
1932          memmove((void *)config->haddr[i], config->haddr[i] + 1,
1933             (size_t)(p - config->haddr[i]));
1934       }
1935       else if (NULL != (p = strchr(config->haddr[i], ':'))
1936          && (0 < (config->hport[i] = atoi(p + 1))))
1937       {
1938          *p = '\0';
1939       }
1940       else
1941       {
1942          log_error(LOG_LEVEL_FATAL, "invalid bind port spec %s", config->haddr[i]);
1943          /* Never get here - LOG_LEVEL_FATAL causes program exit */
1944       }
1945       if (*config->haddr[i] == '\0')
1946       {
1947          /*
1948           * Only the port specified. We stored it in config->hport[i]
1949           * and don't need its text representation anymore.
1950           * Use config->hport[i] == 0 to iterate listening addresses since
1951           * now.
1952           */
1953          freez(config->haddr[i]);
1954       }
1955    }
1956
1957    /*
1958     * Want to run all the loaders once now.
1959     *
1960     * Need to set up a fake csp, so they can get to the config.
1961     */
1962    fake_csp = zalloc_or_die(sizeof(*fake_csp));
1963    fake_csp->config = config;
1964
1965    if (run_loader(fake_csp))
1966    {
1967       freez(fake_csp);
1968       log_error(LOG_LEVEL_FATAL, "A loader failed while loading config file. Exiting.");
1969       /* Never get here - LOG_LEVEL_FATAL causes program exit */
1970    }
1971    freez(fake_csp);
1972
1973 /* FIXME: this is a kludge for win32 */
1974 #if defined(_WIN32) && !defined (_WIN_CONSOLE)
1975
1976    g_default_actions_file = config->actions_file[1]; /* FIXME Hope this is default.action */
1977    g_user_actions_file  = config->actions_file[2];  /* FIXME Hope this is user.action */
1978    g_default_filterfile = config->re_filterfile[0]; /* FIXME Hope this is default.filter */
1979    g_user_filterfile    = config->re_filterfile[1]; /* FIXME Hope this is user.filter */
1980
1981 #ifdef FEATURE_TRUST
1982    g_trustfile        = config->trustfile;
1983 #endif /* def FEATURE_TRUST */
1984
1985
1986 #endif /* defined(_WIN32) && !defined (_WIN_CONSOLE) */
1987 /* FIXME: end kludge */
1988
1989
1990    if (current_configfile == NULL)
1991    {
1992       config->need_bind = 1;
1993    }
1994    else
1995    {
1996       struct configuration_spec * oldcfg = (struct configuration_spec *)
1997                                            current_configfile->f;
1998       /*
1999        * Check if config->haddr[i],hport[i] == oldcfg->haddr[i],hport[i]
2000        */
2001       config->need_bind = 0;
2002
2003       for (i = 0; i < MAX_LISTENING_SOCKETS; i++)
2004       {
2005          if (config->hport[i] != oldcfg->hport[i])
2006          {
2007             config->need_bind = 1;
2008          }
2009          else if (config->haddr[i] == NULL)
2010          {
2011             if (oldcfg->haddr[i] != NULL)
2012             {
2013                config->need_bind = 1;
2014             }
2015          }
2016          else if (oldcfg->haddr[i] == NULL)
2017          {
2018             config->need_bind = 1;
2019          }
2020          else if (0 != strcmp(config->haddr[i], oldcfg->haddr[i]))
2021          {
2022             config->need_bind = 1;
2023          }
2024       }
2025
2026       current_configfile->unloader = unload_configfile;
2027    }
2028
2029    fs->next = files->next;
2030    files->next = fs;
2031
2032    current_configfile = fs;
2033
2034    return (config);
2035 }
2036
2037
2038 /*********************************************************************
2039  *
2040  * Function    :  savearg
2041  *
2042  * Description :  Called from `load_config'.  It saves each non-empty
2043  *                and non-comment line from config into
2044  *                config->proxy_args.  This is used to create the
2045  *                show-status page.  On error, frees
2046  *                config->proxy_args and sets it to NULL
2047  *
2048  * Parameters  :
2049  *          1  :  command = config setting that was found
2050  *          2  :  argument = the setting's argument (if any)
2051  *          3  :  config = Configuration to save into.
2052  *
2053  * Returns     :  N/A
2054  *
2055  *********************************************************************/
2056 static void savearg(char *command, char *argument, struct configuration_spec * config)
2057 {
2058    char * buf;
2059    char * s;
2060
2061    assert(command);
2062    assert(argument);
2063
2064    /*
2065     * Add config option name embedded in
2066     * link to its section in the user-manual
2067     */
2068    buf = strdup_or_die("\n<a href=\"");
2069    if (!strncmpic(config->usermanual, "file://", 7) ||
2070        !strncmpic(config->usermanual, "http", 4))
2071    {
2072       string_append(&buf, config->usermanual);
2073    }
2074    else
2075    {
2076       string_append(&buf, "http://" CGI_SITE_2_HOST "/user-manual/");
2077    }
2078    string_append(&buf, CONFIG_HELP_PREFIX);
2079    string_join  (&buf, string_toupper(command));
2080    string_append(&buf, "\">");
2081    string_append(&buf, command);
2082    string_append(&buf, "</a> ");
2083
2084    if (NULL == buf)
2085    {
2086       freez(config->proxy_args);
2087       return;
2088    }
2089
2090    if ((NULL != argument) && ('\0' != *argument))
2091    {
2092       s = html_encode(argument);
2093       if (NULL == s)
2094       {
2095          freez(buf);
2096          freez(config->proxy_args);
2097          return;
2098       }
2099
2100       if (strncmpic(argument, "http://", 7) == 0)
2101       {
2102          string_append(&buf, "<a href=\"");
2103          string_append(&buf, s);
2104          string_append(&buf, "\">");
2105          string_join  (&buf, s);
2106          string_append(&buf, "</a>");
2107       }
2108       else
2109       {
2110          string_join  (&buf, s);
2111       }
2112    }
2113
2114    string_append(&buf, "<br>");
2115    string_join(&config->proxy_args, buf);
2116 }
2117
2118
2119 /*
2120   Local Variables:
2121   tab-width: 3
2122   end:
2123 */