Handle hostnames with upper-case letters
[webmin.git] / proftpd / mod_core.pl
1 # mod_core.pl
2 # Core proftpd directives
3
4 # mod_core_directives(version)
5 # Returns a directive structure, like the one user by Apache. Types are :
6 #       0 - Networking
7 #       1 - Logging
8 #       2 - Files
9 #       3 - Access control
10 #       4 - Misc
11 #       5 - User and Group
12 #       6 - Authentication
13 sub mod_core_directives
14 {
15 local $rv = [
16         [ 'AccessDenyMsg', 0, 3, 'virtual anon global', 1.202 ],
17         [ 'AccessGrantMsg', 0, 3, 'virtual anon global', 0.99 ],
18         [ 'Allow Deny Order', 1, 3, 'limit', 0.99 ],
19         [ 'AllowAll DenyAll', 0, 3, 'directory anon limit ftpaccess', 0.99 ],
20         [ 'AllowFilter', 0, 3, 'virtual anon global', 1.20 ],
21         [ 'AllowForeignAddress', 0, 0, 'virtual anon global', 1.17 ],
22         [ 'AllowGroup', 1, 3, 'limit', 1.11 ],
23         [ 'AllowOverwrite', 0, 3, 'virtual anon directory ftpaccess global', 0.99 ],
24         [ 'AllowUser', 1, 3, 'limit', 1.17 ],
25         [ 'AllowRetrieveRestart', 0, 0, 'virtual anon directory global ftpaccess', 0.99 ],
26         [ 'AllowStoreRestart', 0, 0, 'virtual anon directory global ftpaccess', 0.99 ],
27         [ 'AnonRequirePassword', 0, 6, 'anon', 0.99 ],
28         [ 'AnonymousGroup', 0, 6, 'virtual global', 1.13 ],
29         [ 'AuthAliasOnly', 0, 6, 'virtual anon global', 1.13 ],
30         [ 'AuthUsingAlias', 0, 6, 'anon', 1.20 ],
31         [ 'Bind', 0, 0, 'virtual', '1.16-1.27' ],
32         [ 'DefaultAddress', 0, 0, 'virtual', '1.27' ],
33         [ 'CDPath', 1, 2, 'virtual anon global', 1.20 ],
34         [ 'Class Classes', 1, 3, 'virtual', 1.20 ],
35         [ 'CommandBufferSize', 0, 0, 'virtual global', 1.20 ],
36         [ 'DefaultServer', 0, 0, 'virtual', undef, 0.99, 8 ],
37         [ 'DefaultTransferMode', 0, 0, 'virtual global', 1.20 ],
38         [ 'DeferWelcome', 0, 0, 'virtual global', 0.99 ],
39         [ 'DeleteAbortedStores', 0, 2, 'virtual directory anon global ftpaccess', 1.20 ],
40         [ 'DenyFilter', 0, 3, 'virtual anon global', 1.20 ],
41         [ 'DenyGroup', 1, 3, 'limit', 1.11 ],
42         [ 'DenyUser', 1, 3, 'limit', 1.17 ],
43         [ 'DisplayConnect', 0, 6, 'virtual global', 1.20 ],
44         [ 'DisplayFirstChdir', 0, 2, 'virtual anon directory global', '0.99-1.31' ],
45         [ 'DisplayChdir', 0, 2, 'virtual anon directory global', 1.31 ],
46         [ 'DisplayGoAway', 0, 6, 'virtual anon global', 1.20 ],
47         [ 'DisplayLogin', 0, 6, 'virtual anon global', 0.99 ],
48         [ 'DisplayQuit', 0, 6, 'virtual anon global', 1.20 ],
49         [ 'Group', 0, 5, 'virtual anon', undef, 0.99, 9 ],
50         [ 'GroupOwner', 0, 5, 'anon directory ftpaccess', 0.99 ],
51         [ 'GroupPassword', 1, 6, 'virtual anon global', 0.99 ],
52         [ 'HiddenStor', 0, 2, 'virtual anon directory global', '1.20-1.31' ],
53         [ 'HiddenStores', 0, 2, 'virtual anon directory global', 1.31 ],
54         [ 'HideGroup', 1, 2, 'directory anon', 0.99 ],
55         [ 'HideNoAccess', 0, 2, 'directory anon', 0.99 ],
56         [ 'HideUser', 1, 2, 'directory anon', 0.99 ],
57         [ 'IdentLookups', 0, 0, 'virtual global', 1.15 ],
58         [ 'IgnoreHidden', 0, 2, 'limit', 0.99 ],
59         [ 'MasqueradeAddress', 0, 0, 'virtual', 1.202 ],
60         [ 'MaxClients', 0, 0, 'virtual anon global', 0.99 ],
61         [ 'MaxClientsPerHost', 0, 0, 'virtual anon global', 1.17 ],
62 #       [ 'MaxClientsPerUser', 0, 0, 'virtual anon global', 1.20 ],
63         [ 'MaxInstances', 0, 0, 'root', undef, 1.16, 8 ],
64         [ 'MaxLoginAttempts', 0, 6, 'virtual global', 0.99 ],
65         [ 'MultilineRFC2228', 0, 0, 'root', 1.20 ],
66         [ 'PassivePorts', 0, 0, 'virtual global', 1.20 ],
67         [ 'PathAllowFilter', 0, 2, 'virtual anon global', 1.17 ],
68         [ 'PathDenyFilter', 0, 2, 'virtual anon global', 1.17 ],
69         [ 'PidFile', 0, 4, 'root', 1.20 ],
70         [ 'Port', 0, 0, 'virtual', 0.99 ],
71         [ 'RequireValidShell', 0, 6, 'virtual anon global', 0.99 ],
72         [ 'RLimitCPU', 0, 4, 'root', 1.202 ],
73         [ 'RLimitMemory', 0, 4, 'root', 1.202 ],
74         [ 'RLimitOpenFiles', 0, 4, 'root', 1.202 ],
75         [ 'ScoreboardPath', 0, 4, 'root', 1.16 ],
76         [ 'ServerAdmin', 0, 4, 'virtual', 0.99 ],
77         [ 'ServerIdent', 0, 0, 'virtual global', 1.20 ],
78         [ 'ServerName', 0, 4, 'virtual', undef, 0.99, 11 ],
79         [ 'ServerType', 0, 0, 'root', undef, 0.99, 10 ],
80         [ 'ShowSymlinks', 0, 2, 'virtual anon global', 0.99 ],
81         [ 'SocketBindTight', 0, 0, 'root', 0.99 ],
82         [ 'SyslogFacility', 0, 1, 'root', 1.16 ],
83         [ 'SyslogLevel', 0, 1, 'virtual anon global', 1.20 ],
84         [ 'tcpBackLog', 0, 0, 'root', 0.99 ],
85         [ 'tcpNoDelay', 0, 0, 'virtual global', 1.20 ],
86         [ 'tcpReceiveWindow', 0, 0, 'virtual', 0.99 ],
87         [ 'tcpSendWindow', 0, 0, 'virtual', 0.99 ],
88         [ 'TimesGMT', 0, 4, 'root', 1.20 ],
89         [ 'TimeoutIdle', 0, 0, 'root', 0.99 ],
90         [ 'TimeoutLogin', 0, 0, 'root', 0.99 ],
91         [ 'TimeoutNoTransfer', 0, 0, 'root', 0.99 ],
92         [ 'TimeoutStalled', 0, 0, 'root', 1.16 ],
93         [ 'TransferLog', 0, 1, 'virtual anon global', undef, 1.14, 10 ],
94         [ 'Umask', 0, 2, 'virtual directory ftpaccess', undef, 0.99, 3 ],
95         [ 'UseFtpUsers', 0, 6, 'virtual anon global', 0.99 ],
96         [ 'UseHostsAllowFile', 0, 3, 'virtual anon', 1.20 ],
97         [ 'UseHostsDenyFile', 0, 3, 'virtual anon', 1.20 ],
98         [ 'UseReverseDNS', 0, 0, 'root', 1.17 ],
99         [ 'User', 0, 5, 'virtual anon', undef, 0.99, 10 ],
100         [ 'UserDirRoot', 0, 2, 'anon', 1.20 ],
101         [ 'UserAlias', 1, 6, 'virtual anon global', 0.99 ],
102         [ 'UserOwner', 0, 5, 'anon directory', 1.20 ],
103         [ 'UserPassword', 1, 6, 'virtual anon global', 0.99 ],
104         [ 'WtmpLog', 0, 4, 'virtual anon global', 1.17 ],
105         ];
106 return &make_directives($rv, $_[0], "mod_core");
107 }
108
109 sub edit_AccessDenyMsg
110 {
111 return (1, $text{'mod_core_accessdeny'},
112         &opt_input($_[0]->{'words'}->[0], "AccessDenyMsg", $text{'default'}, 20));
113 }
114 sub save_AccessDenyMsg
115 {
116 return &parse_opt("AccessDenyMsg");
117 }
118
119 sub edit_AccessGrantMsg
120 {
121 return (1, $text{'mod_core_accessgrant'},
122         &opt_input($_[0]->{'words'}->[0], "AccessGrantMsg", $text{'default'}, 20));
123 }
124 sub save_AccessGrantMsg
125 {
126 return &parse_opt("AccessGrantMsg");
127 }
128
129 sub edit_Allow_Deny_Order
130 {
131 local (@type, @what, @mode, $i);
132 foreach $d (@{$_[0]}, @{$_[1]}) {
133         local @w = @{$d->{'words'}};
134         shift(@w) if (lc($w[0]) eq 'from');
135         for($i=0; $i<@w; $i++) {
136                 push(@type, lc($d->{'name'}) eq "allow" ? 1 : 2);
137                 push(@what, $w[$i] eq 'all' || $w[$i] eq 'none' ? undef
138                                                                 : $w[$i]);
139                 if ($w[$i] eq 'all') { push(@mode, 0); }
140                 elsif ($w[$i] eq 'none') { push(@mode, 1); }
141                 elsif ($w[$i] =~ /^\d+\.\d+\.\d+\.\d+$/) { push(@mode, 2); }
142                 elsif ($w[$i] =~ /^[0-9\.\/]+$/) { push(@mode, 3); }
143                 else { push(@mode, 4); }
144                 }
145         }
146 push(@type, ""); push(@what, ""); push(@mode, 0);
147 $rv = "<i>$text{'mod_core_order'}</i>\n".
148       &choice_input($_[2]->[0]->{'value'}, "order", "",
149                     "$text{'mod_core_denyallow'},deny,allow", 
150                     "$text{'mod_core_allowdeny'},allow,deny", 
151                     "$text{'default'},")."<br>\n";
152 $rv .= "<table border>\n".
153        "<tr $tb> <td><b>$text{'mod_core_action'}</b></td> ".
154        "<td><b>$text{'mod_core_cond'}</b></td> </tr>\n";
155 @sels = map { $text{"mod_core_mode_$_"}.','.$_ } (0 .. 4);
156 for($i=0; $i<@type; $i++) {
157         $rv .= "<tr $cb> <td>".&select_input($type[$i], "allow_type_$i", "0",
158                 "&nbsp;,0", "$text{'mod_core_allow'},1",
159                 "$text{'mod_core_deny'},2")."</td>\n";
160         $rv .= "<td>".&select_input($mode[$i], "allow_mode_$i", "0", @sels);
161         $rv .= sprintf "<input name=allow_what_$i size=20 value=\"%s\"></td>\n",
162                  $mode[$i] ? $what[$i] : "";
163         $rv .= "</tr>\n";
164         }
165 $rv .= "</table>\n";
166 return (2, $text{'mod_core_allow_deny'}, $rv);
167 }
168 sub save_Allow_Deny_Order
169 {
170 local ($i, $type, $mode, $what, @allow, @deny);
171 for($i=0; defined($type = $in{"allow_type_$i"}); $i++) {
172         $mode = $in{"allow_mode_$i"}; $what = $in{"allow_what_$i"};
173         next if (!$type);
174         if ($mode == 0) { $what = "all"; }
175         elsif ($mode == 1) { $what = "none"; }
176         elsif ($mode == 2) {
177                 &check_ipaddress($what) || &check_ip6address($what) ||
178                         &error(&text('mod_core_eip', $what));
179                 }
180         elsif ($mode == 3) {
181                 $what =~ /^[0-9\.]+\.$/ ||
182                     $what =~ /^([0-9\.]+)\/\d+$/ && &check_ipaddress("$1") ||
183                     $what =~ /^([a-f0-9:]+)\/\d+$/ && &check_ip6address("$1") ||
184                         &error(&text('mod_core_enet', $what));
185                 }
186         elsif ($mode == 4) {
187                 $what =~ /^[A-Za-z0-9\.\-]+$/ ||
188                         &error(&text('mod_core_ehost', $what));
189                 }
190         if ($type == 1) { push(@allow, $what); }
191         else { push(@deny, $what); }
192         }
193 return ( \@allow, \@deny, &parse_choice("order", ""));
194 }
195
196 sub edit_AllowAll_DenyAll
197 {
198 #local $a = @{$_[0]}, $d = @{$_[1]};
199 local $a = $_[0], $d = $_[1];
200 local $rv = sprintf "<input type=radio name=AllowAll value=0 %s> %s\n",
201         $a || $d ? "" : "checked", $text{'mod_core_addefault'};
202 $rv .= sprintf "<input type=radio name=AllowAll value=1 %s> %s\n",
203         $a ? "checked" : "", $text{'mod_core_allowall'};
204 $rv .= sprintf "<input type=radio name=AllowAll value=2 %s> %s\n",
205         $d ? "checked" : "", $text{'mod_core_denyall'};
206 return (1, $text{'mod_core_adall'}, $rv);
207 }
208 sub save_AllowAll_DenyAll
209 {
210 return $in{'AllowAll'} == 0 ? ( [ ], [ ] ) :
211        $in{'AllowAll'} == 1 ? ( [ "" ], [ ] ) : ( [ ], [ "" ] );
212 }
213
214 sub edit_AllowFilter
215 {
216 return (1, $text{'mod_core_filter'},
217         &opt_input($_[0]->{'value'}, "AllowFilter", $text{'default'}, 15));
218 }
219 sub save_AllowFilter
220 {
221 return &parse_opt("AllowFilter");
222 }
223
224 sub edit_AllowForeignAddress
225 {
226 return (1, $text{'mod_core_foreign'},
227         &choice_input($_[0]->{'value'}, "AllowForeignAddress", "",
228                       "$text{'yes'},on", "$text{'no'},off",
229                       "$text{'default'},"));
230 }
231 sub save_AllowForeignAddress
232 {
233 return &parse_choice("AllowForeignAddress", "");
234 }
235
236 sub edit_AllowGroup
237 {
238 local $v = @{$_[0]} ? join(" ", (map { $_->{'value'} } @{$_[0]})) : undef;
239 return (2, $text{'mod_core_agroup'},
240         &opt_input($v, "AllowGroup", $text{'mod_core_all'}, 50));
241 }
242 sub save_AllowGroup
243 {
244 return ( $in{'AllowGroup_def'} ? [ ] : [ split(/\s+/, $in{'AllowGroup'}) ] );
245 }
246
247 sub edit_AllowOverwrite
248 {
249 return (1, $text{'mod_core_overwrite'},
250         &choice_input($_[0]->{'value'}, "AllowOverwrite", "",
251                       "$text{'yes'},on", "$text{'no'},off",
252                       "$text{'default'},"));
253 }
254 sub save_AllowOverwrite
255 {
256 return &parse_choice("AllowOverwrite", "");
257 }
258
259 sub edit_AllowRetrieveRestart
260 {
261 return (1, $text{'mod_core_restart'},
262         &choice_input($_[0]->{'value'}, "AllowRetrieveRestart", "",
263                       "$text{'yes'},on", "$text{'no'},off",
264                       "$text{'default'},"));
265 }
266 sub save_AllowRetrieveRestart
267 {
268 return &parse_choice("AllowRetrieveRestart", "");
269 }
270
271 sub edit_AllowStoreRestart
272 {
273 return (1, $text{'mod_core_restart2'},
274         &choice_input($_[0]->{'value'}, "AllowStoreRestart", "",
275                       "$text{'yes'},on", "$text{'no'},off",
276                       "$text{'default'},"));
277 }
278 sub save_AllowStoreRestart
279 {
280 return &parse_choice("AllowStoreRestart", "");
281 }
282
283 sub edit_AllowUser
284 {
285 local $v = @{$_[0]} ? join(" ", (map { $_->{'value'} } @{$_[0]})) : undef;
286 return (2, $text{'mod_core_auser'},
287         &opt_input($v, "AllowUser", $text{'mod_core_all'}, 50));
288 }
289 sub save_AllowUser
290 {
291 return ( $in{'AllowUser_def'} ? [ ] : [ split(/\s+/, $in{'AllowUser'}) ] );
292 }
293
294 sub edit_AnonRequirePassword
295 {
296 return (1, $text{'mod_core_require'},
297         &choice_input($_[0]->{'value'}, "AnonRequirePassword", "",
298                       "$text{'yes'},on", "$text{'no'},off",
299                       "$text{'default'},"));
300 }
301 sub save_AnonRequirePassword
302 {
303 return &parse_choice("AnonRequirePassword", "");
304 }
305
306 sub edit_AnonymousGroup
307 {
308 return (2, $text{'mod_core_anongroup'},
309         &opt_input($_[0]->{'value'}, "AnonymousGroup", $text{'default'}, 50));
310         
311 }
312 sub save_AnonymousGroup
313 {
314 return &parse_opt("AnonymousGroup", '\S', $text{'mod_core_eanongroup'});
315 }
316
317 sub edit_AuthAliasOnly
318 {
319 return (1, $text{'mod_core_authalias'},
320         &choice_input($_[0]->{'value'}, "AuthAliasOnly", "",
321                       "$text{'yes'},on", "$text{'no'},off",
322                       "$text{'default'},"));
323 }
324 sub save_AuthAliasOnly
325 {
326 return &parse_choice("AuthAliasOnly", "");
327 }
328
329 sub edit_AuthUsingAlias
330 {
331 return (1, $text{'mod_core_authusingalias'},
332         &choice_input($_[0]->{'value'}, "AuthUsingAlias", "",
333                       "$text{'yes'},on", "$text{'no'},off",
334                       "$text{'default'},"));
335 }
336 sub save_AuthUsingAlias
337 {
338 return &parse_choice("AuthUsingAlias", "");
339 }
340
341 sub edit_Bind
342 {
343 return (1, $text{'mod_core_bind'},
344         &opt_input($_[0]->{'value'}, "Bind", $text{'mod_core_bind_all'}, 15));
345 }
346 sub save_Bind
347 {
348 return &parse_opt("Bind", '^(\d+)\.(\d+)\.(\d+)\.(\d+)|([0-9:]+)$',
349                   $text{'mod_core_ebind'});
350 }
351
352 sub edit_DefaultAddress
353 {
354 return (1, $text{'mod_core_bind'},
355         &opt_input($_[0]->{'value'}, "DefaultAddress", $text{'mod_core_bind_all'}, 15));
356 }
357 sub save_DefaultAddress
358 {
359 $in{'DefaultAddress_def'} || &to_ipaddress($in{'DefaultAddress'}) ||
360         &to_ip6address($in{'DefaultAddress'}) ||
361         &error(text{'mod_core_ebind'});
362 return &parse_opt("DefaultAddress", '^\S+$',
363                   $text{'mod_core_ebind'});
364 }
365
366 sub edit_CDPath
367 {
368 local $rv = "<textarea rows=3 cols=50 name=CDPath>";
369 foreach $p (@{$_[0]}) {
370         $rv .= "$p->{'value'}\n";
371         }
372 $rv .= "</textarea>\n";
373 return (2, $text{'mod_core_cdpath'}, $rv);
374 }
375 sub save_CDPath
376 {
377 $in{'CDPath'} =~ s/\r//g;
378 return ( [ split(/\s+/, $in{'CDPath'}) ] );
379 }
380
381 sub edit_Class_Classes
382 {
383 local $rv = $text{'mod_core_classes'}.
384             &choice_input($_[1]->[0]->{'value'}, "Classes", "",
385                           "$text{'yes'},on", "$text{'no'},off",
386                           "$text{'default'},")."<br>\n";
387 $rv .= "<table border>\n".
388        "<tr $tb> <td><b>$text{'mod_core_cname'}</b></td> ".
389        "<td><b>$text{'mod_core_ctype'}</b></td> </tr>\n";
390 local $i = 0;
391 foreach $c (@{$_[0]}, { }) {
392         local @w = @{$c->{'words'}};
393         $rv .= "<tr $cb>\n";
394         $rv .= "<td><input name=Class_n_$i size=10 value='$w[0]'></td>\n";
395         $rv .= "<td><select name=Class_t_$i>\n";
396         $rv .= sprintf "<option value=limit %s>%s\n",
397                 $w[1] eq 'limit' ? 'selected' : '', $text{'mod_core_climit'};
398         $rv .= sprintf "<option value=regex %s>%s\n",
399                 $w[1] eq 'regex' ? 'selected' : '', $text{'mod_core_cregex'};
400         $rv .= sprintf "<option value=ip %s>%s\n",
401                 $w[1] eq 'ip' ? 'selected' : '', $text{'mod_core_cip'};
402         $rv .= "</select>\n";
403         $rv .= "<input name=Class_v_$i size=20 value='$w[2]'></td>\n";
404         $rv .= "</tr>\n";
405         $i++;
406         }
407 $rv .= "</table>\n";
408 return (2, $text{'mod_core_cls'}, $rv);
409 }
410 sub save_Class_Classes
411 {
412 local ($i, @rv);
413 for($i=0; defined($in{"Class_n_$i"}); $i++) {
414         next if (!$in{"Class_n_$i"});
415         $in{"Class_t_$i"} ne 'limit' ||
416                 $in{"Class_v_$i"} =~ /^\d+$/ ||
417                         &error($text{'mod_core_eclimit'});
418         $in{"Class_t_$i"} ne 'regex' ||
419                 $in{"Class_v_$i"} =~ /\S/ ||
420                         &error($text{'mod_core_ecregex'});
421         $in{"Class_t_$i"} ne 'ip' ||
422                 $in{"Class_v_$i"} =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)\/(\d+)$/ ||
423                         &error($text{'mod_core_ecip'});
424         push(@rv, join(" ", $in{"Class_n_$i"}, $in{"Class_t_$i"},
425                             $in{"Class_v_$i"}));
426         }
427 return ( \@rv, $in{'Classes'} eq 'on' ? [ 'on' ] :
428                $in{'Classes'} eq 'off' ? [ 'off' ] : [ ] );
429 }
430
431 sub edit_CommandBufferSize
432 {
433 return (1, $text{'mod_core_buffer'},
434         &opt_input($_[0]->{'value'}, "CommandBufferSize", $text{'default'}, 5));
435 }
436 sub save_CommandBufferSize
437 {
438 return &parse_opt("CommandBufferSize", '^\d+$', $text{'mod_core_ebuffer'});
439 }
440
441 sub edit_DefaultServer
442 {
443 return (1, $text{'mod_core_defaultserver'},
444         &choice_input($_[0]->{'value'}, "DefaultServer", "off",
445                       "$text{'yes'},on",
446                       "$text{'no'},off"));
447 }
448 sub save_DefaultServer
449 {
450 return &parse_choice("DefaultServer", "off");
451 }
452
453 sub edit_DefaultTransferMode
454 {
455 return (1, $text{'mod_core_transfer'},
456         &select_input($_[0]->{'value'}, "DefaultTransferMode", "",
457                       "$text{'mod_core_ascii'},ascii",
458                       "$text{'mod_core_binary'},binary",
459                       "$text{'default'},"));
460 }
461 sub save_DefaultTransferMode
462 {
463 return &parse_choice("DefaultTransferMode", "");
464 }
465
466 sub edit_DeferWelcome
467 {
468 return (1, $text{'mod_core_defer'},
469         &choice_input($_[0]->{'value'}, "DeferWelcome", "",
470                       "$text{'yes'},on", "$text{'no'},off",
471                       "$text{'default'},"));
472 }
473 sub save_DeferWelcome
474 {
475 return &parse_choice("DeferWelcome", "");
476 }
477
478 sub edit_DeleteAbortedStores
479 {
480 return (1, $text{'mod_core_aborted'},
481         &choice_input($_[0]->{'value'}, "DeleteAbortedStores", "",
482                       "$text{'yes'},on", "$text{'no'},off",
483                       "$text{'default'},"));
484 }
485 sub save_DeleteAbortedStores
486 {
487 return &parse_choice("DeleteAbortedStores", "");
488 }
489
490 sub edit_DenyFilter
491 {
492 return (1, $text{'mod_core_dfilter'},
493         &opt_input($_[0]->{'value'}, "DenyFilter", $text{'default'}, 15));
494 }
495 sub save_DenyFilter
496 {
497 return &parse_opt("DenyFilter");
498 }
499
500 sub edit_DenyGroup
501 {
502 local $v = @{$_[0]} ? join(" ", (map { $_->{'value'} } @{$_[0]})) : undef;
503 return (2, $text{'mod_core_dgroup'},
504         &opt_input($v, "DenyGroup", $text{'mod_core_none'}, 50));
505 }
506 sub save_DenyGroup
507 {
508 return ( $in{'DenyGroup_def'} ? [ ] : [ split(/\s+/, $in{'DenyGroup'}) ] );
509 }
510
511 sub edit_DenyUser
512 {
513 local $v = @{$_[0]} ? join(" ", (map { $_->{'value'} } @{$_[0]})) : undef;
514 return (2, $text{'mod_core_duser'},
515         &opt_input($v, "DenyUser", $text{'mod_core_none'}, 50));
516 }
517 sub save_DenyUser
518 {
519 return ( $in{'DenyUser_def'} ? [ ] : [ split(/\s+/, $in{'DenyUser'}) ] );
520 }
521
522 sub edit_DisplayConnect
523 {
524 return (2, $text{'mod_core_display'},
525         &opt_input($_[0]->{'value'}, "DisplayConnect",
526                    $text{'mod_core_none'}, 50));
527 }
528 sub save_DisplayConnect
529 {
530 return &parse_opt("DisplayConnect", '\S', $text{'mod_core_edisplay'});
531 }
532
533 sub edit_DisplayFirstChdir
534 {
535 return (1, $text{'mod_core_firstcd'},
536         &opt_input($_[0]->{'value'}, "DisplayFirstChdir",
537                    $text{'mod_core_none'}, 15));
538 }
539 sub save_DisplayFirstChdir
540 {
541 return &parse_opt("DisplayFirstChdir", '^\S+$', $text{'mod_core_efirstcd'});
542 }
543
544 sub edit_DisplayChdir
545 {
546 return (1, $text{'mod_core_firstcd'},
547         &opt_input($_[0]->{'words'}->[0], "DisplayChdir",
548                    $text{'mod_core_none'}, 15).
549         &ui_checkbox("DisplayChdir_always", 'true', $text{'mod_core_firstcdt'},
550                      $_[0]->{'words'}->[1] eq 'true'));
551 }
552 sub save_DisplayChdir
553 {
554 local @rv = &parse_opt("DisplayChdir", '^\S+$', $text{'mod_core_efirstcd'});
555 if ($in{'DisplayChdir_always'}) {
556         $rv[0]->[0] .= ' true';
557         }
558 return @rv;
559 }
560
561 sub edit_DisplayGoAway
562 {
563 return (2, $text{'mod_core_goaway'},
564         &opt_input($_[0]->{'value'}, "DisplayGoAway",
565                    $text{'mod_core_none'}, 50));
566 }
567 sub save_DisplayGoAway
568 {
569 return &parse_opt("DisplayGoAway", '\S', $text{'mod_core_egoaway'});
570 }
571
572 sub edit_DisplayLogin
573 {
574 return (2, $text{'mod_core_login'},
575         &opt_input($_[0]->{'value'}, "DisplayLogin",
576                    $text{'mod_core_none'}, 50));
577 }
578 sub save_DisplayLogin
579 {
580 return &parse_opt("DisplayLogin", '\S', $text{'mod_core_elogin'});
581 }
582
583 sub edit_DisplayQuit
584 {
585 return (2, $text{'mod_core_quit'},
586         &opt_input($_[0]->{'value'}, "DisplayQuit",
587                    $text{'mod_core_none'}, 50));
588 }
589 sub save_DisplayQuit
590 {
591 return &parse_opt("DisplayQuit", '\S', $text{'mod_core_equit'});
592 }
593
594 sub edit_Group
595 {
596 local($rv, @ginfo);
597 $rv = sprintf "<input type=radio name=Group value=0 %s> $text{'default'}\n",
598        $_[0] ? "" : "checked";
599 $rv .= sprintf "<input type=radio name=Group value=1 %s> %s\n",
600         $_[0] && $_[0]->{'value'} !~ /^#/ ? "checked" : "",
601         $text{'mod_core_gname'};
602 $rv .= sprintf "<input name=Group_name size=8 value=\"%s\"> %s\n",
603         $_[0]->{'value'} !~ /^#/ ? $_[0]->{'value'} : "",
604         &group_chooser_button("Group_name", 0);
605 $rv .= sprintf "<input type=radio name=Group value=2 %s> %s\n",
606         $_[0]->{'value'} =~ /^#/ ? "checked" : "",
607         $text{'mod_core_gid'};
608 $rv .= sprintf "<input name=Group_id size=6 value=\"%s\">\n",
609          $_[0]->{'value'} =~ /^#(.*)$/ ? $1 : "";
610 return (2, $text{'mod_core_group'}, $rv);
611 }
612 sub save_Group
613 {
614 if ($in{'Group'} == 0) { return ( [ ] ); }
615 elsif ($in{'Group'} == 1) { return ( [ $in{'Group_name'} ] ); }
616 elsif ($in{'Group_id'} !~ /^\-?\d+$/) {
617         &error(&text('core_euid', $in{'Group_id'}));
618         }
619 else { return ( [ "#$in{'Group_id'}" ] ); }
620 }
621
622 sub edit_GroupOwner
623 {
624 return (1, $text{'mod_core_gowner'},
625         &opt_input($_[0]->{'value'}, "GroupOwner", $text{'default'}, 13,
626                    &group_chooser_button("GroupOwner")));
627 }
628 sub save_GroupOwner
629 {
630 if ($in{'GroupOwner_def'}) { return ( [ ] ); }
631 else {
632         defined(getgrnam($in{'GroupOwner'})) || &error($text{'mod_core_egowner'});
633         return ( [ $in{'GroupOwner'} ] );
634         }
635 }
636
637 sub edit_GroupPassword
638 {
639 local $rv = "<table border>\n";
640 $rv .= "<tr $tb> <td><b>$text{'mod_core_gpname'}</b></td> ".
641        "<td><b>$text{'mod_core_gppass'}</b></td> </tr>\n";
642 local $i = 0;
643 foreach $g (@{$_[0]}) {
644         local @v = @{$g->{'words'}};
645         $rv .= "<tr $cb>\n";
646         $rv .= "<td><input name=GroupPassword_n_$i size=13 value='$v[0]'></td>\n";
647         $rv .= "<td><input type=radio name=GroupPassword_d_$i value='$v[1]' checked> $text{'mod_core_gpdef'}\n";
648         $rv .= "<input type=radio name=GroupPassword_d_$i value=0>\n";
649         $rv .= "<input name=GroupPassword_p_$i size=25></td> </tr>\n";
650         $i++;
651         }
652 $rv .= "<tr $cb>\n".
653        "<td><input name=GroupPassword_n_$i size=13></td>\n".
654        "<td><input name=GroupPassword_p_$i size=35></td>\n".
655        "</tr> </table>\n";
656 return (2, $text{'mod_core_grouppassword'}, $rv);
657 }
658 sub save_GroupPassword
659 {
660 local @rv;
661 for($i=0; defined($in{"GroupPassword_n_$i"}); $i++) {
662         next if (!$in{"GroupPassword_n_$i"});
663         scalar(getgrnam($in{"GroupPassword_n_$i"})) ||
664                 &error($text{'mod_core_egpname'});
665         if ($in{"GroupPassword_d_$i"}) {
666                 push(@rv, $in{"GroupPassword_n_$i"}.' '.
667                           $in{"GroupPassword_d_$i"});
668                 }
669         else {
670                 $salt = substr(time(), 0, 2);
671                 push(@rv, $in{"GroupPassword_n_$i"}.' '.
672                           &unix_crypt($in{"GroupPassword_p_$i"}, $salt));
673                 }
674         }
675 return ( \@rv );
676 }
677
678 sub edit_HiddenStor
679 {
680 return (1, $text{'mod_core_hstor'},
681         &choice_input($_[0]->{'value'}, "HiddenStor", "",
682                       "$text{'yes'},on", "$text{'no'},off",
683                       "$text{'default'},"));
684 }
685 sub save_HiddenStor
686 {
687 return &parse_choice("HiddenStor", "");
688 }
689
690 sub edit_HiddenStores
691 {
692 return (1, $text{'mod_core_hstor'},
693         &choice_input($_[0]->{'value'}, "HiddenStores", "",
694                       "$text{'yes'},on", "$text{'no'},off",
695                       "$text{'default'},"));
696 }
697 sub save_HiddenStores
698 {
699 return &parse_choice("HiddenStores", "");
700 }
701
702 sub edit_HideGroup
703 {
704 return (2, $text{'mod_core_hgroup'},
705         sprintf "<input name=HideGroup size=50 value='%s'>",
706          join(" ", map { $_->{'value'} } @{$_[0]}));
707 }
708 sub save_HideGroup
709 {
710 local @hg = split(/\s+/, $in{'HideGroup'});
711 foreach $g (@hg) {
712         scalar(getgrnam($g)) || &error($text{'mod_core_ehgroup'});
713         }
714 return ( \@hg );
715 }
716
717 sub edit_HideNoAccess
718 {
719 return (1, $text{'mod_core_hnoaccess'},
720         &choice_input($_[0]->{'value'}, "HideNoAccess", "",
721                       "$text{'yes'},on", "$text{'no'},off",
722                       "$text{'default'},"));
723 }
724 sub save_HideNoAccess
725 {
726 return &parse_choice("HideNoAccess", "");
727 }
728
729 sub edit_HideUser
730 {
731 return (2, $text{'mod_core_huser'},
732         sprintf "<input name=HideUser size=50 value='%s'>",
733          join(" ", map { $_->{'value'} } @{$_[0]}));
734 }
735 sub save_HideUser
736 {
737 local @hu = split(/\s+/, $in{'HideUser'});
738 foreach $u (@hu) {
739         defined(getpwnam($u)) || &error($text{'mod_core_ehuser'});
740         }
741 return ( \@hu );
742 }
743
744 sub edit_IdentLookups
745 {
746 return (1, $text{'mod_core_ident'},
747         &choice_input($_[0]->{'value'}, "IdentLookups", "",
748                       "$text{'yes'},on", "$text{'no'},off",
749                       "$text{'default'},"));
750 }
751 sub save_IdentLookups
752 {
753 return &parse_choice("IdentLookups", "");
754 }
755
756 sub edit_IgnoreHidden
757 {
758 return (1, $text{'mod_core_ihidden'},
759         &choice_input($_[0]->{'value'}, "IgnoreHidden", "off",
760                       "$text{'yes'},on", "$text{'no'},off"));
761 }
762 sub save_IgnoreHidden
763 {
764 return &parse_choice("IgnoreHidden", "off");
765 }
766
767 sub edit_MasqueradeAddress
768 {
769 return (2, $text{'mod_core_masq'},
770         &opt_input($_[0]->{'value'}, "MasqueradeAddress",
771                    $text{'mod_core_masq_def'}, 30));
772 }
773 sub save_MasqueradeAddress
774 {
775 $in{'MasqueradeAddress_def'} || &to_ipaddress($in{'MasqueradeAddress'}) ||
776         &error($text{'mod_core_emasq'});
777 return &parse_opt("MasqueradeAddress");
778 }
779
780 sub edit_MaxClients
781 {
782 return (2, $text{'mod_core_maxc'}, &edit_max($_[0], "MaxClients"));
783 }
784 sub save_MaxClients
785 {
786 return &save_max("MaxClients");
787 }
788
789 sub edit_MaxClientsPerHost
790 {
791 return (2, $text{'mod_core_maxch'}, &edit_max($_[0], "MaxClientsPerHost"));
792 }
793 sub save_MaxClientsPerHost
794 {
795 return &save_max("MaxClientsPerHost");
796 }
797
798 sub edit_MaxClientsPerUser
799 {
800 return (2, $text{'mod_core_maxcu'}, &edit_max($_[0], "MaxClientsPerUser"));
801 }
802 sub save_MaxClientsPerUser
803 {
804 return &save_max("MaxClientsPerUser");
805 }
806
807 sub edit_max
808 {
809 local $m = !$_[0] ? 0 :
810            $_[0]->{'words'}->[0] eq 'none' ? 1 : 2;
811 local $rv = sprintf "<input type=radio name=$_[1]_m value=0 %s> %s\n",
812                 $m == 0 ? "checked" : "", $text{'default'};
813 $rv .= sprintf "<input type=radio name=$_[1]_m value=1 %s> %s\n",
814                 $m == 1 ? "checked" : "", $text{'mod_core_maxc1'};
815 $rv .= sprintf "<input type=radio name=$_[1]_m value=2 %s>\n",
816                 $m == 2 ? "checked" : "";
817 $rv .= sprintf "<input name=$_[1] size=6 value='%s'><br>\n",
818                 $m == 2 ? $_[0]->{'words'}->[0] : "";
819 $rv .= sprintf "%s <input name=$_[1]_t size=40 value='%s'>\n",
820         $text{'mod_core_maxcmsg'}, $_[0]->{'words'}->[1];
821 return $rv;
822 }
823 sub save_max
824 {
825 if ($in{"$_[0]_m"} == 0) {
826         return ( [ ] );
827         }
828 else {
829         local $n;
830         if ($in{"$_[0]_m"} == 1) {
831                 $n = "none";
832                 }
833         else {
834                 $in{$_[0]} =~ /^\d+$/ || &error($text{'mod_core_emaxc'});
835                 $n = $in{$_[0]};
836                 }
837         if ($in{"$_[0]_t"}) {
838                 return ( [ "$n \"".$in{"$_[0]_t"}."\"" ] );
839                 }
840         else {
841                 return ( [ $n ] );
842                 }
843         }
844 }
845
846 sub edit_MaxInstances
847 {
848 return (1, $text{'mod_core_instances'},
849         &opt_input($_[0]->{'value'}, "MaxInstances", $text{'default'}, 4));
850 }
851 sub save_MaxInstances
852 {
853 return &parse_opt("MaxInstances", '^\d+$', $text{'mod_core_einstances'});
854 }
855
856 sub edit_MaxLoginAttempts
857 {
858 return (1, $text{'mod_core_logins'},
859         &opt_input($_[0]->{'value'}, "MaxLoginAttempts", $text{'default'}, 4));
860 }
861 sub save_MaxLoginAttempts
862 {
863 return &parse_opt("MaxLoginAttempts", '^\d+$', $text{'mod_core_elogins'});
864 }
865
866 sub edit_MultilineRFC2228
867 {
868 return (1, $text{'mod_core_rfc2228'},
869         &choice_input($_[0]->{'value'}, "MultilineRFC2228", "",
870                       "$text{'yes'},on", "$text{'no'},off",
871                       "$text{'default'},"));
872 }
873 sub save_MultilineRFC2228
874 {
875 return &parse_choice("MultilineRFC2228", "");
876 }
877
878 sub edit_PassivePorts
879 {
880 local $rv = sprintf "<input type=radio name=PassivePorts_def value=1 %s> %s\n",
881                 $_[0] ? "" : "checked", $text{'default'};
882 $rv .= sprintf "<input type=radio name=PassivePorts_def value=0 %s> %s\n",
883                 $_[0] ? "checked" : "", $text{'mod_core_pasvr'};
884 $rv .= sprintf "<input name=PassivePorts_f size=5 value='%s'> -\n",
885                 $_[0]->{'words'}->[0];
886 $rv .= sprintf "<input name=PassivePorts_t size=5 value='%s'>\n",
887                 $_[0]->{'words'}->[1];
888 return (1, $text{'mod_core_pasv'}, $rv);
889 }
890 sub save_PassivePorts
891 {
892 if ($in{'PassivePorts_def'}) {
893         return ( [ ] );
894         }
895 else {
896         $in{'PassivePorts_f'} =~ /^\d+$/ || &error($text{'mod_core_epasv'});
897         $in{'PassivePorts_t'} =~ /^\d+$/ || &error($text{'mod_core_epasv'});
898         return ( [ "$in{'PassivePorts_f'} $in{'PassivePorts_t'}" ] );
899         }
900 }
901
902 sub edit_PathAllowFilter
903 {
904 return (1, $text{'mod_core_pathallow'},
905         &opt_input($_[0]->{'words'}->[0], "PathAllowFilter",
906                    $text{'mod_core_any'}, 20));
907 }
908 sub save_PathAllowFilter
909 {
910 return &parse_opt("PathAllowFilter");
911 }
912
913 sub edit_PathDenyFilter
914 {
915 return (1, $text{'mod_core_pathdeny'},
916         &opt_input($_[0]->{'words'}->[0], "PathDenyFilter",
917                    $text{'mod_core_none'}, 20));
918 }
919 sub save_PathDenyFilter
920 {
921 return &parse_opt("PathDenyFilter");
922 }
923
924 sub edit_PidFile
925 {
926 return (2, $text{'mod_core_pidfile'},
927         &opt_input($_[0]->{'words'}->[0], "PidFile", $text{'default'}, 50,
928                    &file_chooser_button("PidFile")));
929 }
930 sub save_PidFile
931 {
932 return &parse_opt("PidFile", '^\/\S+$', $text{'mod_core_epidfile'});
933 }
934
935 sub edit_Port
936 {
937 return (1, $text{'mod_core_port'},
938         &opt_input($_[0]->{'value'}, "Port", $text{'default'}, 6));
939 }
940 sub save_Port
941 {
942 return &parse_opt("Port", '^\d+$', $text{'mod_core_eport'});
943 }
944
945 sub edit_RequireValidShell
946 {
947 return (1, $text{'mod_core_shell'},
948         &choice_input($_[0]->{'value'}, "RequireValidShell", "",
949                       "$text{'yes'},on", "$text{'no'},off",
950                       "$text{'default'},"));
951 }
952 sub save_RequireValidShell
953 {
954 return &parse_choice("RequireValidShell", "");
955 }
956
957 sub edit_RLimitCPU
958 {
959 return &rlimit_input("RLimitCPU", $text{'mod_core_cpulimit'}, $_[0]);
960 }
961 sub save_RLimitCPU
962 {
963 return &parse_rlimit("RLimitCPU", $text{'mod_core_ecpulimit'});
964 }
965
966 sub edit_RLimitMemory
967 {
968 return &rlimit_input("RLimitMemory", $text{'mod_core_memlimit'}, $_[0]);
969 }
970 sub save_RLimitMemory
971 {
972 return &parse_rlimit("RLimitMemory", $text{'mod_core_ememlimit'});
973 }
974
975 sub edit_RLimitOpenFiles
976 {
977 return &rlimit_input("RLimitOpenFiles", $text{'mod_core_filelimit'}, $_[0]);
978 }
979 sub save_RLimitOpenFiles
980 {
981 return &parse_rlimit("RLimitOpenFiles", $text{'mod_core_efilelimit'});
982 }
983
984 # rlimit_input(name, desc, value)
985 sub rlimit_input
986 {
987 local @w = @{$_[2]->{'words'}};
988 local $rv;
989 $rv .= sprintf "<b>%s</b> <input type=radio name=%s_smax value=2 %s> %s\n",
990                 $text{'mod_core_soft'}, $_[0], $w[0] ? "" : "checked",
991                 $text{'default'};
992 $rv .= sprintf "<input type=radio name=%s_smax value=1 %s> %s\n",
993                 $_[0], $w[0] eq 'max' ? "checked" : "", $text{'mod_core_max'};
994 $rv .= sprintf "<input type=radio name=%s_smax value=0 %s>\n",
995                 $_[0], !$w[0] || $w[0] eq 'max' ? "" : "checked";
996 $rv .= sprintf "<input name=%s_soft size=6 value='%s'>\n",
997                 $_[0], $w[0] eq 'max' ? '' : $w[0];
998 $rv .= "&nbsp;&nbsp;&nbsp;";
999
1000 $rv .= sprintf "<b>%s</b> <input type=radio name=%s_hmax value=2 %s> %s\n",
1001                 $text{'mod_core_hard'}, $_[0], $w[1] ? "" : "checked",
1002                 $text{'default'};
1003 $rv .= sprintf "<input type=radio name=%s_hmax value=1 %s> %s\n",
1004                 $_[0], $w[1] eq 'max' ? "checked" : "", $text{'mod_core_max'};
1005 $rv .= sprintf "<input type=radio name=%s_hmax value=0 %s>\n",
1006                 $_[0], !$w[1] || $w[1] eq 'max' ? "" : "checked";
1007 $rv .= sprintf "<input name=%s_hard size=6 value='%s'>\n",
1008                 $_[0], $w[1] eq 'max' ? '' : $w[1];
1009 return (2, $_[1], $rv);
1010 }
1011
1012 # parse_rlimit(name, desc)
1013 sub parse_rlimit
1014 {
1015 if ($in{"$_[0]_smax"} == 2) {
1016         return ( [ ] );
1017         }
1018 local @v;
1019 if ($in{"$_[0]_smax"} == 1) {
1020         push(@v, "max");
1021         }
1022 else {
1023         $in{"$_[0]_soft"} =~ /^(\d+)(G|M|K|B)?$/i ||
1024                 &error(&text('mod_core_esoft', $_[1]));
1025         push(@v, $in{"$_[0]_soft"});
1026         }
1027 if ($in{"$_[0]_hmax"} == 1) {
1028         push(@v, "max");
1029         }
1030 elsif ($in{"$_[0]_hmax"} == 0) {
1031         $in{"$_[0]_hard"} =~ /^(\d+)(G|M|K|B)?$/i ||
1032                 &error(&text('mod_core_ehard', $_[1]));
1033         push(@v, $in{"$_[0]_hard"});
1034         }
1035 return ( [ join(" ", @v) ] );
1036 }
1037
1038 sub edit_ScoreboardPath
1039 {
1040 return (2, $text{'mod_core_score'},
1041         &opt_input($_[0]->{'words'}->[0], "ScoreboardPath", $text{'default'},
1042                    50, &file_chooser_button("ScoreboardPath")));
1043 }
1044 sub save_ScoreboardPath
1045 {
1046 return &parse_opt("ScoreboardPath", '^\/\S+$', $text{'mod_core_escore'});
1047 }
1048
1049 sub edit_ServerAdmin
1050 {
1051 return (2, $text{'mod_core_admin'},
1052         &opt_input($_[0]->{'words'}->[0], "ServerAdmin", $text{'default'}, 40));
1053 }
1054 sub save_ServerAdmin
1055 {
1056 return &parse_opt("ServerAdmin", '^\S+\@\S+$', $text{'mod_core_eadmin'});
1057 }
1058
1059 sub edit_ServerIdent
1060 {
1061 local @w = @{$_[0]->{'words'}};
1062 local $rv = sprintf "<input type=radio name=ServerIdent_m value=0 %s> %s\n",
1063         $_[0] ? "" : "checked", $text{'default'};
1064 $rv .= sprintf "<input type=radio name=ServerIdent_m value=1 %s> %s\n",
1065         lc($w[0]) eq 'off' ? "checked" : "", $text{'mod_core_none'};
1066 $rv .= sprintf "<input type=radio name=ServerIdent_m value=2 %s> %s\n",
1067         lc($w[0]) eq 'on' && !$w[1] ? "checked" : "",
1068         $text{'mod_core_identmsg_def'};
1069 $rv .= sprintf "<input type=radio name=ServerIdent_m value=3 %s>\n",
1070         lc($w[0]) eq 'on' && $w[1] ? "checked" : "";
1071 $rv .= sprintf "<input name=ServerIdent size=30 value='%s'>\n",
1072         lc($w[0]) eq 'on' ? $w[1] : "";
1073 return (2, $text{'mod_core_identmsg'}, $rv);
1074 }
1075 sub save_ServerIdent
1076 {
1077 if ($in{'ServerIdent_m'} == 0) {
1078         return ( [ ] );
1079         }
1080 elsif ($in{'ServerIdent_m'} == 1) {
1081         return ( [ "off" ] );
1082         }
1083 elsif ($in{'ServerIdent_m'} == 2) {
1084         return ( [ "on" ] );
1085         }
1086 else {
1087         return ( [ "on \"$in{'ServerIdent'}\"" ] );
1088         }
1089 }
1090
1091 sub edit_ServerName
1092 {
1093 return (2, $text{'mod_core_servername'},
1094         &opt_input($_[0]->{'words'}->[0], "ServerName", $text{'default'}, 50));
1095 }
1096 sub save_ServerName
1097 {
1098 return &parse_opt("ServerName", '\S', $text{'mod_core_eservername'});
1099 }
1100
1101 sub edit_ServerType
1102 {
1103 return (1, $text{'mod_core_type'},
1104         &select_input($_[0]->{'value'}, "ServerType", "",
1105                       "$text{'mod_core_inetd'},inetd",
1106                       "$text{'mod_core_stand'},standalone",
1107                       "$text{'default'},"));
1108 }
1109 sub save_ServerType
1110 {
1111 return &parse_choice("ServerType", "");
1112 }
1113
1114 sub edit_ShowSymlinks
1115 {
1116 return (1, $text{'mod_core_links'},
1117         &choice_input($_[0]->{'value'}, "ShowSymlinks", "",
1118                       "$text{'yes'},on", "$text{'no'},off",
1119                       "$text{'default'},"));
1120 }
1121 sub save_ShowSymlinks
1122 {
1123 return &parse_choice("ShowSymlinks", "");
1124 }
1125
1126 sub edit_SocketBindTight
1127 {
1128 return (1, $text{'mod_core_tight'},
1129         &choice_input($_[0]->{'value'}, "SocketBindTight", "",
1130                       "$text{'yes'},on", "$text{'no'},off",
1131                       "$text{'default'},"));
1132 }
1133 sub save_SocketBindTight
1134 {
1135 return &parse_choice("SocketBindTight", "");
1136 }
1137
1138 sub edit_SyslogFacility
1139 {
1140 local @facils = map { "$_,$_" } ( 'auth', 'authpriv', 'cron', 'daemon', 'kern', 'lpr', 'mail', 'news', 'user', 'uucp', 'local0', 'local1', 'local2', 'local3', 'local4', 'local5', 'local6', 'local7' );
1141 return (1, $text{'mod_core_facility'},
1142         &select_input($_[0]->{'value'}, "SyslogFacility", "",
1143                       "$text{'default'},", @facils));
1144 }
1145 sub save_SyslogFacility
1146 {
1147 return &parse_select("SyslogFacility", "");
1148 }
1149
1150 sub edit_SyslogLevel
1151 {
1152 local @levels = map { "$_,$_" } ( 'emerg', 'alert', 'crit', 'error', 'warn', 'notice', 'info', 'debug' );
1153 return (1, $text{'mod_core_level'},
1154         &select_input($_[0]->{'value'}, "SyslogLevel", "",
1155                       "$text{'default'},", @levels));
1156 }
1157 sub save_SyslogLevel
1158 {
1159 return &parse_select("SyslogLevel", "");
1160 }
1161
1162 sub edit_TransferLog
1163 {
1164 local $mode = $_[0]->{'value'} eq 'NONE' ? 2 :
1165               $_[0]->{'value'} ? 0 : 1;
1166 local $rv = sprintf "<input type=radio name=TransferLog_def value=1 %s> %s\n",
1167                 $mode == 1 ? "checked" : "", $text{'default'};
1168 if ($_[1]->{'version'} >= 1.17) {
1169         $rv .= sprintf"<input type=radio name=TransferLog_def value=2 %s> %s\n",
1170                 $mode == 2 ? "checked" : "", $text{'mod_core_nowhere'};
1171         }
1172 $rv .= sprintf "<input type=radio name=TransferLog_def value=0 %s>\n",
1173                 $mode == 0 ? "checked" : "";
1174 $rv .= sprintf "<input name=TransferLog size=50 value='%s'>\n",
1175                 $mode == 0 ? $_[0]->{'value'} : "";
1176 return (2, $text{'mod_core_tlog'}, $rv);
1177 }
1178 sub save_TransferLog
1179 {
1180 if ($in{'TransferLog_def'} == 1) {
1181         return ( [ ] );
1182         }
1183 elsif ($in{'TransferLog_def'} == 2) {
1184         return ( [ 'NONE' ] );
1185         }
1186 else {
1187         $in{'TransferLog'} =~ /^\/\S+$/ || &error($text{'mod_core_etlog'}); 
1188         return ( [ $in{'TransferLog'} ] );
1189         }
1190 }
1191
1192 sub edit_tcpBackLog
1193 {
1194 return (1, $text{'mod_core_backlog'},
1195         &opt_input($_[0]->{'value'}, "tcpBackLog", $text{'default'}, 6));
1196 }
1197 sub save_tcpBackLog
1198 {
1199 return &parse_opt("tcpBackLog", '^\d+$', $text{'mod_core_ebacklog'});
1200 }
1201
1202 sub edit_tcpNoDelay
1203 {
1204 return (1, $text{'mod_core_nodelay'},
1205         &choice_input($_[0]->{'value'}, "tcpNoDelay", "",
1206                       "$text{'yes'},on", "$text{'no'},off",
1207                       "$text{'default'},"));
1208 }
1209 sub save_tcpNoDelay
1210 {
1211 return &parse_choice("tcpNoDelay", "");
1212 }
1213
1214 sub edit_tcpReceiveWindow
1215 {
1216 return (1, $text{'mod_core_rwindow'},
1217         &opt_input($_[0]->{'value'}, "tcpReceiveWindow", $text{'default'}, 6));
1218 }
1219 sub save_tcpReceiveWindow
1220 {
1221 return &parse_opt("tcpReceiveWindow", '^\d+$', $text{'mod_core_erwindow'});
1222 }
1223
1224 sub edit_tcpSendWindow
1225 {
1226 return (1, $text{'mod_core_swindow'},
1227         &opt_input($_[0]->{'value'}, "tcpSendWindow", $text{'default'}, 6));
1228 }
1229 sub save_tcpSendWindow
1230 {
1231 return &parse_opt("tcpSendWindow", '^\d+$', $text{'mod_core_eswindow'});
1232 }
1233
1234 sub edit_TimesGMT
1235 {
1236 return (1, $text{'mod_core_gmt'},
1237         &choice_input($_[0]->{'value'}, "TimesGMT", "",
1238                       "$text{'yes'},on", "$text{'no'},off",
1239                       "$text{'default'},"));
1240 }
1241 sub save_TimesGMT
1242 {
1243 return &parse_choice("TimesGMT", "");
1244 }
1245
1246 sub edit_TimeoutIdle
1247 {
1248 return (1, $text{'mod_core_tidle'},
1249         &opt_input($_[0]->{'value'}, "TimeoutIdle", $text{'default'}, 6,
1250                    $text{'mod_core_secs'}));
1251 }
1252 sub save_TimeoutIdle
1253 {
1254 return &parse_opt("TimeoutIdle", '^\d+$', $text{'mod_core_etidle'});
1255 }
1256
1257 sub edit_TimeoutLogin
1258 {
1259 return (1, $text{'mod_core_tlogin'},
1260         &opt_input($_[0]->{'value'}, "TimeoutLogin", $text{'default'}, 6,
1261                    $text{'mod_core_secs'}));
1262 }
1263 sub save_TimeoutLogin
1264 {
1265 return &parse_opt("TimeoutLogin", '^\d+$', $text{'mod_core_etlogin'});
1266 }
1267
1268 sub edit_TimeoutNoTransfer
1269 {
1270 return (1, $text{'mod_core_ttransfer'},
1271         &opt_input($_[0]->{'value'}, "TimeoutNoTransfer", $text{'default'}, 6,
1272                    $text{'mod_core_secs'}));
1273 }
1274 sub save_TimeoutNoTransfer
1275 {
1276 return &parse_opt("TimeoutNoTransfer", '^\d+$', $text{'mod_core_ettransfer'});
1277 }
1278
1279 sub edit_TimeoutStalled
1280 {
1281 return (1, $text{'mod_core_tstalled'},
1282         &opt_input($_[0]->{'value'}, "TimeoutStalled", $text{'default'}, 6,
1283                    $text{'mod_core_secs'}));
1284 }
1285 sub save_TimeoutStalled
1286 {
1287 return &parse_opt("TimeoutStalled", '^\d+$', $text{'mod_core_etstalled'});
1288 }
1289
1290 sub edit_Umask
1291 {
1292 local $rv;
1293 $rv .= sprintf "<input type=radio name=Umask_def value=1 %s> %s\n",
1294         $_[0]->{'words'}->[0] ? "" : "checked", $text{'default'};
1295 $rv .= sprintf "<input type=radio name=Umask_def value=0 %s> %s\n",
1296         $_[0]->{'words'}->[0] ? "checked" : "", $text{'mod_core_octal'};
1297 $rv .= sprintf "<input name=Umask size=5 value='%s'>\n",
1298         $_[0]->{'words'}->[0];
1299
1300 $rv .= "&nbsp;&nbsp;&nbsp;<b>$text{'mod_core_umask_d'}</b>\n";
1301 $rv .= sprintf "<input type=radio name=Umask_d_def value=1 %s> %s\n",
1302         $_[0]->{'words'}->[1] ? "" : "checked", $text{'default'};
1303 $rv .= sprintf "<input type=radio name=Umask_d_def value=0 %s> %s\n",
1304         $_[0]->{'words'}->[1] ? "checked" : "", $text{'mod_core_octal'};
1305 $rv .= sprintf "<input name=Umask_d size=5 value='%s'>\n",
1306         $_[0]->{'words'}->[1];
1307
1308 return (2, $text{'mod_core_umask'}, $rv);
1309 }
1310 sub save_Umask
1311 {
1312 if ($in{'Umask_def'}) {
1313         return ( [ ] );
1314         }
1315 else {
1316         $in{'Umask'} =~ /^[0-7]{3}$/ || &error($text{'mod_core_eumask'});
1317         if ($in{'Umask_d_def'}) {
1318                 return ( [ $in{'Umask'} ] );
1319                 }
1320         else {
1321                 $in{'Umask_d'} =~ /^[0-7]{3}$/ || &error($text{'mod_core_eumask'});
1322                 return ( [ $in{'Umask'}." ".$in{'Umask_d'} ] );
1323                 }
1324         }
1325 }
1326
1327 sub edit_UseFtpUsers
1328 {
1329 return (1, $text{'mod_core_ftpusers'},
1330         &choice_input($_[0]->{'value'}, "UseFtpUsers", "",
1331                       "$text{'yes'},on", "$text{'no'},off",
1332                       "$text{'default'},"));
1333 }
1334 sub save_UseFtpUsers
1335 {
1336 return &parse_choice("UseFtpUsers", "");
1337 }
1338
1339 sub edit_UseHostsAllowFile
1340 {
1341 return (2, $text{'mod_core_hostsallow'},
1342         &opt_input($_[0]->{'value'}, "UseHostsAllowFile", $text{'default'}, 50,
1343                    &file_chooser_button("UseHostsAllowFile")));
1344 }
1345 sub save_UseHostsAllowFile
1346 {
1347 $in{'UseHostsAllowFile_def'} || -r $in{'UseHostsAllowFile'} ||
1348         &error($text{'mod_core_ehostsallow'});
1349 return &parse_opt("UseHostsAllowFile");
1350 }
1351
1352 sub edit_UseHostsDenyFile
1353 {
1354 return (2, $text{'mod_core_hostsdeny'},
1355         &opt_input($_[0]->{'value'}, "UseHostsDenyFile", $text{'default'}, 50,
1356                    &file_chooser_button("UseHostsDenyFile")));
1357 }
1358 sub save_UseHostsDenyFile
1359 {
1360 $in{'UseHostsDenyFile_def'} || -r $in{'UseHostsDenyFile'} ||
1361         &error($text{'mod_core_ehostsdeny'});
1362 return &parse_opt("UseHostsDenyFile");
1363 }
1364
1365 sub edit_UseReverseDNS
1366 {
1367 return (1, $text{'mod_core_revdns'},
1368         &choice_input($_[0]->{'value'}, "UseReverseDNS", "",
1369                       "$text{'yes'},on", "$text{'no'},off",
1370                       "$text{'default'},"));
1371 }
1372 sub save_UseReverseDNS
1373 {
1374 return &parse_choice("UseReverseDNS", "");
1375 }
1376
1377 sub edit_UserDirRoot
1378 {
1379 return (1, $text{'mod_core_userdir'},
1380         &choice_input($_[0]->{'value'}, "UserDirRoot", "",
1381                       "$text{'yes'},on", "$text{'no'},off",
1382                       "$text{'default'},"));
1383 }
1384 sub save_UserDirRoot
1385 {
1386 return &parse_choice("UserDirRoot", "");
1387 }
1388
1389 sub edit_User
1390 {
1391 local($rv, @uinfo);
1392 $rv = sprintf "<input type=radio name=User value=0 %s> $text{'default'}\n",
1393        $_[0] ? "" : "checked";
1394 $rv .= sprintf "<input type=radio name=User value=1 %s> %s\n",
1395         $_[0] && $_[0]->{'value'} !~ /^#/ ? "checked" : "",
1396         $text{'mod_core_uname'};
1397 $rv .= sprintf "<input name=User_name size=8 value=\"%s\"> %s&nbsp;\n",
1398         $_[0]->{'value'} !~ /^#/ ? $_[0]->{'value'} : "",
1399         &user_chooser_button("User_name", 0);
1400 $rv .= sprintf "<input type=radio name=User value=2 %s> %s\n",
1401         $_[0]->{'value'} =~ /^#/ ? "checked" : "",
1402         $text{'mod_core_uid'};
1403 $rv .= sprintf "<input name=User_id size=6 value=\"%s\">\n",
1404          $_[0]->{'value'} =~ /^#(.*)$/ ? $1 : "";
1405 return (2, $text{'mod_core_user'}, $rv);
1406 }
1407 sub save_User
1408 {
1409 if ($in{'User'} == 0) { return ( [ ] ); }
1410 elsif ($in{'User'} == 1) { return ( [ $in{'User_name'} ] ); }
1411 elsif ($in{'User_id'} !~ /^\-?\d+$/) {
1412         &error(&text('core_egid', $in{'User_id'}));
1413         }
1414 else { return ( [ "#$in{'User_id'}" ] ); }
1415 }
1416
1417 sub edit_UserAlias
1418 {
1419 local $rv = "<table border>\n".
1420             "<tr $tb> <td><b>$text{'mod_core_afrom'}</b></td> ".
1421             "<td><b>$text{'mod_core_ato'}</b></td> </tr>\n";
1422 local $i = 0;
1423 foreach $u (@{$_[0]}, { }) {
1424         local @w = @{$u->{'words'}};
1425         $rv .= "<tr $cb>\n";
1426         $rv .= "<td><input name=UserAlias_f_$i size=15 value='$w[0]'></td>\n";
1427         $rv .= "<td><input name=UserAlias_t_$i size=15 value='$w[1]'></td>\n";
1428         $rv .= "</tr>\n";
1429         $i++;
1430         }
1431 $rv .= "</table>\n";
1432 return (2, $text{'mod_core_ualias'}, $rv);
1433 }
1434 sub save_UserAlias
1435 {
1436 local @rv;
1437 for($i=0; defined($in{"UserAlias_f_$i"}); $i++) {
1438         next if (!$in{"UserAlias_f_$i"});
1439         $in{"UserAlias_f_$i"} =~ /^\S+$/ || &error($text{'mod_core_eafrom'});
1440         $in{"UserAlias_t_$i"} =~ /^\S+$/ || &error($text{'mod_core_eato'});
1441         push(@rv, $in{"UserAlias_f_$i"}.' '.$in{"UserAlias_t_$i"});
1442         }
1443 return ( \@rv );
1444 }
1445
1446 sub edit_UserOwner
1447 {
1448 return (1, $text{'mod_core_uowner'},
1449         &opt_input($_[0]->{'value'}, "UserOwner", $text{'default'}, 13,
1450                    &user_chooser_button("UserOwner")));
1451 }
1452 sub save_UserOwner
1453 {
1454 if ($in{'UserOwner_def'}) { return ( [ ] ); }
1455 else {
1456         getpwnam($in{'UserOwner'}) || &error($text{'mod_core_euowner'});
1457         return ( [ $in{'UserOwner'} ] );
1458         }
1459 }
1460
1461 sub edit_UserPassword
1462 {
1463 local $rv = "<table border>\n";
1464 $rv .= "<tr $tb> <td><b>$text{'mod_core_upname'}</b></td> ".
1465        "<td><b>$text{'mod_core_uppass'}</b></td> </tr>\n";
1466 local $i = 0;
1467 foreach $u (@{$_[0]}) {
1468         local @v = @{$u->{'words'}};
1469         $rv .= "<tr $cb>\n";
1470         $rv .= "<td><input name=UserPassword_n_$i size=13 value='$v[0]'></td>\n";
1471         $rv .= "<td><input type=radio name=UserPassword_d_$i value='$v[1]' checked> $text{'mod_core_updef'}\n";
1472         $rv .= "<input type=radio name=UserPassword_d_$i value=0>\n";
1473         $rv .= "<input name=UserPassword_p_$i size=25></td> </tr>\n";
1474         $i++;
1475         }
1476 $rv .= "<tr $cb>\n".
1477        "<td><input name=UserPassword_n_$i size=13></td>\n".
1478        "<td><input name=UserPassword_p_$i size=35></td>\n".
1479        "</tr> </table>\n";
1480 return (2, $text{'mod_core_userpassword'}, $rv);
1481 }
1482 sub save_UserPassword
1483 {
1484 local @rv;
1485 for($i=0; defined($in{"UserPassword_n_$i"}); $i++) {
1486         next if (!$in{"UserPassword_n_$i"});
1487         scalar(getpwnam($in{"UserPassword_n_$i"})) ||
1488                 &error($text{'mod_core_eupname'});
1489         if ($in{"UserPassword_d_$i"}) {
1490                 push(@rv, $in{"UserPassword_n_$i"}.' '.
1491                           $in{"UserPassword_d_$i"});
1492                 }
1493         else {
1494                 $salt = substr(time(), 0, 2);
1495                 push(@rv, $in{"UserPassword_n_$i"}.' '.
1496                           &unix_crypt($in{"UserPassword_p_$i"}, $salt));
1497                 }
1498         }
1499 return ( \@rv );
1500 }
1501
1502 sub edit_WtmpLog
1503 {
1504 return (1, $text{'mod_core_wtmp'},
1505         &choice_input($_[0]->{'value'}, "WtmpLog", "",
1506                       "$text{'yes'},on", "$text{'no'},off",
1507                       "$text{'mod_core_none'},NONE", "$text{'default'},"));
1508 }
1509 sub save_WtmpLog
1510 {
1511 return &parse_choice("WtmpLog", "");
1512 }
1513