Handle hostnames with upper-case letters
[webmin.git] / dhcpd / save_subnet.cgi
1 #!/usr/local/bin/perl
2 # save_subnet.cgi
3 # Update, create or delete a subnet
4
5 require './dhcpd-lib.pl';
6 require './params-lib.pl';
7 &ReadParse();
8 &lock_file($config{'dhcpd_conf'});
9 ($par, $sub, $indent, $npar, $nindent) = get_branch('sub', $in{'new'});
10 $parconf = $par->{'members'};
11
12 # check acls
13 %access = &get_module_acl();
14 &error_setup($text{'eacl_aviol'});
15 if ($in{'delete'}) {
16         &error("$text{'eacl_np'} $text{'eacl_pds'}")
17                 if !&can('rw', \%access, $sub, 1);
18         }
19 elsif ($in{'options'}) {
20         &error("$text{'eacl_np'} $text{'eacl_pss'}") 
21                 if !&can('r', \%access, $sub);
22         }
23 elsif ($in{'new'}) {
24         &error("$text{'eacl_np'} $text{'eacl_pis'}")
25                 unless &can('c', \%access, $sub) && 
26                                 &can('rw', \%access, $par) &&
27                                 (!$npar || &can('rw', \%access, $npar));
28         # restrict duplicates
29         if ($access{'uniq_sub'}) {
30                 foreach $s (&get_subnets()) {
31                         &error("$text{'eacl_np'} $text{'eacl_uniq'}")
32                                 if lc $s->{'values'}->[0] eq lc $in{'network'};
33                         }
34                 }
35         }
36 elsif (!$in{'leases'}) {
37         &error("$text{'eacl_np'} $text{'eacl_pus'}")
38                 unless &can('rw', \%access, $sub) &&
39                         (!$npar || &can('rw', \%access, $npar));
40         }
41
42 # save
43 if ($in{'options'}) {
44         # Redirect to client options
45         &redirect("edit_options.cgi?sidx=$in{'sidx'}&idx=$in{'idx'}");
46         exit;
47         }
48 elsif ($in{'leases'}) {
49         # Redirect to lease list for subnet
50         &redirect("list_leases.cgi?network=$sub->{'values'}->[0]&netmask=$sub->{'values'}->[2]");
51         exit;
52         }
53 else {
54         if ($in{'delete'}) {
55                 &error_setup($text{'ssub_faildel'});
56                 if ($par->{'name'} eq "shared-network") {
57                         @subnets = &find("subnet", $par->{'members'});
58                         if (@subnets < 2) {
59                                 &error(&text('ssub_nosubnet', $par->{'values'}->[0]));
60                                 }
61                         }
62                 }
63         else {
64                 &error_setup($text{'ssub_failsave'});
65                 # Validate and save inputs
66                 &to_ipaddress($in{'network'}) ||
67                         &error("'$in{'network'}' $text{'ssub_invalidsubaddr'}");
68                 &check_ipaddress($in{'netmask'}) ||
69                         &error("'$in{'netmask'}' $text{'ssub_invalidnmask'}");
70                 $oldnetwork = $sub->{'values'}->[0];
71                 $sub->{'values'} = [ $in{'network'}, "netmask", $in{'netmask'} ];
72                 }
73
74         @wasin = &find("host", $sub->{'members'});
75         foreach $hn (split(/\0/, $in{'hosts'})) {
76                 if ($hn =~ /(\d+),(\d+)/) {
77                         push(@nowin, $parconf->[$2]->{'members'}->[$1]);
78                         $nowpr{$parconf->[$2]->{'members'}->[$1]} =
79                                 $parconf->[$2];
80                         }
81                 elsif ($hn =~ /(\d+),/) {
82                         push(@nowin, $parconf->[$1]);
83                         $nowpr{$parconf->[$1]} = $par;
84                         }
85                 if ($nowin[$#nowin]->{'name'} ne "host") {
86                         &error($text{'sgroup_echanged'});
87                         }
88                 }
89         @wasgin = &find("group", $sub->{'members'});
90         foreach $gn (split(/\0/, $in{'groups'})) {
91                 if ($gn =~ /(\d+),(\d+)/) {
92                         push(@nowgin, $parconf->[$2]->{'members'}->[$1]);
93                         $nowgpr{$parconf->[$2]->{'members'}->[$1]} =
94                                 $parconf->[$2];
95                         }
96                 elsif ($gn =~ /(\d+),/) {
97                         push(@nowgin, $parconf->[$1]);
98                         $nowgpr{$parconf->[$1]} = $par;
99                         }
100                 if ($nowgin[$#nowgin]->{'name'} ne "group") {
101                         &error($text{'sgroup_echanged'});
102                         }
103                 }
104
105         &error_setup($text{'eacl_aviol'});
106         foreach $h (&unique(@wasin, @nowin)) {
107                 $was = &indexof($h, @wasin) != -1;
108                 $now = &indexof($h, @nowin) != -1;
109
110                 # per-host ACLs for new or updated hosts
111                 if ($was != $now && !&can('rw', \%access, $h)) {
112                         &error("$text{'eacl_np'} $text{'eacl_pus'}");
113                         }
114                 if ($was && !$now) {
115                         # Move out of the subnet
116                         &save_directive($sub, [ $h ], [ ], $indent);
117                         &save_directive($par, [ ], [ $h ], $indent);
118                         }
119                 elsif ($now && !$was) {
120                         # Move into the subnet (maybe from another subnet)
121                         &save_directive($nowpr{$h}, [ $h ], [ ], $indent);
122                         &save_directive($sub, [ ], [ $h ], $indent + 1);
123                         }
124                 }
125         foreach $g (&unique(@wasgin, @nowgin)) {
126                 $was = &indexof($g, @wasgin) != -1;
127                 $now = &indexof($g, @nowgin) != -1;
128
129                 # per-group ACLs for new or updated groups
130                 if ($was != $now && !&can('rw', \%access, $g)) {
131                         &error("$text{'eacl_np'} $text{'eacl_pus'}");
132                         }
133                 if ($was && !$now) {
134                         # Move out of the subnet
135                         &save_directive($sub, [ $g ], [ ], $indent);
136                         &save_directive($par, [ ], [ $g ], $indent);
137                         }
138                 elsif ($now && !$was) {
139                         # Move into the subnet (maybe from another subnet)
140                         &save_directive($nowgpr{$g}, [ $g ], [ ], $indent);
141                         &save_directive($sub, [ ], [ $g ], $indent + 1);
142                         }
143                 }
144
145         if (!$in{'delete'}) {
146                 &error_setup($text{'ssub_failsave'});
147                 for($i=0; defined($low = $in{"range_low_$i"}); $i++) {
148                         next if (!$low);
149                         $hi = $in{"range_hi_$i"}; $dyn = $in{"range_dyn_$i"};
150                         &check_ipaddress($low) ||
151                                 &error("'$low' $text{'ssub_invalidipr'}");
152                         !$hi || &check_ipaddress($hi) ||
153                                 &error("'$hi' $text{'ssub_invalidipr'}");
154                         $rng = { 'name' => 'range',
155                                  'values' => [ ($dyn ? "dynamic-bootp" : ()),
156                                                $low, ($hi ? $hi : ()) ] };
157                         push(@rng, $rng);
158                         }
159                 &save_directive($sub, "range", \@rng, 1);
160                 $sub->{'comment'} = $in{'desc'};
161                 &parse_params($sub, 0);
162
163                 if (!npar || $in{'assign'} > 0 && $npar->{'name'} ne "shared-network") {
164                         &error($text{'sgroup_echanged'});
165                         }
166                 if ($in{'new'}) {
167                         # save acl for new network
168                         &save_dhcpd_acl('rw','sub',\%access,$in{'network'});
169                         # Add to the end of the parent structure
170                         &save_directive($npar, [ ], [ $sub ], $nindent);
171                         }
172                 elsif ($par eq $npar) {
173                         # Update the subnet in the current parent
174                         &save_directive($par, [ $sub ], [ $sub ], $nindent);
175                         if ($in{'network'} ne $oldnetwork) {
176                                 # Fix the ACL
177                                 &drop_dhcpd_acl('sub', \%access, $oldnetwork);
178                                 &save_dhcpd_acl('rw','sub',\%access,
179                                                 $in{'network'});
180                                 }
181                         }
182                 else {
183                         # Move the subnet
184                         if ($par->{'name'} eq "shared-network") {
185                                 @subnets = &find("subnet", $par->{'members'});
186                                 if (@subnets < 2) {
187                                         &error(&text('ssub_nosubnet', $par->{'values'}->[0]));
188                                         }
189                                 }
190                         &save_directive($par, [ $sub ], [ ], 0);
191                         &save_directive($npar, [ ], [ $sub ], $nindent);
192                         }
193                 }
194         }
195 &flush_file_lines();
196 if ($in{'delete'}) {
197         # Delete this subnet
198         if ($in{'hosts'} eq "" && $in{'groups'} eq "") {
199                 &drop_dhcpd_acl('sub', \%access, $sub->{'values'}->[0]);
200                 &save_directive($par, [ $sub ], [ ], 0);
201                 &flush_file_lines();
202                 }
203         else {
204                 &unlock_file($config{'dhcpd_conf'});
205                 &redirect("confirm_delete.cgi?sidx=$in{'sidx'}&idx=$in{'idx'}"
206                         ."\&type=1");
207                 exit;
208                 }
209         }
210 &unlock_file($config{'dhcpd_conf'});
211 &webmin_log($in{'delete'} ? 'delete' : $in{'new'} ? 'create' : 'modify',
212             'subnet', "$sub->{'values'}->[0]/$sub->{'values'}->[2]", \%in);
213
214 &redirect($in{'ret'} eq "shared" ? "edit_shared.cgi?idx=$in{'sidx'}" : "");