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