Handle hostnames with upper-case letters
[webmin.git] / dhcpd / save_options.cgi
1 #!/usr/local/bin/perl
2 # save_options.cgi
3 # Save client options for some subnet, shared net, group, host or global
4
5 require './dhcpd-lib.pl';
6 require './params-lib.pl';
7 &ReadParse();
8 &lock_file($config{'dhcpd_conf'});
9 $client = &get_parent_config();
10 push(@parents, $client);
11 foreach $i ($in{'sidx'}, $in{'uidx'}, $in{'gidx'}, $in{'idx'}) {
12         if ($i ne '') {
13                 $client = $client->{'members'}->[$i];
14                 push(@parents, $client);
15                 $indent++;
16                 }
17         }
18
19 # check acls
20 %access = &get_module_acl();
21 &error_setup($text{'eacl_aviol'});
22 if ($client->{'name'} eq 'subnet') {
23         &error("$text{'eacl_np'} $text{'eacl_pus'}")
24                 if !&can('rw', \%access, $client);
25         }
26 elsif ($client->{'name'} eq 'shared-network') {
27         &error("$text{'eacl_np'} $text{'eacl_pun'}")
28                 if !&can('rw', \%access, $client);
29         }
30 elsif ($client->{'name'} eq 'host') {
31         &error("$text{'eacl_np'} $text{'eacl_puh'}")
32                 if !&can('rw', \%access, $client);
33         }
34 elsif ($client->{'name'} eq 'group') {
35         &error("$text{'eacl_np'} $text{'eacl_pug'}")
36                 if !&can('rw', \%access, $client);
37         }
38 else {
39         &error("$text{'eacl_np'} $text{'eacl_pglob'}")
40                 if !$access{'global'};
41         }
42
43 # save
44 &error_setup($text{'sopt_failsave'});
45
46 &save_option("host-name", 3, $client, $indent);
47 &save_option("routers", 2, $client, $indent);
48 &save_option("subnet-mask", 0, $client, $indent);
49 &save_option("broadcast-address", 0, $client, $indent);
50 &save_option("domain-name", 3, $client, $indent);
51 &save_option("domain-name-servers", 2, $client, $indent);
52 &save_option("domain-search", 6, $client, $indent);
53 &save_option("time-servers", 2, $client, $indent);
54 &save_option("log-servers", 2, $client, $indent);
55 &save_option("swap-server", 2, $client, $indent);
56 &save_option("root-path", 3, $client, $indent);
57 &save_option("nis-domain", 3, $client, $indent);
58 &save_option("nis-servers", 2, $client, $indent);
59 &save_option("font-servers", 2, $client, $indent);
60 &save_option("x-display-manager", 2, $client, $indent);
61 &save_option("static-routes", 5, $client, $indent);
62 &save_option("ntp-servers", 2, $client, $indent);
63 &save_option("netbios-name-servers", 2, $client, $indent);
64 &save_option("netbios-scope", 3, $client, $indent);
65 &save_option("netbios-node-type", 1, $client, $indent);
66 &save_option("time-offset", 1, $client, $indent);
67 &save_option("dhcp-server-identifier", 2, $client, $indent);
68 &save_option("slp-directory-agent", 2, $client, $indent, 1);
69 &save_option("slp-service-scope", 3, $client, $indent, 1);
70 if ($in{'level'} eq "global") {
71         # save params as well
72         &save_choice("use-host-decl-names", $client, 0);
73         &parse_params($client, 0);
74         }
75 elsif ($in{'level'} eq "host") {
76         $ret="edit_host.cgi?sidx=$in{'sidx'}&uidx=$in{'uidx'}&gidx=$in{'gidx'}&idx=$in{'idx'}";
77         }
78 elsif ($in{'level'} eq "group") {
79         $ret="edit_group.cgi?sidx=$in{'sidx'}&uidx=$in{'uidx'}&idx=$in{'idx'}";
80         }
81 elsif ($in{'level'} eq "subnet") {
82         $ret="edit_subnet.cgi?sidx=$in{'sidx'}&idx=$in{'idx'}";
83         }
84 elsif ($in{'level'} eq "shared-network") {
85         $ret="edit_shared.cgi?idx=$in{'idx'}";
86         }
87
88 if ($config{'dhcpd_version'} >= 3) {
89         # Save option definitions, new DHCPd
90         @defs = grep { $_->{'name'} eq 'option' &&
91                          $_->{'values'}->[1] eq 'code' &&
92                          $_->{'values'}->[3] eq '=' }
93                        @{$client->{'members'}};
94         %optdef = map { $_->{'values'}->[0], $_ } @defs;
95         for($i=0; defined($in{"dname_$i"}); $i++) {
96                 next if (!$in{"dname_$i"} || !$in{"dnum_$i"} ||
97                          !$in{"dtype_$i"});
98                 $in{"dname_$i"} =~ /^[a-z0-9\.\-\_]+$/i ||
99                         &error(&text('sopt_edname', $in{"dname_$i"}));
100                 $in{"dnum_$i"} =~ /^\d+$/ ||
101                         &error(&text('sopt_ednum', $in{"dnum_$i"}));
102                 if ($in{"dtype_$i"} =~ /^[a-z0-9\s\.\-\_]+$/i) {
103                         @dtypes = ( $in{"dtype_$i"} );
104                         }
105                 elsif ($in{"dtype_$i"} =~ /^\{.*\}$/) {
106                         @dtypes = split(/\s+/, $in{"dtype_$i"});
107                         }
108                 else {
109                         &error(&text('sopt_edtype', $in{"dtype_$i"}));
110                         }
111                 push(@newdefs, { 'name' => 'option',
112                                  'values' => [ $in{"dname_$i"}, "code",
113                                                $in{"dnum_$i"}, "=",
114                                                @dtypes,
115                                              ] } );
116                 }
117         &save_directive($client, \@defs, \@newdefs, $indent, 1);
118
119         # Find option definitions at higher levels
120         foreach $p (@parents) {
121                 @popts = &find("option", $p->{'members'});
122                 @pdefs = grep { $_->{'values'}->[1] eq 'code' &&
123                                 $_->{'values'}->[3] eq '=' } @popts;
124                 foreach $o (@pdefs) {
125                         $optdef{$o->{'values'}->[0]} = $o
126                                 if ($o->{'values'}->[0]);
127                         }
128                 }
129
130         # Find the last definition
131         $maxdef = undef;
132         foreach $d (@newdefs) {
133                 $maxdef = $d if (!$maxdef || $d->{'line'} > $maxdef->{'line'});
134                 }
135
136         # Save custom options
137         @custom = grep { $_->{'name'} eq 'option' &&
138                          $optdef{$_->{'values'}->[0]} &&
139                          $_->{'values'}->[1] ne 'code' }
140                        @{$client->{'members'}};
141         for($i=0; defined($in{"cname_$i"}); $i++) {
142                 next if ($in{"cname_$i"} eq "");
143                 local $o = $optdef{$in{"cname_$i"}};
144                 local $cv = $in{"cval_$i"};
145                 $cv =~ /\S/ || &error(&text('sopt_ecval', $in{"cname_$i"}));
146                 if ($o && $o->{'values'}->[4] eq 'ip-address') {
147                         &check_ipaddress($cv) ||
148                           &check_ip6address($cv) ||
149                             &error(&text('sopt_ecip', $in{"cname_$i"}));
150                         }
151                 if ($o && $o->{'values'}->[4] eq 'string' ||
152                     $cv !~ /^([0-9a-fA-F]{1,2}:)*[0-9a-fA-F]{1,2}$/ &&
153                     !&check_ipaddress($cv)) {
154                         # Quote if type is a string, or unknown and not an IP
155                         $cv = "\"$cv\"";
156                         }
157                 push(@newcustom, { 'name' => 'option',
158                                    'values' => [ $in{"cname_$i"}, $cv ] } );
159                 }
160         &save_directive($client, \@custom, \@newcustom, $indent,
161                         $maxdef ? 0 : 1, $maxdef);
162         }
163 else {
164         # Save custom options, old DHCPd
165         @custom = grep { $_->{'name'} eq 'option' &&
166                          $_->{'values'}->[0] =~ /^option-(\S+)$/ &&
167                          $_->{'values'}->[1] ne 'code' }
168                        @{$client->{'members'}};
169         for($i=0; defined($in{"cnum_$i"}); $i++) {
170                 next if (!$in{"cnum_$i"} || !$in{"cval_$i"});
171                 $in{"cnum_$i"} =~ /^\d+$/ ||
172                    ($config{'dhcpd_version'} >= 3 &&
173                     $in{"cnum_$i"} =~ /^\S+$/) ||
174                         &error(&text('sopt_ednum', $in{"cnum_$i"}));
175                 local $cv = $in{"cval_$i"};
176                 $cv = "\"$cv\""
177                         if ($cv !~ /^([0-9a-fA-F]{1,2}:)*[0-9a-fA-F]{1,2}$/ &&
178                             !&check_ipaddress($cv));
179                 push(@newcustom, { 'name' => 'option',
180                                    'values' => [ 'option-'.$in{"cnum_$i"},
181                                                  $cv ] } );
182                 }
183         &save_directive($client, \@custom, \@newcustom, $indent, 1);
184         }
185
186 &flush_file_lines();
187 &unlock_file($config{'dhcpd_conf'});
188 if ($client->{'name'} eq 'subnet') {
189         &webmin_log("options", 'subnet',
190                     "$client->{'values'}->[0]/$client->{'values'}->[2]", \%in);
191         }
192 elsif ($client->{'name'} eq 'shared-network') {
193         &webmin_log("options", 'subnet', $client->{'values'}->[0], \%in);
194         }
195 elsif ($client->{'name'} eq 'host') {
196         &webmin_log("options", 'host', $client->{'values'}->[0], \%in);
197         }
198 elsif ($client->{'name'} eq 'group') {
199         @count = &find("host", $client->{'members'});
200         &webmin_log("options", 'group',
201                     join(",", map { $_->{'values'}->[0] } @count), \%in);
202         }
203 &redirect($ret);
204
205 # save_option(name, type, &config, indent, [has-boolean])
206 sub save_option
207 {
208 local($v);
209 local $m = $_[2]->{'members'};
210 for($i=0; $i<@$m; $i++) {
211         if ($m->[$i]->{'name'} eq 'option' &&
212             $m->[$i]->{'values'}->[0] eq $_[0]) {
213                 $v = $m->[$i];
214                 last;
215                 }
216         }
217 if ($in{"$_[0]_def"}) {
218         &save_directive($_[2], [ $v ], [ ], 0, 1) if ($v);
219         }
220 else {
221         local $nv = $in{$_[0]};
222         local @nv = split(/\s+/, $nv);
223         if ($_[1] == 0) {
224                 &to_ipaddress($nv) ||
225                         &error("$_[0] '$nv' $text{'sopt_invalidip'}");
226                 }
227         elsif ($_[1] == 1) {
228                 $nv =~ /^-?\d+$/ || &error("'$nv' $text{'sopt_invalidint'}");
229                 }
230         elsif ($_[1] == 2) {
231                 foreach my $ip (@nv) {
232                         &to_ipaddress($ip) ||
233                                 &error("'$ip' $text{'sopt_invalidip'}");
234                         }
235                 $nv = join(", ", @nv);
236                 }
237         elsif ($_[1] == 3) {
238                 $nv = "\"$nv\"";
239                 }
240         elsif ($_[1] == 5) {
241                 local($ipp, @nnv);
242                 foreach $ipp (@nv) {
243                         $ipp =~ /^(\S+)\s*,\s*(\S+)$/ ||
244                                 &error("'$ipp' $text{'sopt_invalidipp'}");
245                         &check_ipaddress($1) ||
246                                 &error("'$1' $text{'sopt_invalidip'}");
247                         &check_ipaddress($2) ||
248                                 &error("'$2' $text{'sopt_invalidip'}");
249                         push(@nnv, "$1 $2");
250                         }
251                 $nv = join(", ", @nnv);
252                 }
253         elsif ($_[1] == 6) {
254                 $nv = join(", ", map { "\"$_\"" } @nv);
255                 }
256         local @bool = !$_[4] ? ( ) :
257                       $in{$_[0]."_bool"} ? ( "true" ) : ( "false" );
258         local $dir = { 'name' => 'option',
259                        'values' => [ $_[0], @bool, $nv ] };
260         &save_directive($_[2], $v ? [ $v ] : [ ], [ $dir ], $_[3], 1);
261         }
262 }
263