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
13sub mod_core_directives
14{
15local $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	];
106return &make_directives($rv, $_[0], "mod_core");
107}
108
109sub edit_AccessDenyMsg
110{
111return (1, $text{'mod_core_accessdeny'},
112	&opt_input($_[0]->{'words'}->[0], "AccessDenyMsg", $text{'default'}, 20));
113}
114sub save_AccessDenyMsg
115{
116return &parse_opt("AccessDenyMsg");
117}
118
119sub edit_AccessGrantMsg
120{
121return (1, $text{'mod_core_accessgrant'},
122	&opt_input($_[0]->{'words'}->[0], "AccessGrantMsg", $text{'default'}, 20));
123}
124sub save_AccessGrantMsg
125{
126return &parse_opt("AccessGrantMsg");
127}
128
129sub edit_Allow_Deny_Order
130{
131local (@type, @what, @mode, $i);
132foreach $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	}
146push(@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);
156for($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";
166return (2, $text{'mod_core_allow_deny'}, $rv);
167}
168sub save_Allow_Deny_Order
169{
170local ($i, $type, $mode, $what, @allow, @deny);
171for($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	}
193return ( \@allow, \@deny, &parse_choice("order", ""));
194}
195
196sub edit_AllowAll_DenyAll
197{
198#local $a = @{$_[0]}, $d = @{$_[1]};
199local $a = $_[0], $d = $_[1];
200local $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'};
206return (1, $text{'mod_core_adall'}, $rv);
207}
208sub save_AllowAll_DenyAll
209{
210return $in{'AllowAll'} == 0 ? ( [ ], [ ] ) :
211       $in{'AllowAll'} == 1 ? ( [ "" ], [ ] ) : ( [ ], [ "" ] );
212}
213
214sub edit_AllowFilter
215{
216return (1, $text{'mod_core_filter'},
217	&opt_input($_[0]->{'value'}, "AllowFilter", $text{'default'}, 15));
218}
219sub save_AllowFilter
220{
221return &parse_opt("AllowFilter");
222}
223
224sub edit_AllowForeignAddress
225{
226return (1, $text{'mod_core_foreign'},
227	&choice_input($_[0]->{'value'}, "AllowForeignAddress", "",
228		      "$text{'yes'},on", "$text{'no'},off",
229		      "$text{'default'},"));
230}
231sub save_AllowForeignAddress
232{
233return &parse_choice("AllowForeignAddress", "");
234}
235
236sub edit_AllowGroup
237{
238local $v = @{$_[0]} ? join(" ", (map { $_->{'value'} } @{$_[0]})) : undef;
239return (2, $text{'mod_core_agroup'},
240	&opt_input($v, "AllowGroup", $text{'mod_core_all'}, 50));
241}
242sub save_AllowGroup
243{
244return ( $in{'AllowGroup_def'} ? [ ] : [ split(/\s+/, $in{'AllowGroup'}) ] );
245}
246
247sub edit_AllowOverwrite
248{
249return (1, $text{'mod_core_overwrite'},
250	&choice_input($_[0]->{'value'}, "AllowOverwrite", "",
251		      "$text{'yes'},on", "$text{'no'},off",
252		      "$text{'default'},"));
253}
254sub save_AllowOverwrite
255{
256return &parse_choice("AllowOverwrite", "");
257}
258
259sub edit_AllowRetrieveRestart
260{
261return (1, $text{'mod_core_restart'},
262	&choice_input($_[0]->{'value'}, "AllowRetrieveRestart", "",
263		      "$text{'yes'},on", "$text{'no'},off",
264		      "$text{'default'},"));
265}
266sub save_AllowRetrieveRestart
267{
268return &parse_choice("AllowRetrieveRestart", "");
269}
270
271sub edit_AllowStoreRestart
272{
273return (1, $text{'mod_core_restart2'},
274	&choice_input($_[0]->{'value'}, "AllowStoreRestart", "",
275		      "$text{'yes'},on", "$text{'no'},off",
276		      "$text{'default'},"));
277}
278sub save_AllowStoreRestart
279{
280return &parse_choice("AllowStoreRestart", "");
281}
282
283sub edit_AllowUser
284{
285local $v = @{$_[0]} ? join(" ", (map { $_->{'value'} } @{$_[0]})) : undef;
286return (2, $text{'mod_core_auser'},
287	&opt_input($v, "AllowUser", $text{'mod_core_all'}, 50));
288}
289sub save_AllowUser
290{
291return ( $in{'AllowUser_def'} ? [ ] : [ split(/\s+/, $in{'AllowUser'}) ] );
292}
293
294sub edit_AnonRequirePassword
295{
296return (1, $text{'mod_core_require'},
297	&choice_input($_[0]->{'value'}, "AnonRequirePassword", "",
298		      "$text{'yes'},on", "$text{'no'},off",
299		      "$text{'default'},"));
300}
301sub save_AnonRequirePassword
302{
303return &parse_choice("AnonRequirePassword", "");
304}
305
306sub edit_AnonymousGroup
307{
308return (2, $text{'mod_core_anongroup'},
309	&opt_input($_[0]->{'value'}, "AnonymousGroup", $text{'default'}, 50));
310
311}
312sub save_AnonymousGroup
313{
314return &parse_opt("AnonymousGroup", '\S', $text{'mod_core_eanongroup'});
315}
316
317sub edit_AuthAliasOnly
318{
319return (1, $text{'mod_core_authalias'},
320	&choice_input($_[0]->{'value'}, "AuthAliasOnly", "",
321		      "$text{'yes'},on", "$text{'no'},off",
322		      "$text{'default'},"));
323}
324sub save_AuthAliasOnly
325{
326return &parse_choice("AuthAliasOnly", "");
327}
328
329sub edit_AuthUsingAlias
330{
331return (1, $text{'mod_core_authusingalias'},
332	&choice_input($_[0]->{'value'}, "AuthUsingAlias", "",
333		      "$text{'yes'},on", "$text{'no'},off",
334		      "$text{'default'},"));
335}
336sub save_AuthUsingAlias
337{
338return &parse_choice("AuthUsingAlias", "");
339}
340
341sub edit_Bind
342{
343return (1, $text{'mod_core_bind'},
344	&opt_input($_[0]->{'value'}, "Bind", $text{'mod_core_bind_all'}, 15));
345}
346sub save_Bind
347{
348return &parse_opt("Bind", '^(\d+)\.(\d+)\.(\d+)\.(\d+)|([0-9:]+)$',
349		  $text{'mod_core_ebind'});
350}
351
352sub edit_DefaultAddress
353{
354return (1, $text{'mod_core_bind'},
355	&opt_input($_[0]->{'value'}, "DefaultAddress", $text{'mod_core_bind_all'}, 15));
356}
357sub save_DefaultAddress
358{
359$in{'DefaultAddress_def'} || &to_ipaddress($in{'DefaultAddress'}) ||
360	&to_ip6address($in{'DefaultAddress'}) ||
361	&error(text{'mod_core_ebind'});
362return &parse_opt("DefaultAddress", '^\S+$',
363		  $text{'mod_core_ebind'});
364}
365
366sub edit_CDPath
367{
368local $rv = "<textarea rows=3 cols=50 name=CDPath>";
369foreach $p (@{$_[0]}) {
370	$rv .= "$p->{'value'}\n";
371	}
372$rv .= "</textarea>\n";
373return (2, $text{'mod_core_cdpath'}, $rv);
374}
375sub save_CDPath
376{
377$in{'CDPath'} =~ s/\r//g;
378return ( [ split(/\s+/, $in{'CDPath'}) ] );
379}
380
381sub edit_Class_Classes
382{
383local $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";
390local $i = 0;
391foreach $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</option>\n",
397		$w[1] eq 'limit' ? 'selected' : '', $text{'mod_core_climit'};
398	$rv .= sprintf "<option value=regex %s>%s</option>\n",
399		$w[1] eq 'regex' ? 'selected' : '', $text{'mod_core_cregex'};
400	$rv .= sprintf "<option value=ip %s>%s</option>\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";
408return (2, $text{'mod_core_cls'}, $rv);
409}
410sub save_Class_Classes
411{
412local ($i, @rv);
413for($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	}
427return ( \@rv, $in{'Classes'} eq 'on' ? [ 'on' ] :
428	       $in{'Classes'} eq 'off' ? [ 'off' ] : [ ] );
429}
430
431sub edit_CommandBufferSize
432{
433return (1, $text{'mod_core_buffer'},
434	&opt_input($_[0]->{'value'}, "CommandBufferSize", $text{'default'}, 5));
435}
436sub save_CommandBufferSize
437{
438return &parse_opt("CommandBufferSize", '^\d+$', $text{'mod_core_ebuffer'});
439}
440
441sub edit_DefaultServer
442{
443return (1, $text{'mod_core_defaultserver'},
444	&choice_input($_[0]->{'value'}, "DefaultServer", "off",
445		      "$text{'yes'},on",
446		      "$text{'no'},off"));
447}
448sub save_DefaultServer
449{
450return &parse_choice("DefaultServer", "off");
451}
452
453sub edit_DefaultTransferMode
454{
455return (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}
461sub save_DefaultTransferMode
462{
463return &parse_choice("DefaultTransferMode", "");
464}
465
466sub edit_DeferWelcome
467{
468return (1, $text{'mod_core_defer'},
469	&choice_input($_[0]->{'value'}, "DeferWelcome", "",
470		      "$text{'yes'},on", "$text{'no'},off",
471		      "$text{'default'},"));
472}
473sub save_DeferWelcome
474{
475return &parse_choice("DeferWelcome", "");
476}
477
478sub edit_DeleteAbortedStores
479{
480return (1, $text{'mod_core_aborted'},
481	&choice_input($_[0]->{'value'}, "DeleteAbortedStores", "",
482		      "$text{'yes'},on", "$text{'no'},off",
483		      "$text{'default'},"));
484}
485sub save_DeleteAbortedStores
486{
487return &parse_choice("DeleteAbortedStores", "");
488}
489
490sub edit_DenyFilter
491{
492return (1, $text{'mod_core_dfilter'},
493	&opt_input($_[0]->{'value'}, "DenyFilter", $text{'default'}, 15));
494}
495sub save_DenyFilter
496{
497return &parse_opt("DenyFilter");
498}
499
500sub edit_DenyGroup
501{
502local $v = @{$_[0]} ? join(" ", (map { $_->{'value'} } @{$_[0]})) : undef;
503return (2, $text{'mod_core_dgroup'},
504	&opt_input($v, "DenyGroup", $text{'mod_core_none'}, 50));
505}
506sub save_DenyGroup
507{
508return ( $in{'DenyGroup_def'} ? [ ] : [ split(/\s+/, $in{'DenyGroup'}) ] );
509}
510
511sub edit_DenyUser
512{
513local $v = @{$_[0]} ? join(" ", (map { $_->{'value'} } @{$_[0]})) : undef;
514return (2, $text{'mod_core_duser'},
515	&opt_input($v, "DenyUser", $text{'mod_core_none'}, 50));
516}
517sub save_DenyUser
518{
519return ( $in{'DenyUser_def'} ? [ ] : [ split(/\s+/, $in{'DenyUser'}) ] );
520}
521
522sub edit_DisplayConnect
523{
524return (2, $text{'mod_core_display'},
525	&opt_input($_[0]->{'value'}, "DisplayConnect",
526		   $text{'mod_core_none'}, 50));
527}
528sub save_DisplayConnect
529{
530return &parse_opt("DisplayConnect", '\S', $text{'mod_core_edisplay'});
531}
532
533sub edit_DisplayFirstChdir
534{
535return (1, $text{'mod_core_firstcd'},
536	&opt_input($_[0]->{'value'}, "DisplayFirstChdir",
537		   $text{'mod_core_none'}, 15));
538}
539sub save_DisplayFirstChdir
540{
541return &parse_opt("DisplayFirstChdir", '^\S+$', $text{'mod_core_efirstcd'});
542}
543
544sub edit_DisplayChdir
545{
546return (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}
552sub save_DisplayChdir
553{
554local @rv = &parse_opt("DisplayChdir", '^\S+$', $text{'mod_core_efirstcd'});
555if ($in{'DisplayChdir_always'}) {
556	$rv[0]->[0] .= ' true';
557	}
558return @rv;
559}
560
561sub edit_DisplayGoAway
562{
563return (2, $text{'mod_core_goaway'},
564	&opt_input($_[0]->{'value'}, "DisplayGoAway",
565		   $text{'mod_core_none'}, 50));
566}
567sub save_DisplayGoAway
568{
569return &parse_opt("DisplayGoAway", '\S', $text{'mod_core_egoaway'});
570}
571
572sub edit_DisplayLogin
573{
574return (2, $text{'mod_core_login'},
575	&opt_input($_[0]->{'value'}, "DisplayLogin",
576		   $text{'mod_core_none'}, 50));
577}
578sub save_DisplayLogin
579{
580return &parse_opt("DisplayLogin", '\S', $text{'mod_core_elogin'});
581}
582
583sub edit_DisplayQuit
584{
585return (2, $text{'mod_core_quit'},
586	&opt_input($_[0]->{'value'}, "DisplayQuit",
587		   $text{'mod_core_none'}, 50));
588}
589sub save_DisplayQuit
590{
591return &parse_opt("DisplayQuit", '\S', $text{'mod_core_equit'});
592}
593
594sub edit_Group
595{
596local($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 : "";
610return (2, $text{'mod_core_group'}, $rv);
611}
612sub save_Group
613{
614if ($in{'Group'} == 0) { return ( [ ] ); }
615elsif ($in{'Group'} == 1) { return ( [ $in{'Group_name'} ] ); }
616elsif ($in{'Group_id'} !~ /^\-?\d+$/) {
617	&error(&text('core_euid', $in{'Group_id'}));
618	}
619else { return ( [ "#$in{'Group_id'}" ] ); }
620}
621
622sub edit_GroupOwner
623{
624return (1, $text{'mod_core_gowner'},
625	&opt_input($_[0]->{'value'}, "GroupOwner", $text{'default'}, 13,
626		   &group_chooser_button("GroupOwner")));
627}
628sub save_GroupOwner
629{
630if ($in{'GroupOwner_def'}) { return ( [ ] ); }
631else {
632	defined(getgrnam($in{'GroupOwner'})) || &error($text{'mod_core_egowner'});
633	return ( [ $in{'GroupOwner'} ] );
634	}
635}
636
637sub edit_GroupPassword
638{
639local $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";
642local $i = 0;
643foreach $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";
656return (2, $text{'mod_core_grouppassword'}, $rv);
657}
658sub save_GroupPassword
659{
660local @rv;
661for($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	}
675return ( \@rv );
676}
677
678sub edit_HiddenStor
679{
680return (1, $text{'mod_core_hstor'},
681	&choice_input($_[0]->{'value'}, "HiddenStor", "",
682		      "$text{'yes'},on", "$text{'no'},off",
683		      "$text{'default'},"));
684}
685sub save_HiddenStor
686{
687return &parse_choice("HiddenStor", "");
688}
689
690sub edit_HiddenStores
691{
692return (1, $text{'mod_core_hstor'},
693	&choice_input($_[0]->{'value'}, "HiddenStores", "",
694		      "$text{'yes'},on", "$text{'no'},off",
695		      "$text{'default'},"));
696}
697sub save_HiddenStores
698{
699return &parse_choice("HiddenStores", "");
700}
701
702sub edit_HideGroup
703{
704return (2, $text{'mod_core_hgroup'},
705	sprintf "<input name=HideGroup size=50 value='%s'>",
706	 join(" ", map { $_->{'value'} } @{$_[0]}));
707}
708sub save_HideGroup
709{
710local @hg = split(/\s+/, $in{'HideGroup'});
711foreach $g (@hg) {
712	scalar(getgrnam($g)) || &error($text{'mod_core_ehgroup'});
713	}
714return ( \@hg );
715}
716
717sub edit_HideNoAccess
718{
719return (1, $text{'mod_core_hnoaccess'},
720	&choice_input($_[0]->{'value'}, "HideNoAccess", "",
721		      "$text{'yes'},on", "$text{'no'},off",
722		      "$text{'default'},"));
723}
724sub save_HideNoAccess
725{
726return &parse_choice("HideNoAccess", "");
727}
728
729sub edit_HideUser
730{
731return (2, $text{'mod_core_huser'},
732	sprintf "<input name=HideUser size=50 value='%s'>",
733	 join(" ", map { $_->{'value'} } @{$_[0]}));
734}
735sub save_HideUser
736{
737local @hu = split(/\s+/, $in{'HideUser'});
738foreach $u (@hu) {
739	defined(getpwnam($u)) || &error($text{'mod_core_ehuser'});
740	}
741return ( \@hu );
742}
743
744sub edit_IdentLookups
745{
746return (1, $text{'mod_core_ident'},
747	&choice_input($_[0]->{'value'}, "IdentLookups", "",
748		      "$text{'yes'},on", "$text{'no'},off",
749		      "$text{'default'},"));
750}
751sub save_IdentLookups
752{
753return &parse_choice("IdentLookups", "");
754}
755
756sub edit_IgnoreHidden
757{
758return (1, $text{'mod_core_ihidden'},
759	&choice_input($_[0]->{'value'}, "IgnoreHidden", "off",
760		      "$text{'yes'},on", "$text{'no'},off"));
761}
762sub save_IgnoreHidden
763{
764return &parse_choice("IgnoreHidden", "off");
765}
766
767sub edit_MasqueradeAddress
768{
769return (2, $text{'mod_core_masq'},
770	&opt_input($_[0]->{'value'}, "MasqueradeAddress",
771		   $text{'mod_core_masq_def'}, 30));
772}
773sub save_MasqueradeAddress
774{
775$in{'MasqueradeAddress_def'} || &to_ipaddress($in{'MasqueradeAddress'}) ||
776	&error($text{'mod_core_emasq'});
777return &parse_opt("MasqueradeAddress");
778}
779
780sub edit_MaxClients
781{
782return (2, $text{'mod_core_maxc'}, &edit_max($_[0], "MaxClients"));
783}
784sub save_MaxClients
785{
786return &save_max("MaxClients");
787}
788
789sub edit_MaxClientsPerHost
790{
791return (2, $text{'mod_core_maxch'}, &edit_max($_[0], "MaxClientsPerHost"));
792}
793sub save_MaxClientsPerHost
794{
795return &save_max("MaxClientsPerHost");
796}
797
798sub edit_MaxClientsPerUser
799{
800return (2, $text{'mod_core_maxcu'}, &edit_max($_[0], "MaxClientsPerUser"));
801}
802sub save_MaxClientsPerUser
803{
804return &save_max("MaxClientsPerUser");
805}
806
807sub edit_max
808{
809local $m = !$_[0] ? 0 :
810	   $_[0]->{'words'}->[0] eq 'none' ? 1 : 2;
811local $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];
821return $rv;
822}
823sub save_max
824{
825if ($in{"$_[0]_m"} == 0) {
826	return ( [ ] );
827	}
828else {
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
846sub edit_MaxInstances
847{
848return (1, $text{'mod_core_instances'},
849	&opt_input($_[0]->{'value'}, "MaxInstances", $text{'default'}, 4));
850}
851sub save_MaxInstances
852{
853return &parse_opt("MaxInstances", '^\d+$', $text{'mod_core_einstances'});
854}
855
856sub edit_MaxLoginAttempts
857{
858return (1, $text{'mod_core_logins'},
859	&opt_input($_[0]->{'value'}, "MaxLoginAttempts", $text{'default'}, 4));
860}
861sub save_MaxLoginAttempts
862{
863return &parse_opt("MaxLoginAttempts", '^\d+$', $text{'mod_core_elogins'});
864}
865
866sub edit_MultilineRFC2228
867{
868return (1, $text{'mod_core_rfc2228'},
869	&choice_input($_[0]->{'value'}, "MultilineRFC2228", "",
870		      "$text{'yes'},on", "$text{'no'},off",
871		      "$text{'default'},"));
872}
873sub save_MultilineRFC2228
874{
875return &parse_choice("MultilineRFC2228", "");
876}
877
878sub edit_PassivePorts
879{
880local $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];
888return (1, $text{'mod_core_pasv'}, $rv);
889}
890sub save_PassivePorts
891{
892if ($in{'PassivePorts_def'}) {
893	return ( [ ] );
894	}
895else {
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
902sub edit_PathAllowFilter
903{
904return (1, $text{'mod_core_pathallow'},
905	&opt_input($_[0]->{'words'}->[0], "PathAllowFilter",
906		   $text{'mod_core_any'}, 20));
907}
908sub save_PathAllowFilter
909{
910return &parse_opt("PathAllowFilter");
911}
912
913sub edit_PathDenyFilter
914{
915return (1, $text{'mod_core_pathdeny'},
916	&opt_input($_[0]->{'words'}->[0], "PathDenyFilter",
917		   $text{'mod_core_none'}, 20));
918}
919sub save_PathDenyFilter
920{
921return &parse_opt("PathDenyFilter");
922}
923
924sub edit_PidFile
925{
926return (2, $text{'mod_core_pidfile'},
927	&opt_input($_[0]->{'words'}->[0], "PidFile", $text{'default'}, 50,
928		   &file_chooser_button("PidFile")));
929}
930sub save_PidFile
931{
932return &parse_opt("PidFile", '^\/\S+$', $text{'mod_core_epidfile'});
933}
934
935sub edit_Port
936{
937return (1, $text{'mod_core_port'},
938	&opt_input($_[0]->{'value'}, "Port", $text{'default'}, 6));
939}
940sub save_Port
941{
942return &parse_opt("Port", '^\d+$', $text{'mod_core_eport'});
943}
944
945sub edit_RequireValidShell
946{
947return (1, $text{'mod_core_shell'},
948	&choice_input($_[0]->{'value'}, "RequireValidShell", "",
949		      "$text{'yes'},on", "$text{'no'},off",
950		      "$text{'default'},"));
951}
952sub save_RequireValidShell
953{
954return &parse_choice("RequireValidShell", "");
955}
956
957sub edit_RLimitCPU
958{
959return &rlimit_input("RLimitCPU", $text{'mod_core_cpulimit'}, $_[0]);
960}
961sub save_RLimitCPU
962{
963return &parse_rlimit("RLimitCPU", $text{'mod_core_ecpulimit'});
964}
965
966sub edit_RLimitMemory
967{
968return &rlimit_input("RLimitMemory", $text{'mod_core_memlimit'}, $_[0]);
969}
970sub save_RLimitMemory
971{
972return &parse_rlimit("RLimitMemory", $text{'mod_core_ememlimit'});
973}
974
975sub edit_RLimitOpenFiles
976{
977return &rlimit_input("RLimitOpenFiles", $text{'mod_core_filelimit'}, $_[0]);
978}
979sub save_RLimitOpenFiles
980{
981return &parse_rlimit("RLimitOpenFiles", $text{'mod_core_efilelimit'});
982}
983
984# rlimit_input(name, desc, value)
985sub rlimit_input
986{
987local @w = @{$_[2]->{'words'}};
988local $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];
1009return (2, $_[1], $rv);
1010}
1011
1012# parse_rlimit(name, desc)
1013sub parse_rlimit
1014{
1015if ($in{"$_[0]_smax"} == 2) {
1016	return ( [ ] );
1017	}
1018local @v;
1019if ($in{"$_[0]_smax"} == 1) {
1020	push(@v, "max");
1021	}
1022else {
1023	$in{"$_[0]_soft"} =~ /^(\d+)(G|M|K|B)?$/i ||
1024		&error(&text('mod_core_esoft', $_[1]));
1025	push(@v, $in{"$_[0]_soft"});
1026	}
1027if ($in{"$_[0]_hmax"} == 1) {
1028	push(@v, "max");
1029	}
1030elsif ($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	}
1035return ( [ join(" ", @v) ] );
1036}
1037
1038sub edit_ScoreboardPath
1039{
1040return (2, $text{'mod_core_score'},
1041	&opt_input($_[0]->{'words'}->[0], "ScoreboardPath", $text{'default'},
1042		   50, &file_chooser_button("ScoreboardPath")));
1043}
1044sub save_ScoreboardPath
1045{
1046return &parse_opt("ScoreboardPath", '^\/\S+$', $text{'mod_core_escore'});
1047}
1048
1049sub edit_ServerAdmin
1050{
1051return (2, $text{'mod_core_admin'},
1052	&opt_input($_[0]->{'words'}->[0], "ServerAdmin", $text{'default'}, 40));
1053}
1054sub save_ServerAdmin
1055{
1056return &parse_opt("ServerAdmin", '^\S+\@\S+$', $text{'mod_core_eadmin'});
1057}
1058
1059sub edit_ServerIdent
1060{
1061local @w = @{$_[0]->{'words'}};
1062local $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] : "";
1073return (2, $text{'mod_core_identmsg'}, $rv);
1074}
1075sub save_ServerIdent
1076{
1077if ($in{'ServerIdent_m'} == 0) {
1078	return ( [ ] );
1079	}
1080elsif ($in{'ServerIdent_m'} == 1) {
1081	return ( [ "off" ] );
1082	}
1083elsif ($in{'ServerIdent_m'} == 2) {
1084	return ( [ "on" ] );
1085	}
1086else {
1087	return ( [ "on \"$in{'ServerIdent'}\"" ] );
1088	}
1089}
1090
1091sub edit_ServerName
1092{
1093return (2, $text{'mod_core_servername'},
1094	&opt_input($_[0]->{'words'}->[0], "ServerName", $text{'default'}, 50));
1095}
1096sub save_ServerName
1097{
1098return &parse_opt("ServerName", '\S', $text{'mod_core_eservername'});
1099}
1100
1101sub edit_ServerType
1102{
1103return (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}
1109sub save_ServerType
1110{
1111return &parse_choice("ServerType", "");
1112}
1113
1114sub edit_ShowSymlinks
1115{
1116return (1, $text{'mod_core_links'},
1117	&choice_input($_[0]->{'value'}, "ShowSymlinks", "",
1118		      "$text{'yes'},on", "$text{'no'},off",
1119		      "$text{'default'},"));
1120}
1121sub save_ShowSymlinks
1122{
1123return &parse_choice("ShowSymlinks", "");
1124}
1125
1126sub edit_SocketBindTight
1127{
1128return (1, $text{'mod_core_tight'},
1129	&choice_input($_[0]->{'value'}, "SocketBindTight", "",
1130		      "$text{'yes'},on", "$text{'no'},off",
1131		      "$text{'default'},"));
1132}
1133sub save_SocketBindTight
1134{
1135return &parse_choice("SocketBindTight", "");
1136}
1137
1138sub edit_SyslogFacility
1139{
1140local @facils = map { "$_,$_" } ( 'auth', 'authpriv', 'cron', 'daemon', 'kern', 'lpr', 'mail', 'news', 'user', 'uucp', 'local0', 'local1', 'local2', 'local3', 'local4', 'local5', 'local6', 'local7' );
1141return (1, $text{'mod_core_facility'},
1142	&select_input($_[0]->{'value'}, "SyslogFacility", "",
1143		      "$text{'default'},", @facils));
1144}
1145sub save_SyslogFacility
1146{
1147return &parse_select("SyslogFacility", "");
1148}
1149
1150sub edit_SyslogLevel
1151{
1152local @levels = map { "$_,$_" } ( 'emerg', 'alert', 'crit', 'error', 'warn', 'notice', 'info', 'debug' );
1153return (1, $text{'mod_core_level'},
1154	&select_input($_[0]->{'value'}, "SyslogLevel", "",
1155		      "$text{'default'},", @levels));
1156}
1157sub save_SyslogLevel
1158{
1159return &parse_select("SyslogLevel", "");
1160}
1161
1162sub edit_TransferLog
1163{
1164local $mode = $_[0]->{'value'} eq 'NONE' ? 2 :
1165	      $_[0]->{'value'} ? 0 : 1;
1166local $rv = sprintf "<input type=radio name=TransferLog_def value=1 %s> %s\n",
1167		$mode == 1 ? "checked" : "", $text{'default'};
1168if ($_[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'} : "";
1176return (2, $text{'mod_core_tlog'}, $rv);
1177}
1178sub save_TransferLog
1179{
1180if ($in{'TransferLog_def'} == 1) {
1181	return ( [ ] );
1182	}
1183elsif ($in{'TransferLog_def'} == 2) {
1184	return ( [ 'NONE' ] );
1185	}
1186else {
1187	$in{'TransferLog'} =~ /^\/\S+$/ || &error($text{'mod_core_etlog'});
1188	return ( [ $in{'TransferLog'} ] );
1189	}
1190}
1191
1192sub edit_tcpBackLog
1193{
1194return (1, $text{'mod_core_backlog'},
1195	&opt_input($_[0]->{'value'}, "tcpBackLog", $text{'default'}, 6));
1196}
1197sub save_tcpBackLog
1198{
1199return &parse_opt("tcpBackLog", '^\d+$', $text{'mod_core_ebacklog'});
1200}
1201
1202sub edit_tcpNoDelay
1203{
1204return (1, $text{'mod_core_nodelay'},
1205	&choice_input($_[0]->{'value'}, "tcpNoDelay", "",
1206		      "$text{'yes'},on", "$text{'no'},off",
1207		      "$text{'default'},"));
1208}
1209sub save_tcpNoDelay
1210{
1211return &parse_choice("tcpNoDelay", "");
1212}
1213
1214sub edit_tcpReceiveWindow
1215{
1216return (1, $text{'mod_core_rwindow'},
1217	&opt_input($_[0]->{'value'}, "tcpReceiveWindow", $text{'default'}, 6));
1218}
1219sub save_tcpReceiveWindow
1220{
1221return &parse_opt("tcpReceiveWindow", '^\d+$', $text{'mod_core_erwindow'});
1222}
1223
1224sub edit_tcpSendWindow
1225{
1226return (1, $text{'mod_core_swindow'},
1227	&opt_input($_[0]->{'value'}, "tcpSendWindow", $text{'default'}, 6));
1228}
1229sub save_tcpSendWindow
1230{
1231return &parse_opt("tcpSendWindow", '^\d+$', $text{'mod_core_eswindow'});
1232}
1233
1234sub edit_TimesGMT
1235{
1236return (1, $text{'mod_core_gmt'},
1237	&choice_input($_[0]->{'value'}, "TimesGMT", "",
1238		      "$text{'yes'},on", "$text{'no'},off",
1239		      "$text{'default'},"));
1240}
1241sub save_TimesGMT
1242{
1243return &parse_choice("TimesGMT", "");
1244}
1245
1246sub edit_TimeoutIdle
1247{
1248return (1, $text{'mod_core_tidle'},
1249	&opt_input($_[0]->{'value'}, "TimeoutIdle", $text{'default'}, 6,
1250		   $text{'mod_core_secs'}));
1251}
1252sub save_TimeoutIdle
1253{
1254return &parse_opt("TimeoutIdle", '^\d+$', $text{'mod_core_etidle'});
1255}
1256
1257sub edit_TimeoutLogin
1258{
1259return (1, $text{'mod_core_tlogin'},
1260	&opt_input($_[0]->{'value'}, "TimeoutLogin", $text{'default'}, 6,
1261		   $text{'mod_core_secs'}));
1262}
1263sub save_TimeoutLogin
1264{
1265return &parse_opt("TimeoutLogin", '^\d+$', $text{'mod_core_etlogin'});
1266}
1267
1268sub edit_TimeoutNoTransfer
1269{
1270return (1, $text{'mod_core_ttransfer'},
1271	&opt_input($_[0]->{'value'}, "TimeoutNoTransfer", $text{'default'}, 6,
1272		   $text{'mod_core_secs'}));
1273}
1274sub save_TimeoutNoTransfer
1275{
1276return &parse_opt("TimeoutNoTransfer", '^\d+$', $text{'mod_core_ettransfer'});
1277}
1278
1279sub edit_TimeoutStalled
1280{
1281return (1, $text{'mod_core_tstalled'},
1282	&opt_input($_[0]->{'value'}, "TimeoutStalled", $text{'default'}, 6,
1283		   $text{'mod_core_secs'}));
1284}
1285sub save_TimeoutStalled
1286{
1287return &parse_opt("TimeoutStalled", '^\d+$', $text{'mod_core_etstalled'});
1288}
1289
1290sub edit_Umask
1291{
1292local $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
1308return (2, $text{'mod_core_umask'}, $rv);
1309}
1310sub save_Umask
1311{
1312if ($in{'Umask_def'}) {
1313	return ( [ ] );
1314	}
1315else {
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
1327sub edit_UseFtpUsers
1328{
1329return (1, $text{'mod_core_ftpusers'},
1330	&choice_input($_[0]->{'value'}, "UseFtpUsers", "",
1331		      "$text{'yes'},on", "$text{'no'},off",
1332		      "$text{'default'},"));
1333}
1334sub save_UseFtpUsers
1335{
1336return &parse_choice("UseFtpUsers", "");
1337}
1338
1339sub edit_UseHostsAllowFile
1340{
1341return (2, $text{'mod_core_hostsallow'},
1342	&opt_input($_[0]->{'value'}, "UseHostsAllowFile", $text{'default'}, 50,
1343		   &file_chooser_button("UseHostsAllowFile")));
1344}
1345sub save_UseHostsAllowFile
1346{
1347$in{'UseHostsAllowFile_def'} || -r $in{'UseHostsAllowFile'} ||
1348	&error($text{'mod_core_ehostsallow'});
1349return &parse_opt("UseHostsAllowFile");
1350}
1351
1352sub edit_UseHostsDenyFile
1353{
1354return (2, $text{'mod_core_hostsdeny'},
1355	&opt_input($_[0]->{'value'}, "UseHostsDenyFile", $text{'default'}, 50,
1356		   &file_chooser_button("UseHostsDenyFile")));
1357}
1358sub save_UseHostsDenyFile
1359{
1360$in{'UseHostsDenyFile_def'} || -r $in{'UseHostsDenyFile'} ||
1361	&error($text{'mod_core_ehostsdeny'});
1362return &parse_opt("UseHostsDenyFile");
1363}
1364
1365sub edit_UseReverseDNS
1366{
1367return (1, $text{'mod_core_revdns'},
1368	&choice_input($_[0]->{'value'}, "UseReverseDNS", "",
1369		      "$text{'yes'},on", "$text{'no'},off",
1370		      "$text{'default'},"));
1371}
1372sub save_UseReverseDNS
1373{
1374return &parse_choice("UseReverseDNS", "");
1375}
1376
1377sub edit_UserDirRoot
1378{
1379return (1, $text{'mod_core_userdir'},
1380	&choice_input($_[0]->{'value'}, "UserDirRoot", "",
1381		      "$text{'yes'},on", "$text{'no'},off",
1382		      "$text{'default'},"));
1383}
1384sub save_UserDirRoot
1385{
1386return &parse_choice("UserDirRoot", "");
1387}
1388
1389sub edit_User
1390{
1391local($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 : "";
1405return (2, $text{'mod_core_user'}, $rv);
1406}
1407sub save_User
1408{
1409if ($in{'User'} == 0) { return ( [ ] ); }
1410elsif ($in{'User'} == 1) { return ( [ $in{'User_name'} ] ); }
1411elsif ($in{'User_id'} !~ /^\-?\d+$/) {
1412	&error(&text('core_egid', $in{'User_id'}));
1413	}
1414else { return ( [ "#$in{'User_id'}" ] ); }
1415}
1416
1417sub edit_UserAlias
1418{
1419local $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";
1422local $i = 0;
1423foreach $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";
1432return (2, $text{'mod_core_ualias'}, $rv);
1433}
1434sub save_UserAlias
1435{
1436local @rv;
1437for($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	}
1443return ( \@rv );
1444}
1445
1446sub edit_UserOwner
1447{
1448return (1, $text{'mod_core_uowner'},
1449	&opt_input($_[0]->{'value'}, "UserOwner", $text{'default'}, 13,
1450		   &user_chooser_button("UserOwner")));
1451}
1452sub save_UserOwner
1453{
1454if ($in{'UserOwner_def'}) { return ( [ ] ); }
1455else {
1456	getpwnam($in{'UserOwner'}) || &error($text{'mod_core_euowner'});
1457	return ( [ $in{'UserOwner'} ] );
1458	}
1459}
1460
1461sub edit_UserPassword
1462{
1463local $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";
1466local $i = 0;
1467foreach $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";
1480return (2, $text{'mod_core_userpassword'}, $rv);
1481}
1482sub save_UserPassword
1483{
1484local @rv;
1485for($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	}
1499return ( \@rv );
1500}
1501
1502sub edit_WtmpLog
1503{
1504return (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}
1509sub save_WtmpLog
1510{
1511return &parse_choice("WtmpLog", "");
1512}
1513
1514