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