Handle hostnames with upper-case letters
[webmin.git] / apache / core.pl
1 # core.pl
2 # Defines the core module directives
3
4 # core_directives(version)
5 # Returns ar array of references to associative arrays, each containing
6 # information about some directive. The keys of each array are:
7 #  name -       The name of this directive
8 #  type -       What kind of directive this in. Possible values are
9 #               0 - Processes and limits
10 #               1 - Networking and addresses
11 #               2 - Apache Modules
12 #               3 - Log files
13 #               4 - Access control
14 #               5 - document options
15 #               6 - MIME types
16 #               7 - Error handling
17 #               8 - Users and Groups
18 #               9 - Miscellaneous
19 #               10- Aliases and redirects
20 #               11- CGI programs
21 #               12- Directory indexing
22 #               13- Proxying
23 #               14- SSL
24 #               15- Perl
25 #               16- PHP
26 #               17- Vhost aliases
27 #               18- Filters
28 #               19- Character Sets
29 #               20- Image maps
30 #  multiple -   Can this directive appear multiple times
31 #  global -     Can be used in the global server context
32 #  virtual -    Can be used in a VirtualHost section or in the global section
33 #  directory -  Can be used in a Directory section context
34 #  htaccess -   Can be used in a .htaccess file
35 sub core_directives
36 {
37 local($rv);
38 $rv = [ [ 'AccessFileName', 0, 5, 'virtual', undef, 5 ],
39         [ 'AddDefaultCharset', 0, 19, 'virtual directory htaccess', 2.0 ],
40         &can_configure_apache_modules() ? ( ) :
41                 ( [ 'ClearModuleList AddModule', 1, 2, 'global', -2.0 ] ),
42         [ 'AllowOverride', 0, 5, 'directory' ],
43         [ 'AuthName', 0, 4, 'directory htaccess', undef, 10 ],
44         [ 'AuthType', 0, 4, 'directory htaccess', undef, 8 ],
45         [ 'BindAddress Listen Port', 1, 1, 'global', -2.0, 10 ],
46         [ 'ContentDigest', 0, 5, 'virtual directory htaccess' ],
47         [ 'CoreDumpDirectory', 0, 9, 'global', '1.3-2.0' ],
48         [ 'DefaultType', 0, 6, 'virtual directory htaccess' ],
49         [ 'DocumentRoot', 0, 5, 'virtual', undef, 10 ],
50         [ 'ErrorDocument', 1, 7, 'virtual directory htaccess' ],
51         [ 'ErrorLog', 0, 3, 'virtual' ],
52         [ 'FileETag', 0, 5, 'virtual directory htaccess', 2.0 ],
53         [ 'ForceType', 0, 6, 'directory htaccess', 2.0 ],
54         [ 'Group', 0, 8, 'virtual', -2.0 ],
55         [ 'HostNameLookups', 0, 1, 'virtual directory' ],
56         [ 'IdentityCheck', 0, 1, 'virtual directory' ],
57         [ 'KeepAlive MaxKeepAliveRequests', 0, 1, 'global' ],
58         [ 'KeepAliveTimeout', 0, 1, 'global' ],
59         [ 'ListenBacklog', 0, 1, 'global', '1.2-2.0' ],
60         [ 'LockFile', 0, 9, 'global', -2.0 ],
61         [ 'LimitRequestBody', 0, 0, 'virtual directory htaccess', 1.302 ],
62         [ 'LimitRequestFields', 0, 0, 'global', 1.302 ],
63         [ 'LimitRequestFieldsize', 0, 0, 'global', 1.302 ],
64         [ 'LimitRequestLine', 0, 0, 'global', 1.302 ],
65         [ 'LimitXMLRequestBody', 0, 0, 'virtual directory htaccess', 2.0 ],
66         [ 'LogLevel', 0, 3, 'virtual', 1.3 ],
67         [ 'MaxClients', 0, 0, 'global', -2.0 ],
68         [ 'MaxRequestsPerChild', 0, 0, 'global', -2.0 ],
69         [ 'StartServers', 0, 0, 'global', -2.0 ],
70         [ 'MinSpareServers', 0, 0, 'global', -2.0 ],
71         [ 'MaxSpareServers', 0, 0, 'global', -2.0 ],
72         [ 'NameVirtualHost', 1, 1, 'global', 1.3, 5 ],
73         [ 'Options', 0, 5, 'virtual directory htaccess', undef, 3 ],
74         [ 'PidFile', 0, 9, 'global', -2.0 ],
75         [ 'require', 0, 4, 'directory htaccess', undef, 6 ],
76         [ 'RLimitCPU', 0, 0, 'virtual', 1.2 ],
77         [ 'RLimitMEM', 0, 0, 'virtual', 1.2 ],
78         [ 'RLimitNPROC', 0, 0, 'virtual', 1.2 ],
79         [ 'Satisfy', 0, 4, 'directory htaccess', 1.2, 4 ],
80         [ 'ScoreBoardFile', 0, 9, 'global', '1.2-2.0' ],
81         [ 'SendBufferSize', 0, 1, 'global', -2.0 ],
82         [ 'ServerAdmin', 0, 1, 'virtual' ],
83         $access{'names'} ? (
84                 [ 'ServerAlias', 1, 1, 'virtual virtualonly', 1.2 ],
85                 [ 'ServerName', 0, 1, 'virtual' ] ) : ( ),
86         [ 'ServerPath', 0, 5, 'virtual' ],
87         [ 'ServerType', 0, 9, 'global', -2.0 ],
88         [ 'ServerTokens', 0, 9, 'global', 1.3 ],
89         [ 'ServerSignature', 0, 5, 'virtual directory htaccess', 1.3 ],
90         [ 'SetOutputFilter', 0, 18, 'virtual directory htaccess', 2.0 ],
91         [ 'SetInputFilter', 0, 18, 'virtual directory htaccess', 2.0 ],
92         [ 'TimeOut', 0, 1, 'global' ],
93         [ 'UseCanonicalName', 0, 1, 'virtual directory', 1.3 ],
94         [ 'User', 0, 8, 'virtual', -2.0, 10 ] ];
95 return &make_directives($rv, $_[0], "core");
96 }
97
98 # core_handlers(config, version)
99 # Returns an array of all available handlers
100 sub core_handlers
101 {
102 return ();
103 }
104
105 #########################################################################
106 # Process and limit directives
107 sub edit_MaxClients
108 {
109 return (1,
110         $text{'core_maxconc'},
111         &opt_input($_[0]->{'value'}, "MaxClients", $text{'core_default'}, 4));
112 }
113 sub save_MaxClients
114 {
115 return &parse_opt("MaxClients", '^\d+$',
116                   $text{'core_emaxconc'});
117 }
118
119 sub edit_MaxKeepAliveRequests
120 {
121 return (1,
122         $text{'core_maxkeep'},
123         &opt_input($_[0]->{'value'}, "MaxKeepAliveRequests", $text{'core_default'}, 4));
124 }
125 sub save_MaxKeepAliveRequests
126 {
127 return &parse_opt("MaxKeepAliveRequests", '^\d+$',
128                   $text{'core_emaxkeep'});
129 }
130
131 sub edit_MaxRequestsPerChild
132 {
133 return (1,
134         $text{'core_maxreq'},
135         &opt_input($_[0]->{'value'}, "MaxRequestsPerChild", $text{'core_default'}, 5));
136 }
137 sub save_MaxRequestsPerChild
138 {
139 return &parse_opt("MaxRequestsPerChild", '^\d+$',
140                   $text{'core_emaxreq'});
141 }
142
143 sub edit_MinSpareServers
144 {
145 return (1,
146         $text{'core_minspare'},
147         &opt_input($_[0]->{'value'},"MinSpareServers",$text{'core_default'}, 4));
148 }
149 sub save_MinSpareServers
150 {
151 return &parse_opt("MinSpareServers", '^\d+$',
152                   $text{'core_eminspare'});
153 }
154
155 sub edit_MaxSpareServers
156 {
157 return (1,
158         $text{'core_maxspare'},
159         &opt_input($_[0]->{'value'},"MaxSpareServers",$text{'core_default'}, 4));
160 }
161 sub save_MaxSpareServers
162 {
163 return &parse_opt("MaxSpareServers", '^\d+$',
164                   $text{'core_emaxspare'});
165 }
166
167 sub edit_StartServers
168 {
169 return (1,
170         $text{'core_initial'},
171         &opt_input($_[0]->{'value'}, "StartServers", $text{'core_default'}, 4));
172 }
173 sub save_StartServers
174 {
175 return &parse_opt("StartServers", '^\d+$',
176                   $text{'core_einitial'});
177 }
178
179 sub edit_RLimitCPU
180 {
181 return &rlimit_input("RLimitCPU", $text{'core_cpulimit'}, $_[0]);
182 }
183 sub save_RLimitCPU
184 {
185 return &parse_rlimit("RLimitCPU", $text{'core_cpulimit2'});
186 }
187
188 sub edit_RLimitMEM
189 {
190 return &rlimit_input("RLimitMEM", $text{'core_memlimit'}, $_[0]);
191 }
192 sub save_RLimitMEM
193 {
194 return &parse_rlimit("RLimitMEM", $text{'core_memlimit2'});
195 }
196
197 sub edit_RLimitNPROC
198 {
199 return &rlimit_input("RLimitNPROC", $text{'core_proclimit'}, $_[0]);
200 }
201 sub save_RLimitNPROC
202 {
203 return &parse_rlimit("RLimitNPROC", $text{'core_proclimit2'});
204 }
205
206 # rlimit_input(name, desc, value)
207 sub rlimit_input
208 {
209 local(@v, $rv);
210 @v = split(/\s+/, $_[2]->{'value'});
211 $rv = sprintf "<input type=radio name=$_[0]_mode value=0 %s> $text{'core_default'}<br>\n",
212         @v ? "" : "checked";
213 $rv .= sprintf "<input type=radio name=$_[0]_mode value=1 %s>\n",
214         @v == 1 ? "checked" : "";
215 $rv .= sprintf "$text{'core_slimit'}<input name=$_[0]_soft1 size=5 value=\"%s\"><br>\n",
216         @v == 1 ? $v[0] : "";
217 $rv .= sprintf "<input type=radio name=$_[0]_mode value=2 %s>\n",
218         @v == 2 ? "checked" : "";
219 $rv .= sprintf "$text{'core_slimit'}<input name=$_[0]_soft2 size=5 value=\"%s\">\n",
220         @v == 2 ? $v[0] : "";
221 $rv .= sprintf "$text{'core_hlimit'}<input name=$_[0]_hard2 size=5 value=\"%s\"><br>\n",
222         @v == 2 ? $v[1] : "";
223 return (1, $_[1], $rv);
224 }
225
226 # parse_rlimit(name, desc)
227 sub parse_rlimit
228 {
229 if ($in{"$_[0]_mode"} == 0) { return ( [ ] ); }
230 elsif ($in{"$_[0]_mode"} == 1) {
231         $in{"$_[0]_soft1"} =~ /^(\d+|max)$/ ||
232                 &error(&text('core_eslimit', $in{"$_[0]_soft1"}, $_[1]));
233         return ( [ $in{"$_[0]_soft1"} ] );
234         }
235 elsif ($in{"$_[0]_mode"} == 2) {
236         $in{"$_[0]_soft2"} =~ /^(\d+|max)$/ ||
237                 &error(&text('core_eslimit', $in{"$_[0]_soft2"}, $_[1]));
238         $in{"$_[0]_hard2"} =~ /^(\d+|max)$/ ||
239                 &error(&text('core_ehlimit', $in{"$_[0]_hard2"}, $_[1]));
240         return ( [ $in{"$_[0]_soft2"}." ".$in{"$_[0]_hard2"} ] );
241         }
242 }
243
244
245 #########################################################################
246 # Networking and address directives
247 sub edit_BindAddress_Listen_Port
248 {
249 local($bref, $lref, $pref, @blist, @plist, $inp);
250 $bref = $_[0]; $lref = $_[1]; $pref = $_[2];
251 if (@$lref) {
252         # listen directives in use.. so BindAddress and Port are unused
253         foreach $l (@$lref) {
254                 if ($l->{'value'} =~ /^\[(\S+)\]:(\d+)$/) {
255                         # IPv6 address and port
256                         push(@blist, $1); push(@plist, $2);
257                         }
258                 elsif ($l->{'value'} =~ /^\[(\S+)\]$/) {
259                         # IPv6 address only
260                         push(@blist, $1); push(@plist, undef);
261                         }
262                 elsif ($l->{'value'} =~ /^(\S+):(\d+)$/) {
263                         # IPv4 address and port
264                         push(@blist, $1); push(@plist, $2);
265                         }
266                 elsif ($l->{'value'} =~ /^(\d+)$/) {
267                         # Port only
268                         push(@blist, "*"); push(@plist, $1);
269                         }
270                 elsif ($l->{'value'} =~ /^(\S+)$/) {
271                         # IPv4 address or hostname only
272                         push(@blist, $1); push(@plist, undef);
273                         }
274                 }
275         }
276 else {
277         # no listen directives... check for BindAddress
278         if (@$bref) { push(@blist, $bref->[@$bref-1]->{'value'}); }
279         else { push(@blist, "*"); }
280         push(@plist, undef);
281         }
282 $port = @$pref ? $pref->[@$pref-1]->{'value'} : 80;
283 if ($_[3]->{'version'} < 2.0) {
284         $inp = "<b>$text{'core_dport'}</b> <input name=Port size=6 value=\"$port\"><br>\n";
285         }
286 $inp .= "<table border>\n".
287         "<tr $tb> <td><b>$text{'core_address'}</b></td> <td><b>$text{'core_port'}</b></td> </tr>\n";
288 for($i=0; $i<@blist+1; $i++) {
289         $inp .= sprintf
290                 "<tr><td><input type=radio name=BindAddress_def_$i value=2 %s>".
291                 " $text{'core_none'} <input type=radio name=BindAddress_def_$i value=1 %s>".
292                 " $text{'core_all'} <input type=radio name=BindAddress_def_$i value=0 %s> ".
293                 "<input name=BindAddress_$i size=20 value=\"%s\"></td>",
294                 $blist[$i] ? "" : "checked",
295                 $blist[$i] eq "*" ? "checked" : "",
296                 $blist[$i] && $blist[$i] ne "*" ? "checked" : "",
297                 $blist[$i] eq "*" ? "" : $blist[$i];
298         if ($_[3]->{'version'} < 2.0) {
299                 $inp .= "<td>".&opt_input($plist[$i], "Port_$i", "$text{'core_default'}", 5)."</td>";
300                 }
301         else {
302                 $inp .= "<td><input name=Port_$i size=5 value='$plist[$i]'></td>\n";
303                 }
304         }
305 $inp .= "</table>\n";
306 return (2, $text{'core_listen'}, $inp);
307 }
308 sub save_BindAddress_Listen_Port
309 {
310 local(@blist, @plist, $bdef, $b, $p);
311
312 # build list of addresses and ports
313 for($i=0; defined($in{"Port_$i"}); $i++) {
314         $bdef = $in{"BindAddress_def_$i"}; $b = $in{"BindAddress_$i"};
315         $pdef = $in{"Port_${i}_def"}; $p = $in{"Port_$i"};
316         if ($bdef == 2) { next; }
317
318         if ($bdef) { push(@blist, "*"); }
319         elsif ($b =~ /^\S+$/ &&
320                (&to_ipaddress($b) || &to_ip6address($b))) { push(@blist, $b); }
321         else { &error(&text('core_eaddress', $b)); }
322
323         if ($pdef) { push(@plist, undef); }
324         elsif ($p =~ /^\d+$/) { push(@plist, $p); }
325         else { &error(&text('core_eport', $p)); }
326         }
327 if (!@blist) { &error($text{'core_eoneaddr'}); }
328
329 # Return directives
330 if ($_[0]->{'version'} < 2.0) {
331         # Older apaches have a port directive as well
332         $in{'Port'} =~ /^\d+$/ || &error($text{'core_edefport'});
333         if (@blist == 1 && !$plist[0]) {
334                 # Only one address, and the default port
335                 return ( $blist[0] eq "*" ? [ ] : [ $blist[0] ], [ ],
336                          [ $in{'Port'} ] );
337                 }
338         else {
339                 # More than one address, or a non-default port. Must use Listens
340                 for($i=0; $i<@blist; $i++) {
341                         if ($blist[$i] ne "*" && $plist[$i]) {
342                                 push(@l, "$blist[$i]:$plist[$i]");
343                                 }
344                         elsif ($blist[$i] ne "*") { push(@l, $blist[$i]); }
345                         elsif ($plist[$i]) { push(@l, "*:$plist[$i]"); }
346                         else { push(@l, $in{'Port'}); }
347                         }
348                 return ( [], \@l, [ $in{'Port'} ] );
349                 }
350         }
351 else {
352         # Apache 2.0 just uses Listen directives
353         for($i=0; $i<@blist; $i++) {
354                 if (&check_ip6address($blist[$i])) {
355                         $blist[$i] = "[".$blist[$i]."]";
356                         }
357                 if ($blist[$i] ne "*" && $plist[$i]) {
358                         push(@l, "$blist[$i]:$plist[$i]");
359                         }
360                 elsif ($blist[$i] ne "*") { push(@l, $blist[$i]); }
361                 else { push(@l, "*:$plist[$i]"); }
362                 }
363         return ( [], \@l );
364         }
365 }
366
367 sub edit_KeepAlive_MaxKeepAliveRequests
368 {
369 $kref = $_[0]; $mref = $_[1];
370 if ($_[2]->{'version'} >= 1.2) {
371         # two separate directives for keep-alives
372         $inp = sprintf
373                 "<input type=radio name=KeepAlive_def value=0 %s> $text{'core_none'}\n".
374                 "<input type=radio name=KeepAlive_def value=1 %s> $text{'core_default'}\n".
375                 "<input type=radio name=KeepAlive_def value=2 %s> ".
376                 "<input name=KeepAlive size=5 value=\"%s\">",
377                 $kref->{'value'} =~ /off/i ? "checked" : "",
378                 $kref->{'value'} !~ /off/i && !$mref ? "checked" : "",
379                 $mref ? "checked" : "",
380                 $mref ? $mref->{'value'} : "";
381         }
382 else {
383         # only one directive
384         $inp = sprintf
385                 "<input type=radio name=KeepAlive_def value=0 %s> $text{'core_none'}\n".
386                 "<input type=radio name=KeepAlive_def value=1 %s> $text{'core_default'}\n".
387                 "<input type=radio name=KeepAlive_def value=2 %s> ".
388                 "<input name=KeepAlive size=5 value=\"%s\">",
389                 $kref->{'value'} eq "0" ? "checked" : "",
390                 $kref ? "" : "checked" ,
391                 $kref->{'value'} ? "checked" : "",
392                 $kref->{'value'} ? $kref->{'value'} : "";
393         }
394 return (1, $text{'core_multi'}, $inp);
395 }
396 sub save_KeepAlive_MaxKeepAliveRequests
397 {
398 if ($_[0]->{'version'} >= 1.2) {
399         # two separate directives
400         if ($in{'KeepAlive_def'} == 0) { return ( [ "off" ], [ ] ); }
401         elsif ($in{'KeepAlive_def'} == 1) { return ( [ "on" ], [ ] ); }
402         elsif ($in{'KeepAlive'} !~ /^\d+$/) {
403                 &error(&text('core_ekeep', $in{'KeepAlive'}));
404                 }
405         else { return ( [ "on" ], [ $in{'KeepAlive'} ] ); }
406         }
407 else {
408         # only one directive
409         if ($in{'KeepAlive_def'} == 0) { return ( [ 0 ], [ ] ); }
410         elsif ($in{'KeepAlive_def'} == 1) { return ( [ ], [ ] ); }
411         elsif ($in{'KeepAlive'} !~ /^\d+$/) {
412                 &error(&text('core_ekeep', $in{'KeepAlive'}));
413                 }
414         else { return ( [ $in{'KeepAlive'} ], [ ] ); }
415         }
416 }
417
418 sub edit_KeepAliveTimeout
419 {
420 return (1,
421         $text{'core_keeptout'},
422         &opt_input($_[0]->{'value'}, "KeepAliveTimeout", $text{'core_default'}, 5));
423 }
424 sub save_KeepAliveTimeout
425 {
426 return &parse_opt("KeepAliveTimeout", '^\d+$',
427                   $text{'core_ekeeptout'});
428 }
429
430 sub edit_ListenBacklog
431 {
432 return (1,
433         $text{'core_lqueue'},
434         &opt_input($_[0]->{'value'}, "ListenBacklog", $text{'core_default'}, 4));
435 }
436 sub save_ListenBacklog
437 {
438 return &parse_opt("ListenBacklog", '^\d+$',
439                   $text{'core_elqueue'});
440 }
441
442 sub edit_SendBufferSize
443 {
444 return (1,
445         $text{'core_bufsize'},
446         &opt_input($_[0]->{'value'}, "SendBufferSize", $text{'core_osdefault'}, 4));
447 }
448 sub save_SendBufferSize
449 {
450 return &parse_opt("SendBufferSize", '^\d+$',
451                   $text{'core_ebufsize'});
452 }
453
454 sub edit_ServerAdmin
455 {
456 return (1, $text{'core_admin'},
457         &opt_input($_[0]->{'value'}, "ServerAdmin", $text{'core_noadmin'}, 25));
458 }
459 sub save_ServerAdmin
460 {
461 return &parse_opt("ServerAdmin");
462 }
463
464 sub edit_TimeOut
465 {
466 return (1,
467         $text{'core_rtout'},
468         &opt_input($_[0]->{'value'}, "TimeOut", $text{'core_default'}, 4));
469 }
470 sub save_TimeOut
471 {
472 return &parse_opt("TimeOut", '^\d+$',
473                   $text{'core_ertout'});
474 }
475
476 sub edit_UseCanonicalName
477 {
478 return (1, $text{'core_bhostname'},
479         &choice_input($_[0]->{'value'}, "UseCanonicalName",
480                        "", "$text{'yes'},off", "$text{'no'},on", "$text{'core_default'},"));
481 }
482 sub save_UseCanonicalName
483 {
484 return &parse_choice("UseCanonicalName", "");
485 }
486
487 sub edit_HostNameLookups
488 {
489 if ($_[1]->{'version'} >= 1.3) {
490         return (1, $text{'core_lookup'},
491                 &choice_input($_[0]->{'value'}, "HostNameLookups", "",
492                        "$text{'no'},off", "$text{'yes'},on", "$text{'core_ltwice'},double", "$text{'core_default'},"));
493         }
494 else {
495         return (1, $text{'core_lookup'},
496                 &choice_input($_[0]->{'value'}, "HostNameLookups", "",
497                               "$text{'yes'},on", "$text{'no'},off", "$text{'core_default'},"));
498         }
499 }
500 sub save_HostNameLookups
501 {
502 return &parse_choice("HostNameLookups", "");
503 }
504
505 sub edit_IdentityCheck
506 {
507 return (1, $text{'core_useauth'},
508         &choice_input($_[0]->{'value'}, "IdentityCheck", "",
509                       "$text{'yes'},on", "$text{'no'},off", "$text{'core_default'},"));
510 }
511 sub save_IdentityCheck
512 {
513 return &parse_choice("IdentityCheck", "");
514 }
515
516 sub edit_ServerAlias
517 {
518 local($a, @al);
519 foreach $a (@{$_[0]}) { push(@al, split(/\s+/, $a->{'value'})); }
520 return (1, $text{'core_altnames'},
521         sprintf "<textarea name=ServerAlias rows=3 cols=25>%s</textarea>\n",
522                 join("\n", @al) );
523 }
524 sub save_ServerAlias
525 {
526 local(@al);
527 @al = split(/\s+/, $in{'ServerAlias'});
528 if (@al) {
529         local @spal;
530         while(@al > 200) {
531                 push(@spal, join(" ", @al[0 .. 199]));
532                 @al = @al[200 .. $#al];
533                 }
534         push(@spal, join(" ", @al)) if (@al);
535         return ( \@spal );
536         }
537 else {
538         return ( [ ] );
539         }
540 }
541
542 sub edit_ServerName
543 {
544 return (1, $text{'core_hostname'},
545         &opt_input($_[0]->{'value'}, "ServerName", $text{'core_auto'}, 25));
546 }
547 sub save_ServerName
548 {
549 return &parse_opt("ServerName", '^\S+$', $text{'core_ehostname'});
550 }
551
552 sub edit_NameVirtualHost
553 {
554 local(@nv, $nv, $star);
555 foreach $nv (@{$_[0]}) {
556         if ($nv->{'value'} eq "*" && $_[1]->{'version'} >= 1.312) { $star++; }
557         elsif ($nv->{'value'} =~ /^\[(\S+)\]$/) { push(@nv, $1); }
558         else { push(@nv, $nv->{'value'}); }
559         }
560 if ($_[1]->{'version'} >= 1.312) {
561         $starui = sprintf
562           "<input type=checkbox name=NameVirtualHost_star value=1 %s> %s<br>\n",
563           $star ? "checked" : "", $text{'core_virtaddr_star'};
564         }
565 return (1, $text{'core_virtaddr'},
566         $starui.
567         "<textarea name=NameVirtualHost rows=3 cols=30>".
568         join("\n", @nv)."</textarea>");
569 }
570 sub save_NameVirtualHost
571 {
572 local(@nv, $nv, $addr);
573 @nv = split(/\s+/, $in{'NameVirtualHost'});
574 @nv = ( "*", @nv ) if ($in{'NameVirtualHost_star'});
575 foreach $nv (@nv) {
576         if ($nv =~ /^(\S+):(\d+|\*)$/) { $addr = $1; }
577         else { $addr = $nv; }
578         if (!&to_ipaddress($addr) &&
579             !&to_ip6address($addr) && $addr ne '*') {
580                 &error(&text('core_evirtaddr', $addr));
581                 }
582         if ($nv =~ /^(\S+):(\d+|\*)$/ && &check_ip6address($1)) {
583                 $nv = "[$1]:$2";
584                 }
585         elsif (&check_ip6address($nv)) {
586                 $nv = "[$nv]";
587                 }
588         }
589 if (@nv) { return ( \@nv ); }
590 else { return ( [ ] ); }
591 }
592
593 #########################################################################
594 # Document directives
595 sub edit_AccessFileName
596 {
597 return (1,
598         $text{'core_optfile'},
599         &opt_input($_[0]->{'value'}, "AccessFileName", $text{'core_default'}, 20));
600 }
601 sub save_AccessFileName
602 {
603 if ($_[0]->{'version'} < 1.3) {
604         return &parse_opt("AccessFileName", '^(\S+)$',
605                           $text{'core_eoptfile'});
606         }
607 else {
608         return &parse_opt("AccessFileName", '\S',
609                              $text{'core_enoopt'});
610         }
611 }
612
613 @AllowOverride_v = ("AuthConfig", "FileInfo", "Indexes", "Limit", "Options");
614 @AllowOverride_d = ("$text{'core_auth'}", "$text{'core_mime'}",
615                     "$text{'core_indexing'}", "$text{'core_hostacc'}",
616                     "$text{'core_diropts'}");
617 sub edit_AllowOverride
618 {
619 local($rv, @ov, %over, $rv);
620 $rv = &choice_input($_[0] ? 0 : 1, "AllowOverride_def", 1,
621                  "$text{'core_default'},1", "$text{'core_filesel'},0");
622 $rv .= "<table border><tr><td>\n";
623 if ($_[0]) { @ov = split(/\s+/, $_[0]->{'value'}); }
624 else { @ov = ("All"); }
625 foreach $ov (@ov) { $over{$ov}++; }
626 if ($over{'All'}) { foreach $ov (@AllowOverride_v) { $over{$ov}++; }    }
627 elsif ($over{'None'}) { %over = (); }
628
629 for($i=0; $i<@AllowOverride_v; $i++) {
630         $rv .= sprintf "<input type=checkbox name=AllowOverride_%s %s> %s<br>\n",
631                 $AllowOverride_v[$i],
632                 $over{$AllowOverride_v[$i]} ? "checked" : "",
633                 $AllowOverride_d[$i];
634         }
635 $rv .= "</td></tr></table>\n";
636 return (1, $text{'core_overr'}, $rv);
637 }
638 sub save_AllowOverride
639 {
640 local(@ov, $ov);
641 if ($in{'AllowOverride_def'}) { return ( [ ] ); }
642 foreach $ov (@AllowOverride_v) {
643         if ($in{"AllowOverride_$ov"}) { push(@ov, $ov); }
644         }
645 if (!@ov) { return ( [ "None" ] ); }
646 elsif (@ov == @AllowOverride_v) { return ( [ "All" ] ); }
647 else { return ( [ join(' ', @ov) ] ); }
648 }
649
650 sub edit_ContentDigest
651 {
652 return (1,
653         $text{'core_genmd5'},
654         &choice_input($_[0]->{'value'}, "ContentDigest", "",
655                       "$text{'yes'},on", "$text{'no'},off", "$text{'core_default'},"));
656 }
657 sub save_ContentDigest
658 {
659 return &parse_choice("ContentDigest", "");
660 }
661
662 sub edit_DocumentRoot
663 {
664 return (2, $text{'core_docroot'},
665         &opt_input($_[0]->{'words'}->[0], "DocumentRoot", $text{'core_default'}, 40).
666         &file_chooser_button("DocumentRoot", 1));
667 }
668 sub save_DocumentRoot
669 {
670 if (!$in{'DocumentRoot_def'}) {
671         -d $in{'DocumentRoot'} ||
672                 &error(&text('core_enodoc', $in{'DocumentRoot'}));
673         &allowed_doc_dir($in{'DocumentRoot'}) ||
674                 &error(&text('core_ecandoc', $in{'DocumentRoot'}));
675         }
676 return &parse_opt("DocumentRoot");
677 }
678
679 sub edit_Options
680 {
681 local(@po, @o, $o, %opts, $opts, $po, @pon, $i);
682 @po = ("ExecCGI", "FollowSymLinks", "Includes", "IncludesNOEXEC",
683        "Indexes", "MultiViews", "SymLinksIfOwnerMatch");
684 @pon = ("$text{'core_execcgi'}", "$text{'core_flink'}",
685         "$text{'core_inclexe'}", "$text{'core_incl'}",
686         "$text{'core_genind'}", "$text{'core_genmview'}",
687         "$text{'core_flinkmatch'}");
688 $opts = &choice_input($_[0] ? 0 : 1, "Options_def", 1, "$text{'core_default'},1",
689                       "$text{'core_optsel'},0")."<br>\n";
690 @o = split(/\s+/, $_[0]->{'value'});
691 foreach $o (split(/\s+/, $_[0]->{'value'})) {
692         if ($o =~ /^\+(.*)$/) { $opts{$1} = 2; }
693         elsif ($o =~ /^\-(.*)$/) { $opts{$1} = 3; }
694         else { $opts{$o} = 1; }
695         }
696 if ($opts{'All'}) {
697         local($all); $all = $opts{'All'};
698         undef(%opts);
699         foreach $o (grep {!/MultiViews/} @po) {
700                 $opts{$o} = $all;
701                 }
702         }
703 $opts .= "<table border>\n";
704 $opts .= "<tr $tb> <td><b>$text{'core_option'}</b></td> <td><b>$text{'core_setdir'}</b></td>\n";
705 $opts .= "<td><b>$text{'core_merge'}</b></td> </tr>\n";
706 for($i=0; $i<@po; $i++) {
707         $po = $po[$i];
708         $opts .= "<tr $cb> <td><b>$pon[$i]</b></td> <td>\n";
709         $opts .= sprintf "<input type=radio name=$po value=1 %s> $text{'yes'}\n",
710                         $opts{$po}==1 ? "checked" : "";
711         $opts .= sprintf "<input type=radio name=$po value=0 %s> $text{'no'}\n",
712                         $opts{$po}==0 ? "checked" : "";
713         $opts .= "</td> <td>\n";
714         $opts .= sprintf "<input type=radio name=$po value=2 %s> $text{'core_enable'}\n",
715                         $opts{$po}==2 ? "checked" : "";
716         $opts .= sprintf "<input type=radio name=$po value=3 %s> $text{'core_disable'}\n",
717                         $opts{$po}==3 ? "checked" : "";
718         $opts .= "</td> </tr>\n";
719         }
720 $opts .= "</table>\n";
721 return (2, $text{'core_diropts'}, $opts);
722 }
723 sub save_Options
724 {
725 local(@po, $po, @rv);
726 if ($in{'Options_def'}) { return ( [ ] ); }
727 @po = ("ExecCGI", "FollowSymLinks", "Includes", "IncludesNOEXEC",
728        "Indexes", "MultiViews", "SymLinksIfOwnerMatch");
729 foreach $po (@po) {
730         if ($in{$po} == 1) { push(@rv, $po); }
731         elsif ($in{$po} == 2) { push(@rv, "+$po"); }
732         elsif ($in{$po} == 3) { push(@rv, "-$po"); }
733         }
734 return @rv ? ( [ join(' ', @rv) ] ) : ( [ "None" ] );
735 }
736
737 sub edit_ServerPath
738 {
739 return (2,
740         $text{'core_virtpath'},
741         &opt_input($_[0]->{'value'}, "ServerPath", $text{'core_default'}, 40).
742         &file_chooser_button("ServerPath", 1));
743 }
744 sub save_ServerPath
745 {
746 return &parse_opt("ServerPath", '^\/\S*$',
747                   $text{'core_evirtpath'});
748 }
749
750 sub edit_ServerSignature
751 {
752 return (1, $text{'core_footer'},
753         &select_input($_[0]->{'value'}, "ServerSignature", undef,
754                       "$text{'core_sigemail'},Email", "$text{'core_signame'},On",
755                       "$text{'core_signone'},Off", "$text{'core_default'},"));
756 }
757 sub save_ServerSignature
758 {
759 return &parse_select("ServerSignature", undef);
760 }
761
762 sub edit_FileETag
763 {
764 local (%et, $rv);
765 map { $et{lc($_)}++ } @{$_[0]->{'words'}} if ($_[0]);
766 $rv .= sprintf "<input type=radio name=FileETag_def value=1 %s> %s\n",
767                         $_[0] ? "" : "checked", $text{'default'};
768 $rv .= sprintf "<input type=radio name=FileETag_def value=0 %s> %s\n",
769                         $_[0] ? "checked" : "", $text{'core_fileetag_sel'};
770 foreach $e ('INode', 'MTime', 'Size') {
771         $rv .= sprintf "<input type=checkbox name=FileETag value=%s %s> %s\n",
772                         $e, $et{lc($e)} || $et{'all'} ? "checked" : "",
773                         $text{'core_fileetag_'.lc($e)};
774         }
775 return (2, $text{'core_fileetag'}, $rv);
776 }
777 sub save_FileETag
778 {
779 if ($in{'FileETag_def'}) {
780         return ( [ ] );
781         }
782 else {
783         local @e = split(/\0/, $in{'FileETag'});
784         return @e ? ( [ join(" ", @e) ] ) : ( [ "None" ] );
785         }
786 }
787
788 #########################################################################
789 # MIME directives directives
790 sub edit_DefaultType
791 {
792 return (1,
793         $text{'core_defmime'},
794         &opt_input($_[0]->{'value'}, "DefaultType", $text{'core_default'}, 20));
795 }
796 sub save_DefaultType
797 {
798 return &parse_opt("DefaultType", '^(\S+)\/(\S+)$',
799                   $text{'core_edefmime'});
800 }
801
802 sub edit_ForceType
803 {
804 return (1, $text{'mod_mime_defmime'},
805         &opt_input($_[0]->{'value'}, "ForceType", $text{'mod_mime_real'}, 15));
806 }
807 sub save_ForceType
808 {
809 return &parse_opt("ForceType", '^\S+\/\S+$', $text{'mod_mime_etype'});
810 }
811
812 sub edit_SetOutputFilter
813 {
814 local @vals = split(/[\s;]+/, $_[0]->{'value'});
815 return (2, $text{'core_outfilter'},
816         &filters_input(\@vals, "SetOutputFilter"));
817 }
818 sub save_SetOutputFilter
819 {
820 return &parse_filters("SetOutputFilter");
821 }
822
823 sub edit_SetInputFilter
824 {
825 local @vals = split(/[\s;]+/, $_[0]->{'value'});
826 return (2, $text{'core_infilter'},
827         &filters_input(\@vals, "SetInputFilter"));
828 }
829 sub save_SetInputFilter
830 {
831 return &parse_filters("SetInputFilter");
832 }
833
834 sub edit_AddDefaultCharset
835 {
836 local $rv;
837 local $m = lc($_[0]->{'value'}) eq 'off' ? 2 :
838            $_[0]->{'value'} ? 0 : 1;
839 $rv .= sprintf "<input type=radio name=AddDefaultCharset_def value=1 %s> %s\n",
840                 $m == 1 ? "checked" : "", $text{'default'};
841 $rv .= sprintf "<input type=radio name=AddDefaultCharset_def value=2 %s> %s\n",
842                 $m == 2 ? 'checked' : "", $text{'core_none'};
843 $rv .= sprintf "<input type=radio name=AddDefaultCharset_def value=0 %s>\n",
844                 $m == 0 ? "checked" : "";
845 $rv .= sprintf "<input name=AddDefaultCharset size=12 value='%s'>\n",
846                 $m == 0 ? $_[0]->{'value'} : "";
847 return (1, $text{'core_defchar'}, $rv);
848 }
849 sub save_AddDefaultCharset
850 {
851 if ($in{'AddDefaultCharset_def'} == 1) {
852         return ( [ ] );
853         }
854 elsif ($in{'AddDefaultCharset_def'} == 2) {
855         return ( [ "Off" ] );
856         }
857 else {
858         $in{'AddDefaultCharset'} =~ /^\S+$/ || &error($text{'core_edefchar'});
859         return ( [ $in{'AddDefaultCharset'} ] );
860         }
861 }
862
863 #########################################################################
864 # Access control directives
865 sub edit_AuthName
866 {
867 my $val = $_[1]->{'version'} >= 1.3 ? $_[0]->{'words'}->[0]
868                                     : $_[0]->{'value'};
869 return (1, $text{'core_realm'},
870         &opt_input($val, "AuthName", $text{'core_default'}, 25));
871 }
872 sub save_AuthName
873 {
874 return $in{'AuthName_def'}       ? ( [ ] ) :
875        $_[0]->{'version'} >= 1.3 ? ( [ "\"$in{'AuthName'}\"" ] ) :
876                                    ( [ $in{'AuthName'} ] );
877 }
878
879 sub edit_AuthType
880 {
881 local($rv, $a);
882 $rv = "<select name=AuthType>\n";
883 foreach $a ("", "Basic", "Digest") {
884         $rv .= sprintf "<option %s>$a\n",
885                 lc($_[0]->{'value'}) eq lc($a) ? "selected" : "";
886         }
887 $rv .= "</select>";
888 return (1, $text{'core_authtype'}, $rv);
889 }
890 sub save_AuthType
891 {
892 if ($in{'AuthType'}) { return ( [ $in{'AuthType'} ] ); }
893 else { return ( [ ] ); }
894 }
895
896 sub edit_require
897 {
898 local($rv, $mode, $list);
899 local @w = @{$_[0]->{'words'}};
900 $mode = shift(@w);
901 $list = join(" ", map { $_ =~ /\s/ ? "\"$_\"" : $_ } @w);
902
903 # All users
904 $rv = sprintf
905       "<input type=radio name=require_mode value=0 %s> $text{'default'}<br>\n",
906       $mode ? "" : "checked";
907
908 # Only some users
909 $rv .= sprintf
910       "<input type=radio name=require_mode value=1 %s> $text{'core_users'}:\n",
911       $mode eq "user" ? "checked" : "";
912 $rv .= sprintf
913       "<input name=require_user size=20 value=\"%s\"><br>\n",
914       $mode eq "user" ? &html_escape($list) : "";
915
916 # Only members of groups
917 $rv .= sprintf
918       "<input type=radio name=require_mode value=2 %s> $text{'core_groups'}:\n",
919       $mode eq "group" ? "checked" : "";
920 $rv .= sprintf
921       "<input name=require_group size=20 value=\"%s\"><br>\n",
922       $mode eq "group" ? &html_escape($list) : "";
923
924 # All users
925 $rv .= sprintf
926       "<input type=radio name=require_mode value=3 %s> $text{'core_allusers'}<br>\n",
927       $mode eq "valid-user" ? "checked" : "";
928
929 if ($httpd_modules{'mod_authz_owner'} >= 2.2) {
930         # File owner / group matches
931         $rv .= sprintf
932               "<input type=radio name=require_mode value=4 %s> $text{'core_fileowner'}<br>\n",
933               $mode eq "file-owner" ? "checked" : "";
934         $rv .= sprintf
935               "<input type=radio name=require_mode value=5 %s> $text{'core_filegroup'}<br>\n",
936               $mode eq "file-group" ? "checked" : "";
937
938         }
939 return (1, $text{'core_authlog'}, $rv);
940 }
941 sub save_require
942 {
943 if ($in{'require_mode'} == 0) { return ( [ ] ); }
944 elsif ($in{'require_mode'} == 1) { return ( [ "user $in{'require_user'}" ] ); }
945 elsif ($in{'require_mode'} == 2) { return ( [ "group $in{'require_group'}" ] ); }
946 elsif ($in{'require_mode'} == 3) { return ( [ "valid-user" ] ); }
947 elsif ($in{'require_mode'} == 4) { return ( [ "file-owner" ] ); }
948 elsif ($in{'require_mode'} == 5) { return ( [ "file-group" ] ); }
949 else { return ( [ ] ); }        # huh?
950 }
951
952 sub edit_Satisfy
953 {
954 return (1, $text{'core_satisfy'},
955         &choice_input_vert($_[0]->{'value'}, "Satisfy", "", "$text{'core_default'},",
956                            "$text{'core_authall'},all","$text{'core_authany'},any"));
957 }
958 sub save_Satisfy
959 {
960 return &parse_choice("Satisfy", "");
961 }
962
963 #########################################################################
964 # Misc. directives
965 sub edit_CoreDumpDirectory
966 {
967 return (1, $text{'core_coredir'},
968          &opt_input($_[0]->{'words'}->[0], "CoreDumpDirectory", $text{'core_sroot'}, 20).
969          &file_chooser_button("CoreDumpDirectory", 1));
970 }
971 sub save_CoreDumpDirectory
972 {
973 return &parse_opt("CoreDumpDirectory", '^\S+$', $text{'core_ecore'});
974 }
975
976 sub edit_LockFile
977 {
978 return (1, $text{'core_lockfile'},
979         &opt_input($_[0]->{'words'}->[0], "LockFile", $text{'core_default'}, 20).
980         &file_chooser_button("LockFile", 0));
981 }
982 sub save_LockFile
983 {
984 return &parse_opt("LockFile", '^\S+', $text{'core_elock'});
985 }
986
987 sub edit_LimitRequestBody
988 {
989 return (1, $text{'core_maxbody'},
990         &opt_input($_[0]->{'value'}, "LimitRequestBody", $text{'core_default'}, 8)
991                 .$text{'bytes'});
992 }
993 sub save_LimitRequestBody
994 {
995 return &parse_opt("LimitRequestBody", '^\d+$', $text{'core_ebody'});
996 }
997
998 sub edit_LimitXMLRequestBody
999 {
1000 return (1, $text{'core_maxxml'},
1001         &opt_input($_[0]->{'value'}, "LimitXMLRequestBody",
1002                    $text{'core_default'}, 8).$text{'bytes'});
1003 }
1004 sub save_LimitXMLRequestBody
1005 {
1006 return &parse_opt("LimitXMLRequestBody", '^\d+$', $text{'core_exml'});
1007 }
1008
1009
1010
1011 sub edit_LimitRequestFields
1012 {
1013 return (1, $text{'core_maxhead'},
1014         &opt_input($_[0]->{'value'}, "LimitRequestFields", $text{'core_default'}, 6));
1015 }
1016 sub save_LimitRequestFields
1017 {
1018 return &parse_opt("LimitRequestFields", '^\d+$', $text{'core_ehead'});
1019 }
1020
1021 sub edit_LimitRequestFieldsize
1022 {
1023 return (1, $text{'core_maxshead'},
1024         &opt_input($_[0]->{'value'}, "LimitRequestFieldsize", $text{'core_default'}, 6));
1025 }
1026 sub save_LimitRequestFieldsize
1027 {
1028 return &parse_opt("LimitRequestFieldsize", '^\d+$', $text{'core_eshead'});
1029 }
1030
1031 sub edit_LimitRequestLine
1032 {
1033 return (1, $text{'core_maxline'},
1034         &opt_input($_[0]->{'value'}, "LimitRequestLine", $text{'core_default'}, 6));
1035 }
1036 sub save_LimitRequestLine
1037 {
1038 return &parse_opt("LimitRequestLine", '^\d+$', $text{'core_eline'});
1039 }
1040
1041 sub edit_PidFile
1042 {
1043 return (1, $text{'core_pid'},
1044         &opt_input($_[0]->{'words'}->[0], "PidFile", $text{'core_default'}, 20).
1045         &file_chooser_button("PidFile", 0));
1046 }
1047 sub save_PidFile
1048 {
1049 return &parse_opt("PidFile", '^\S+$', $text{'core_epid'});
1050 }
1051
1052 sub edit_ScoreBoardFile
1053 {
1054 return (1, $text{'core_memsco'},
1055         &opt_input($_[0]->{'words'}->[0], "ScoreBoardFile", $text{'core_default'}, 20).
1056         &file_chooser_button("ScoreBoardFile", 0));
1057 }
1058 sub save_ScoreBoardFile
1059 {
1060 return &parse_opt("ScoreBoardFile", '^\S+$', $text{'core_escore'});
1061 }
1062
1063 sub edit_ServerType
1064 {
1065 return (1, $text{'core_exec'},
1066         &choice_input($_[0]->{'value'}, "ServerType", "standalone",
1067                       "$text{'core_salone'},standalone", "$text{'core_inetd'},inetd"));
1068 }
1069 sub save_ServerType
1070 {
1071 return &parse_choice("ServerType", "standalone");
1072 }
1073
1074 sub edit_ServerTokens
1075 {
1076 local $v = $_[0]->{'value'};
1077 $v = "ProductOnly" if ($v eq "Prod");
1078 $v = "Min" if ($v eq "Minimal");
1079 return (1, $text{'core_header'},
1080         &select_input($v, "ServerTokens", "Full",
1081                       "$text{'core_verosmod'},Full",
1082                       "$text{'core_veros'},OS",
1083                       "$text{'core_ver'},Min",
1084                       "$text{'core_minor'},Minor",
1085                       $_[1]->{'version'} >= 1.313 ?
1086                         ("$text{'core_product'},ProductOnly") : (),
1087                       $_[1]->{'version'} >= 2.041 ?
1088                         ("$text{'core_major'},Major") : ()
1089                         ));
1090 }
1091 sub save_ServerTokens
1092 {
1093 return &parse_select("ServerTokens", "Full");
1094 }
1095
1096 #########################################################################
1097 # User/group directives
1098 sub edit_Group
1099 {
1100 local($rv, @ginfo);
1101 $rv = sprintf "<input type=radio name=Group value=0 %s>$text{'core_default'}&nbsp;\n",
1102        $_[0] ? "" : "checked";
1103 $rv .= sprintf "<input type=radio name=Group value=1 %s>$text{'core_group'}\n",
1104         $_[0] && $_[0]->{'words'}->[0] !~ /^#/ ? "checked" : "";
1105 $rv .= sprintf "<input name=Group_name size=8 value=\"%s\"> %s&nbsp;\n",
1106         $_[0]->{'words'}->[0] !~ /^#/ ? $_[0]->{'words'}->[0] : "",
1107         &group_chooser_button("Group_name", 0);
1108 $rv .= sprintf "<input type=radio name=Group value=2 %s>$text{'core_gid'}\n",
1109         $_[0]->{'words'}->[0] =~ /^#/ ? "checked" : "";
1110 $rv .= sprintf "<input name=Group_id size=6 value=\"%s\">\n",
1111          $_[0]->{'words'}->[0] =~ /^#(.*)$/ ? $1 : "";
1112 return (2, $text{'core_asgroup'}, $rv);
1113 }
1114 sub save_Group
1115 {
1116 if ($in{'Group'} == 0) { return ( [ ] ); }
1117 elsif ($in{'Group'} == 1) { return ( [ $in{'Group_name'} ] ); }
1118 elsif ($in{'Group_id'} !~ /^\-?\d+$/) {
1119         &error(&text('core_euid', $in{'Group_id'}));
1120         }
1121 else { return ( [ "\"#$in{'Group_id'}\"" ] ); }
1122 }
1123
1124 sub edit_User
1125 {
1126 local($rv, @uinfo);
1127 $rv = sprintf "<input type=radio name=User value=0 %s>$text{'core_default'}&nbsp;\n",
1128        $_[0] ? "" : "checked";
1129 $rv .= sprintf "<input type=radio name=User value=1 %s>$text{'core_user'}\n",
1130         $_[0] && $_[0]->{'words'}->[0] !~ /^#/ ? "checked" : "";
1131 $rv .= sprintf "<input name=User_name size=8 value=\"%s\"> %s&nbsp;\n",
1132         $_[0]->{'words'}->[0] !~ /^#/ ? $_[0]->{'words'}->[0] : "",
1133         &user_chooser_button("User_name", 0);
1134 $rv .= sprintf "<input type=radio name=User value=2 %s>$text{'core_uid'}\n",
1135         $_[0]->{'words'}->[0] =~ /^#/ ? "checked" : "";
1136 $rv .= sprintf "<input name=User_id size=6 value=\"%s\">\n",
1137          $_[0]->{'words'}->[0] =~ /^#(.*)$/ ? $1 : "";
1138 return (2, $text{'core_asuser'}, $rv);
1139 }
1140 sub save_User
1141 {
1142 if ($in{'User'} == 0) { return ( [ ] ); }
1143 elsif ($in{'User'} == 1) { return ( [ $in{'User_name'} ] ); }
1144 elsif ($in{'User_id'} !~ /^\-?\d+$/) {
1145         &error(&text('core_egid', $in{'User_id'}));
1146         }
1147 else { return ( [ "\"#$in{'User_id'}\"" ] ); }
1148 }
1149
1150 #########################################################################
1151 # Error handling directives
1152 sub edit_ErrorDocument
1153 {
1154 local($rv, $len, $i);
1155 $rv = "<table border width=100%>\n";
1156 $rv .= "<tr $tb> <td><b>$text{'core_error'}</b></td> <td><b>$text{'core_resp'}</b></td> ".
1157        "<td><b>$text{'core_urlmsg'}</b></td> </tr>\n";
1158 $len = @{$_[0]} + 1;
1159 for($i=0; $i<$len; $i++) {
1160         $v = $_[0]->[$i]->{'value'};
1161         if ($v =~ /^(\d+)\s+((http|ftp|gopher):\S+)$/)
1162                 { $code = $1; $type = 0; $url = $2; }
1163         elsif ($v =~ /^(\d+)\s+(\/.*)$/) { $code = $1; $type = 0; $url = $2; }
1164         elsif ($v =~ /^(\d+)\s+"(.*)"$/) { $code = $1; $type = 1; $url = $2; }
1165         elsif ($v =~ /^(\d+)\s+"?(.*)$/) { $code = $1; $type = 1; $url = $2; }
1166         else { $code = ""; $type = 0; $url = ""; }
1167         $rv .= "<tr $cb>\n";
1168         $rv .= "<td><input name=ErrorDocument_code_$i size=3 value=$code></td>\n";
1169         $rv .= "<td><input type=radio name=ErrorDocument_type_$i value=0 ".
1170                ($type==0 ? "checked" : "").">$text{'core_tourl'}\n";
1171         $rv .= "<input type=radio name=ErrorDocument_type_$i value=1 ".
1172                ($type==1 ? "checked" : "").">$text{'core_mesg'}</td>\n";
1173         $rv .= "<td><input name=ErrorDocument_url_$i size=40 value=\"$url\"></td>\n";
1174         $rv .= "</tr>\n";
1175         }
1176 $rv .= "</table>\n";
1177 return (2, $text{'core_custom'}, $rv);
1178 }
1179 sub save_ErrorDocument
1180 {
1181 local($i, $code, $url, @rv);
1182 for($i=0; defined($in{"ErrorDocument_code_$i"}); $i++) {
1183         $code = $in{"ErrorDocument_code_$i"}; $url = $in{"ErrorDocument_url_$i"};
1184         if ($code !~ /\S/ || $url !~ /\S/) { next; }
1185         $code =~ /^\d\d\d$/ || &error(&text('core_eerror', $code));
1186         if ($in{"ErrorDocument_type_$i"} == 0) {
1187                 $url =~ /^\S+$/ || &error(&text('core_eurl', $url));
1188                 push(@rv, "$code $url");
1189                 }
1190         elsif ($_[0]->{'version'} >= 2.0) { push(@rv, "$code \"$url\""); }
1191         elsif ($_[0]->{'version'} >= 1.2) { push(@rv, "$code \"$url"); }
1192         else { push(@rv, "$code $url"); }
1193         }
1194 return ( \@rv );
1195 }
1196
1197 #########################################################################
1198 # Logging directives
1199 sub edit_ErrorLog
1200 {
1201 if ($_[1]->{'version'} < 1.3) {
1202         return (1, $text{'core_errfile'},
1203                 &opt_input($_[0]->{'value'}, "ErrorLog", $text{'core_default'}, 20).
1204                 &file_chooser_button("ErrorLog", 0));
1205         }
1206 else {
1207         local $v = $_[0]->{'words'}->[0];
1208         local $t = !$v ? 3 :
1209                    $v eq 'syslog' ? 2 :
1210                    $v =~ /^\|/ ? 1 : 0;
1211         $rv = sprintf "<input type=radio name=ErrorLog_type value=3 %s>\n",
1212                 $t == 3 ? "checked" : "";
1213         $rv .= "$text{'core_default'}";
1214         $rv .= sprintf "<input type=radio name=ErrorLog_type value=2 %s>\n",
1215                 $t == 2 ? "checked" : "";
1216         $rv .= "$text{'core_syslog'}<br>\n";
1217         $rv .= sprintf "<input type=radio name=ErrorLog_type value=0 %s>\n",
1218                 $t == 0 ? "checked" : "";
1219         $rv .= sprintf "$text{'core_filelog'}<input name=ErrorLog_file size=25 value='%s'>\n",
1220                 $t == 0 ? $v : "";
1221         $rv .= sprintf "<input type=radio name=ErrorLog_type value=1 %s>\n",
1222                 $t == 1 ? "checked" : "";
1223         $rv .= sprintf "$text{'core_proglog'}<input name=ErrorLog_prog size=25 value='%s'><br>\n",
1224                 $t == 1 ? substr($v, 1) : "";
1225         return (2, $text{'core_logto'}, $rv);
1226         }
1227 }
1228 sub save_ErrorLog
1229 {
1230 if ($_[0]->{'version'} < 1.3) {
1231         $in{'ErrorLog_def'} || &allowed_auth_file($in{'ErrorLog'}) ||
1232                 &error($text{'core_edirlog'});
1233         $in{'ErrorLog_def'} || &directory_exists($in{'ErrorLog'}) ||
1234                     &error($text{'core_eerrordir'});
1235         return &parse_opt("ErrorLog", '^\S+$', $text{'core_efilelog'});
1236         }
1237 else {
1238         if ($in{'ErrorLog_type'} == 3) {
1239                 return ( [ ] );
1240                 }
1241         elsif ($in{'ErrorLog_type'} == 0) {
1242                 $in{'ErrorLog_file'} =~ /\S/ ||
1243                     &error($text{'core_efilemiss'});
1244                 &allowed_auth_file($in{'ErrorLog_file'}) ||
1245                     &error($text{'core_edirlog'});
1246                 &directory_exists($in{'ErrorLog_file'}) ||
1247                     &error($text{'core_eerrordir'});
1248                 return ( [ $in{'ErrorLog_file'} ] );
1249                 }
1250         elsif ($in{'ErrorLog_type'} == 1) {
1251                 $in{'ErrorLog_prog'} =~ /\S/ ||
1252                     &error($text{'core_eprogmiss'});
1253                 $access{'pipe'} ||
1254                     &error($text{'core_eperm'});
1255                 return ( [ "\"|$in{'ErrorLog_prog'}\"" ] );
1256                 }
1257         else {
1258                 return ( [ "syslog" ] );
1259                 }
1260         }
1261 }
1262
1263 sub edit_LogLevel
1264 {
1265 return (1, $text{'core_loglevel'},
1266         &select_input($_[0]->{'value'}, "LogLevel", "",
1267                       "$text{'core_log_emerg'} (emerg),emerg",
1268                       "$text{'core_log_alert'} (alert),alert",
1269                       "$text{'core_log_crit'} (crit),crit",
1270                       "$text{'core_log_error'} (error),error",
1271                       "$text{'core_log_warn'} (warn),warn",
1272                       "$text{'core_log_notice'} (notice),notice",
1273                       "$text{'core_log_info'} (info),info",
1274                       "$text{'core_log_debug'} (debug),debug"));
1275 }
1276 sub save_LogLevel
1277 {
1278 return &parse_select("LogLevel", "");
1279 }
1280
1281 #########################################################################
1282 # Module directives
1283 # This isn't shown if the distro has a way of managing these, such as Debian's
1284 # /etc/apache/mods-enabled
1285 sub edit_ClearModuleList_AddModule
1286 {
1287 local($mods, @allmods, $d, %mods, $m, $i, $rv);
1288 local $httpd = &find_httpd();
1289 ($ver, $mods) = &httpd_info($httpd);
1290 @allmods = grep { !/^core$/ } @$mods;
1291 local $conf = &get_config();
1292 foreach $d (&find_directive_struct("LoadModule", $conf)) {
1293         # Add mod_ like modules
1294         if ($d->{'words'}->[1] =~ /(mod_\S+)\.(so|dll)/) {
1295                 push(@allmods, $1);
1296                 }
1297         # nodo50 v0.1 - Change 000002 - Bug fixed: Apache-ssl module detected as mod_ssl. Now Apache-ssl module included as mod_apachessl
1298         # nodo50 v0.1 - Change 000002 - Bug corregido: El modulo Apache-ssl se detecta como mod_ssl. Ahora Apache-ssl se incluye como mod_apachessl
1299         # Add apache-ssl libssl.so as mod_apachessl module
1300         elsif ($d->{'words'}->[1] =~ /libssl\.so/) {
1301                 push(@allmods, "mod_apachessl");
1302                 }
1303         # Add others lib* like as mod_ modules
1304         # nodo50 v0.1 - Change 000002 - End
1305         elsif ($d->{'words'}->[1] =~ /lib([^\/\s]+)\.(so|dll)/) {
1306                 push(@allmods, "mod_$1");
1307                 }
1308         }
1309
1310 if (@{$_[0]}) {
1311         # Only selected modules have been enabled
1312         foreach $d (@{$_[1]}) {
1313                 local $modc = $d->{'value'};
1314                 $modc =~ s/\.c$//;
1315                 $mods{$modc} = "checked";
1316                 }
1317         }
1318 else { foreach $m (@allmods) { $mods{$m} = "checked"; } }
1319 $rv = &choice_input(@{$_[0]} ? 1 : 0, "ClearModuleList", 1,
1320                     "$text{'core_allmod'},0", "$text{'core_selmod'},1")."<br>\n";
1321 $rv .= "<table>\n";
1322 foreach $m (@allmods) {
1323         if ($i%4 == 0) { $rv .= "<tr>\n"; }
1324         $rv .= "<td><input name=AddModule type=checkbox value=$m $mods{$m}> $m</td>\n";
1325         if ($i++%4 == 3) { $rv .= "</tr>\n"; }
1326         }
1327 $rv .= "</table>\n";
1328 return (2, $text{'core_actmod'}, $rv);
1329 }
1330 sub save_ClearModuleList_AddModule
1331 {
1332 if ($in{'ClearModuleList'}) {
1333         local @mods = split(/\0/, $in{'AddModule'});
1334         return ( [ "" ], [ map { $_.".c" } @mods ] );
1335         }
1336 else { return ( [ ], [ ] ); }
1337 }
1338
1339 1;
1340