Handle hostnames with upper-case letters
[webmin.git] / useradmin / gbatch_exec.cgi
1 #!/usr/local/bin/perl
2 # Execute create/modify/delete group commands in a batch file
3
4 require './user-lib.pl';
5 $access{'batch'} || &error($text{'gbatch_ecannot'});
6 if ($ENV{'REQUEST_METHOD'} eq 'GET') {
7         &ReadParse();
8         }
9 else {
10         &ReadParseMime();
11         }
12 if ($in{'source'} == 0) {
13         $data = $in{'file'};
14         $data =~ /\S/ || &error($text{'batch_efile'});
15         }
16 elsif ($in{'source'} == 1) {
17         open(LOCAL, $in{'local'}) || &error($text{'batch_elocal'});
18         while(<LOCAL>) {
19                 $data .= $_;
20                 }
21         close(LOCAL);
22         }
23 elsif ($in{'source'} == 2) {
24         $data = $in{'text'};
25         $data =~ /\S/ || &error($text{'batch_etext'});
26         }
27
28 &ui_print_unbuffered_header(undef, $text{'gbatch_title'}, "");
29
30 # Force defaults for save options
31 $in{'chgid'} = 1 if (!$access{'chgid'});
32
33 # Work out a good base GID for new groups
34 &build_group_used(\%gused, \%gtaken);
35 $newgid = int($config{'base_gid'} > $access{'lowgid'} ?
36               $config{'base_gid'} : $access{'lowgid'});
37
38 # Process the file
39 &batch_start() if ($in{'batch'});
40 &lock_user_files();
41 $lnum = $created = $modified = $deleted = 0;
42 print "<pre>\n";
43 $pft = &passfiles_type();
44 foreach $line (split(/[\r\n]+/, $data)) {
45         $lnum++;
46         $line =~ s/^\s*#.*$//;
47         next if ($line !~ /\S/);
48         local @line = split(/:/, $line, -1);
49         local %group;
50         if ($line[0] eq 'create') {
51                 # Creating a new group
52
53                 # Validate line
54                 if (!$line[1]) {
55                         print &text('batch_eline', $lnum),"\n";
56                         next;
57                         }
58                 if (@line != 5) {
59                         print &text('batch_elen', $lnum, 5),"\n";
60                         next;
61                         }
62                 if ($line[1] !~ /^[^:\t]+$/) {
63                         print &text('gbatch_egroupname', $lnum),"\n";
64                         next;
65                         }
66                 $group{'group'} = $line[1];
67
68                 if ($gtaken{$group{'group'}}) {
69                         print &text('gbatch_egroup', $lnum,
70                                     $group{'group'}),"\n";
71                         next;
72                         }
73                 if ($line[3] !~ /^\d+$/) {
74                         # make up a GID
75                         while($gused{$newgid}) {
76                                 $newgid++;
77                                 }
78                         $group{'gid'} = $newgid;
79                         }
80                 else {
81                         # use the given UID
82                         if ($gused{$line[3]} && !$access{'gmultiple'}) {
83                                 print &text('gbatch_ecaccess', $lnum,
84                                             $text{'gsave_egidused2'}),"\n";
85                                 next;
86                                 }
87                         $group{'gid'} = $line[3];
88                         }
89                 $gused{$group{'gid'}}++;
90                 $group{'members'} = $line[4];
91
92                 # Check access control restrictions
93                 if (!$access{'gcreate'}) {
94                         print &text('gbatch_ecaccess', $lnum,
95                                     $text{'gsave_ecreate'});
96                         next;
97                         }
98                 local $ch = &check_group(\%group);
99                 if ($ch) {
100                         print &text('gbatch_ecaccess', $lnum, $ch),"\n";
101                         next;
102                         }
103
104                 if ($line[2] eq '') {
105                         # No password needed
106                         $group{'pass'} = '';
107                         $group{'passmode'} = 0;
108                         }
109                 else {
110                         # Normal password
111                         $group{'pass'} = &encrypt_password($line[2]);
112                         $group{'passmode'} = 3;
113                         $group{'plainpass'} = $line[2];
114                         }
115
116                 # Run the before command
117                 &set_user_envs(\%group, 'CREATE_GROUP');
118                 $merr = &making_changes();
119                 &error(&text('gsave_emaking', "<tt>$merr</tt>"))
120                         if (defined($merr));
121
122                 # Create the group!
123                 &create_group(\%group);
124
125                 # All done
126                 &made_changes();
127
128                 # Call other modules, ignoring any failures
129                 $error_must_die = 1;
130                 eval {
131                         &other_modules("useradmin_create_group", \%group)
132                                 if ($access{'cothers'} == 1 && $in{'others'} ||
133                                     $access{'cothers'} == 0);
134                         };
135                 $other_err = $@;
136                 $error_must_die = 0;
137
138                 print "<b>",&text('gbatch_created', $group{'group'}),"</b>\n";
139                 print "<b><i>",&text('batch_eother', $other_err),"</i></b>\n"
140                         if ($other_err);
141                 $created++;
142                 }
143         elsif ($line[0] eq 'delete') {
144                 # Deleting an existing group
145                 if (@line != 2) {
146                         print &text('batch_elen', $lnum, 2),"\n";
147                         next;
148                         }
149                 local @glist = &list_groups();
150                 local ($group) = grep { $_->{'group'} eq $line[1] } @glist;
151                 if (!$group) {
152                         print &text('gbatch_enogroup', $lnum, $line[1]),"\n";
153                         next;
154                         }
155
156                 # Check if deletion is allowed
157                 if (!&can_edit_group(\%access, $group)) {
158                         print &text('gbatch_edaccess', $lnum,
159                                     $text{'gdel_egroup'}),"\n";
160                         next;
161                         }
162                 if (!$config{'delete_root'} && $group->{'gid'} <= 10) {
163                         print &text('gbatch_edaccess', $lnum,
164                                     $text{'gdel_egroup'}),"\n";
165                         next;
166                         }
167
168                 # Check if has primary members
169                 local $prim;
170                 foreach $u (&list_users()) {
171                         if ($u->{'gid'} == $group->{'gid'}) {
172                                 $prim = $u;
173                                 last;
174                                 }
175                         }
176                 if ($prim) {
177                         print &text('gbatch_eprimary', $lnum,
178                                     $prim->{'user'}),"\n";
179                         next;
180                         }
181
182                 # Run the before command
183                 &set_user_envs($group, 'DELETE_GROUP');
184                 $merr = &making_changes();
185                 &error(&text('usave_emaking', "<tt>$merr</tt>"))
186                         if (defined($merr));
187
188                 # Delete from other modules, ignoring errors
189                 $error_must_die = 1;
190                 eval {
191                         &other_modules("useradmin_delete_group", $group)
192                                 if ($access{'dothers'} == 1 && $in{'others'} ||
193                                     $access{'dothers'} == 0);
194                         };
195                 $other_err = $@;
196                 $error_must_die = 0;
197
198                 # Delete the user entry
199                 &delete_group($group);
200
201                 &made_changes();
202
203                 print "<b>",&text('gbatch_deleted',$group->{'group'}),"</b>\n";
204                 print "<b><i>",&text('batch_eother', $other_err),"</i></b>\n"
205                         if ($other_err);
206                 $deleted++;
207                 }
208         elsif ($line[0] eq 'modify') {
209                 # Modifying an existing group
210                 if (@line != 6) {
211                         print &text('batch_elen', $lnum, 6),"\n";
212                         next;
213                         }
214                 local @glist = &list_groups();
215                 local ($group) = grep { $_->{'group'} eq $line[1] } @glist;
216                 if (!$group) {
217                         print &text('gbatch_enogroup', $lnum, $line[1]),"\n";
218                         next;
219                         }
220                 %oldgroup = %group = %$group;
221                 $user{'olduser'} = $user->{'user'};
222                 if (!&can_edit_group(\%access, \%group)) {
223                         print &text('gbatch_emaccess', $lnum,
224                                     $text{'gsave_eedit'}),"\n";
225                         next;
226                         }
227
228                 # Update supplied fields
229                 if ($line[2] ne '') {
230                         if (!$access{'grename'}) {
231                                 print &text('gbatch_erename',
232                                             $lnum, $line[1]),"\n";
233                                 }
234                         $group{'group'} = $line[2];
235                         }
236                 if ($line[3] ne '') {
237                         # New normal password
238                         $group{'pass'} = &encrypt_password($line[3]);
239                         $group{'passmode'} = 3;
240                         $group{'plainpass'} = $line[3];
241                         }
242                 else {
243                         # No change
244                         $group{'passmode'} = 4;
245                         }
246                 $group{'gid'} = $line[4] if ($line[4] ne '');
247                 if ($line[5] =~ /^\s+$/ || $line[5] eq 'NONE') {
248                         # No members
249                         $group{'members'} = '';
250                         }
251                 elsif ($line[5]) {
252                         $group{'members'} = $line[5];
253                         }
254
255                 # Check access control restrictions
256                 local $ch = &check_group(\%group, \%oldgroup);
257                 if ($ch) {
258                         print &text('gbatch_emaccess', $lnum, $ch),"\n";
259                         next;
260                         }
261
262                 # Run the before command
263                 &set_user_envs(\%group, 'MODIFY_GROUP');
264                 $merr = &making_changes();
265                 &error(&text('usave_emaking', "<tt>$merr</tt>"))
266                         if (defined($merr));
267
268                 # Change GIDs
269                 if ($oldgroup{'gid'} != $group{'gid'} && $in{'chgid'}) {
270                         if ($in{'chgid'} == 1) {
271                                 # Do all the home directories of members
272                                 &change_all_home_groups(
273                                         $oldgroup{'gid'}, $group{'gid'},
274                                         [ split(/,/, $group{'members'}) ]);
275                                 }
276                         else {
277                                 # Do all files in this group from the root dir
278                                 &recursive_change("/", -1, $oldgroup{'gid'},
279                                                        -1, $group{'gid'});
280                                 }
281                         }
282
283                 # Actually modify the group
284                 &modify_group(\%oldgroup, \%group);
285                 &made_changes();
286
287                 # Modify in other modules, ignoring errors
288                 $error_must_die = 1;
289                 eval {
290                         &other_modules("groupadmin_modify_group",
291                                        \%group, \%oldgroup)
292                                 if ($access{'mothers'} == 1 && $in{'others'} ||
293                                     $access{'mothers'} == 0);
294                         };
295                 $error_must_die = 0;
296                 $other_err = $@;
297
298                 print "<b>",&text('batch_modified',$oldgroup{'group'}),"</b>\n";
299                 print "<b><i>",&text('batch_eother', $other_err),"</i></b>\n"
300                         if ($other_err);
301                 $modified++;
302                 }
303         else {
304                 print &text('batch_eaction', $lnum, $line[0]),"\n";
305                 next;
306                 }
307         }
308 print "</pre>\n";
309 &batch_end() if ($in{'batch'});
310 &unlock_user_files();
311 &webmin_log("gbatch", undef, $in{'source'} == 1 ? $in{'local'} : undef,
312             { 'created' => $created, 'modified' => $modified,
313               'deleted' => $deleted, 'lnum' => $lnum } );
314
315 &ui_print_footer("gbatch_form.cgi", $text{'batch_return'},
316                  "index.cgi?mode=groups", $text{'index_return'});
317
318 # check_group(\%group, [\%oldgroup])
319 # Check access control restrictions for a group
320 sub check_group
321 {
322 # check if gid is within range
323 if ($access{'lowgid'} && $_[0]->{'gid'} < $access{'lowgid'}) {
324         return &text('usave_elowgid', $access{'lowuid'});
325         }
326 if ($access{'hiuid'} && $_[0]->{'uid'} > $access{'hiuid'}) {
327         return &text('usave_ehiuid', $access{'hiuid'});
328         }
329 if ($_[1] && !$access{'ggid'} && $_[1]->{'gid'} != $_[0]->{'gid'}) {
330         return $text{'gsave_eggid'};
331         }
332 return undef;
333 }
334