Handle hostnames with upper-case letters
[webmin.git] / cfengine / save_class.cgi
1 #!/usr/local/bin/perl
2 # save_class.cgi
3 # Create, update or delete a class in a section
4
5 require './cfengine-lib.pl';
6 &ReadParse();
7 $conf = $in{'cfd'} ? &get_cfd_config() : &get_config();
8 $sec = $conf->[$in{'idx'}] if ($in{'idx'} ne '');
9 $cls = $sec->{'cls'}->[$in{'cidx'}] if ($in{'cidx'} ne '');
10
11 if ($in{'manualmode'}) {
12         # Redirect back to the edit form, but in manual mode
13         &redirect("edit_class.cgi?cfd=$in{'cfd'}&idx=$in{'idx'}&cidx=$in{'cidx'}&new=$in{'new'}&manual=1");
14         }
15 elsif ($in{'delete'}) {
16         # Just delete the class, and maybe the section too
17         &lock_file($sec->{'file'});
18         if (@{$sec->{'cls'}} == 1) {
19                 &save_directive($conf, $sec, undef);
20                 }
21         else {
22                 &save_directive($sec->{'cls'}, $cls, undef);
23                 }
24         &flush_file_lines();
25         &unlock_file($sec->{'file'});
26         &webmin_log("delete", @{$sec->{'cls'}} == 1 ? "section" : "class",
27                     $sec->{'name'});
28         &redirect($in{'cfd'} ? "edit_cfd.cgi" : "");
29         }
30 else {
31         # Validate and save inputs
32         $type = $in{'idx'} eq '' ? $in{'type'} : $sec->{'name'};
33         if (!$sec) {
34                 $sec = { 'name' => $type,
35                          'type' => 'section',
36                          'cls' => [ $cls = { 'type' => 'class' } ] };
37                 }
38         elsif (!$cls) {
39                 $cls = { 'type' => 'class' };
40                 }
41         &error_setup($text{'save_err'});
42         $in{'class_def'} || $in{'class'} =~ /^\S+$/ ||
43                 &error($text{'save_eclass'});
44         $cls->{'name'} = $in{'class_def'} ? 'any' : $in{'class'};
45         $cls->{'implied'} = 0 if (!$in{'class_def'});
46
47         if (defined($in{'manual'})) {
48                 # Just save manually edited text
49                 $in{'manual'} =~ s/\r//g;
50                 $cls->{'text'} = $in{'manual'};
51                 }
52         elsif ($type eq 'links') {
53                 # Save link creation lines
54                 local @olinks = &parse_links($cls);
55                 local @links;
56                 for($i=0; defined($in{"from_$i"}); $i++) {
57                         next if (!$in{"from_$i"} && !$in{"to_$i"});
58                         local $link = $olinks[$i];
59                         $in{"from_$i"} =~ /^\S+$/ ||
60                                 &error(&text('save_elinkfrom', $i+1));
61                         $link->{'_linkfrom'} = $in{"from_$i"};
62                         $in{"to_$i"} =~ /^\S+$/ ||
63                                 &error(&text('save_elinkto', $i+1));
64                         $link->{'_linkto'} = $in{"to_$i"};
65                         $link->{'_linktype'} = $in{"type_$i"} ? "+>" : "->";
66                         $link->{'_linktype'} .= "!" if ($in{"over_$i"});
67                         push(@links, $link);
68                         }
69                 &unparse_links($cls, @links);
70                 }
71         elsif ($type eq 'directories') {
72                 # Save directory creation lines
73                 local @odirs = &parse_directories($cls);
74                 local @dirs;
75                 for($i=0; defined($in{"dir_$i"}); $i++) {
76                         next if (!$in{"dir_$i"});
77                         local $dir = $odirs[$i];
78                         $in{"dir_$i"} =~ /^\S+$/ ||
79                                 &error(&text('save_edir', $i+1));
80                         $dir->{'_dir'} = $in{"dir_$i"};
81
82                         &sdelete($dir, 'mode');
83                         if ($in{"mode_$i"} ne "") {
84                                 $in{"mode_$i"} =~ /^[0-9]{3,4}$/ ||
85                                         &error(&text('save_edirmode', $i+1));
86                                 $dir->{'mode'} = $in{"mode_$i"};
87                                 }
88
89                         &sdelete($dir, 'owner');
90                         if ($in{"owner_$i"} ne "") {
91                                 $in{"owner_$i"} =~ /^\S+$/ ||
92                                         &error(&text('save_edirowner', $i+1));
93                                 $dir->{'owner'} = $in{"owner_$i"};
94                                 }
95
96                         &sdelete($dir, 'group');
97                         if ($in{"group_$i"} ne "") {
98                                 $in{"group_$i"} =~ /^\S+$/ ||
99                                         &error(&text('save_edirgroup', $i+1));
100                                 $dir->{'group'} = $in{"group_$i"};
101                                 }
102
103                         push(@dirs, $dir);
104                         }
105                 &unparse_directories($cls, @dirs);
106                 }
107         elsif ($type eq "control" && !$in{'cfd'}) {
108                 # Save actionsequence and other global definitions
109                 local ($sp, $qu) = &split_str($in{'seq'});
110                 push(@defs, { 'name' => 'actionsequence',
111                               'values' => $sp,
112                               'valuequotes' => $qu } );
113                 for($i=0; defined($in{"def_$i"}); $i++) {
114                         next if (!$in{"def_$i"});
115                         $in{"def_$i"} =~ /^\S+$/ ||
116                                 &error(&text('save_econtroldef', $i+1));
117                         local ($sp, $qu) = &split_str($in{"value_$i"});
118                         push(@defs, { 'name' => $in{"def_$i"},
119                                       'values' => $sp,
120                                       'valuequotes' => $qu } );
121                         }
122                 $cls->{'defs'} = \@defs;
123                 }
124         elsif ($type eq "control" && $in{'cfd'}) {
125                 # Save cfd-specific control options
126                 $in{'run_def'} ||
127                     ($in{'run'} =~ /^(\S+)/ && &has_command("$1")) ||
128                         &error(&text('save_econtrolrun', "$1"));
129                 &save_define($cls->{'defs'}, "cfrunCommand",
130                              $in{'run_def'} ? undef : [ $in{'run'} ]);
131
132                 $in{'elapsed_def'} || $in{'elapsed'} =~ /^\d+$/ ||
133                         &error($text{'save_econtrolelapsed'});
134                 &save_define($cls->{'defs'}, "IfElapsed",
135                              $in{'elapsed_def'} ? undef : [ $in{'elapsed'} ]);
136
137                 $in{'max_def'} || $in{'max'} =~ /^\d+$/ ||
138                         &error($text{'save_econtrolmax'});
139                 &save_define($cls->{'defs'}, "MaxConnections",
140                              $in{'max_def'} ? undef : [ $in{'max'} ]);
141
142                 $in{'auto_def'} ||
143                     ($in{'auto'} =~ /^(\S+)/ && &has_command("$1")) ||
144                         &error(&text('save_econtrolauto', "$1"));
145                 &save_define($cls->{'defs'}, "AutoExecCommand",
146                              $in{'auto_def'} ? undef : [ $in{'auto'} ]);
147
148                 $in{'interval_def'} || $in{'interval'} =~ /^\d+$/ ||
149                         &error($text{'save_econtrolinterval'});
150                 &save_define($cls->{'defs'}, "AutoExecInterval",
151                      $in{'interval_def'} ? undef : [ $in{'interval'} ]);
152
153                 $in{'dom_def'} || $in{'dom'} =~ /^[A-Za-z0-9\.\-]+$/ ||
154                         &error($text{'save_econtroldomain'});
155                 &save_define($cls->{'defs'}, "domain",
156                      $in{'dom_def'} ? undef : [ $in{'dom'} ]);
157
158                 &save_define($cls->{'defs'}, "LogAllConnections",
159                                 $in{'log'} == 1 ? [ "true" ] :
160                                 $in{'log'} == 0 ? [ "false" ] : undef);
161
162                 $in{'allow_def'} || $in{'allow'} =~ /\S/ ||
163                         &error($text{'save_econtrolallow'});
164                 &save_define($cls->{'defs'}, "AllowConnectionsFrom",
165                              $in{'allow_def'} ? undef :
166                              [ split(/\s+/, $in{'allow'}) ] );
167
168                 $in{'deny_def'} || $in{'deny'} =~ /\S/ ||
169                         &error($text{'save_econtroldeny'});
170                 &save_define($cls->{'defs'}, "DenyConnectionsFrom",
171                              $in{'deny_def'} ? undef :
172                              [ split(/\s+/, $in{'deny'}) ] );
173
174                 $in{'skip_def'} || $in{'skip'} =~ /\S/ ||
175                         &error($text{'save_econtrolskip'});
176                 &save_define($cls->{'defs'}, "SkipVerify",
177                              $in{'skip_def'} ? undef :
178                              [ split(/\s+/, $in{'skip'}) ] );
179                 }
180         elsif ($type eq "admit" || $type eq "grant" || $type eq "deny") {
181                 # Save allowed or denied directories
182                 local $vl = 0;
183                 for($i=0; defined($in{"dir_$i"}); $i++) {
184                         next if (!$in{"dir_$i"});
185                         $in{"dir_$i"} =~ /^\S+$/ ||
186                                 &error(&text('save_egrantdir', $i+1));
187                         push(@values, $in{"dir_$i"});
188                         push(@valuelines, $vl++);
189                         local @hosts = split(/\s+/, $in{"hosts_$i"});
190                         @hosts ||
191                             &error(&text('save_egranthosts', $in{"dir_$i"}));
192                         foreach $h (@hosts) {
193                                 &to_ipaddress($h) ||
194                                     $h =~ /\*/ || $h =~ /=/ ||
195                                         &error(&text('save_egranthost', $h));
196                                 push(@values, $h);
197                                 push(@valuelines, $vl++);
198                                 }
199                         $vl++;
200                         }
201
202                 $cls->{'values'} = \@values;
203                 $cls->{'valuelines'} = \@valuelines;
204                 }
205         elsif ($type eq "groups" || $type eq "classes") {
206                 # Save group definitions
207                 for($i=0,$j=0; defined($in{"name_$i"}); $i++) {
208                         next if (!$in{"name_$i"});
209                         $in{"name_$i"} =~ /^\S+$/ ||
210                                 &error(&text('save_egroupname', $i+1));
211                         local ($st, $qu) = &split_str($in{"mems_$i"});
212                         push(@defs, { 'name' => $in{"name_$i"},
213                                       'values' => $st,
214                                       'valuequotes' => $qu } );
215                         $j++;
216                         }
217                 $cls->{'defs'} = \@defs;
218                 }
219         elsif ($type eq "files") {
220                 # Save all the files lines
221                 local @ofiles = &parse_directories($cls);
222                 local @files;
223                 for($i=0; defined($d = $in{"dir_$i"}); $i++) {
224                         next if ($in{"dir_def_$i"});
225                         local $file = $ofiles[$i];
226                         $file->{'_dir'} = $d;
227                         $d =~ /\S/ || &error(&text('save_efilesdir', $i+1));
228
229                         &sdelete($file, 'owner');
230                         if (!$in{"owner_def_$i"}) {
231                                 $in{"owner_$i"} =~ /^\S+$/ ||
232                                         &error(&text('save_efilesowner', $d));
233                                 $file->{'owner'} = $in{"owner_$i"};
234                                 }
235
236                         &sdelete($file, 'group');
237                         if (!$in{"group_def_$i"}) {
238                                 $in{"group_$i"} =~ /^\S+$/ ||
239                                         &error(&text('save_efilesgroup', $d));
240                                 $file->{'group'} = $in{"group_$i"};
241                                 }
242
243                         &sdelete($file, 'mode');
244                         if (!$in{"mode_def_$i"}) {
245                                 $in{"mode_$i"} =~ /^\S+$/ ||
246                                         &error(&text('save_efilesmode', $d));
247                                 $file->{'mode'} = $in{"mode_$i"};
248                                 }
249
250                         &sdelete($file, 'recurse');
251                         if ($in{"rec_def_$i"} == 2) {
252                                 $file->{'recurse'} = 'inf';
253                                 }
254                         elsif ($in{"rec_def_$i"} == 0) {
255                                 $in{"rec_$i"} =~ /^\d+$/ ||
256                                         &error(&text('save_efilesrec', $d));
257                                 $file->{'recurse'} = $in{"rec_$i"};
258                                 }
259
260                         &sdelete($file, 'include');
261                         if (!$in{"include_def_$i"}) {
262                                 $in{"include_$i"} =~ /^\S+$/ ||
263                                     &error(&text('save_efilesinclude', $d));
264                                 $file->{'include'} = $in{"include_$i"};
265                                 }
266
267                         &sdelete($file, 'exclude');
268                         if (!$in{"exclude_def_$i"}) {
269                                 $in{"exclude_$i"} =~ /^\S+$/ ||
270                                     &error(&text('save_efilesexclude', $d));
271                                 $file->{'exclude'} = $in{"exclude_$i"};
272                                 }
273
274                         &sdelete($file, 'acl');
275                         if (!$in{"acl_def_$i"}) {
276                                 $in{"acl_$i"} =~ /^\S+$/ ||
277                                         &error(&text('save_efileacl', $d));
278                                 $file->{'acl'} = $in{"acl_$i"};
279                                 }
280
281                         &sdelete($file, 'action');
282                         if ($in{"act_$i"}) {
283                                 $file->{'action'} = $in{"act_$i"};
284                                 }
285                         push(@files, $file);
286                         }
287                 &unparse_directories($cls, @files);
288                 }
289         elsif ($type eq "copy") {
290                 # Save copy lines
291                 local @ocopies = &parse_directories($cls);
292                 local @copies;
293                 for($i=0; defined($d = $in{"dir_$i"}); $i++) {
294                         next if ($in{"dir_def_$i"});
295                         local $copy = $ocopies[$i];
296                         $copy->{'_dir'} = $d;
297                         $d =~ /\S/ || &error(&text('save_ecopydir', $i+1));
298
299                         &sdelete($copy, "dest");
300                         $in{"dest_$i"} =~ /\S/ ||
301                                 &error(&text('save_ecopydest', $d));
302                         $copy->{'dest'} = $in{"dest_$i"};
303
304                         &sdelete($copy, "server");
305                         if (!$in{"server_def_$i"}) {
306                                 &to_ipaddress($in{"server_$i"}) ||
307                                         &error(&text('save_ecopyserver', $d));
308                                 $copy->{'server'} = $in{"server_$i"};
309                                 }
310
311                         &sdelete($copy, 'owner');
312                         if (!$in{"owner_def_$i"}) {
313                                 $in{"owner_$i"} =~ /^\S+$/ ||
314                                         &error(&text('save_ecopyowner', $d));
315                                 $copy->{'owner'} = $in{"owner_$i"};
316                                 }
317
318                         &sdelete($copy, 'group');
319                         if (!$in{"group_def_$i"}) {
320                                 $in{"group_$i"} =~ /^\S+$/ ||
321                                         &error(&text('save_ecopygroup', $d));
322                                 $copy->{'group'} = $in{"group_$i"};
323                                 }
324
325                         &sdelete($copy, 'mode');
326                         if (!$in{"mode_def_$i"}) {
327                                 $in{"mode_$i"} =~ /^\S+$/ ||
328                                         &error(&text('save_ecopymode', $d));
329                                 $copy->{'mode'} = $in{"mode_$i"};
330                                 }
331
332                         &sdelete($copy, 'recurse');
333                         if ($in{"rec_def_$i"} == 2) {
334                                 $copy->{'recurse'} = 'inf';
335                                 }
336                         elsif ($in{"rec_def_$i"} == 0) {
337                                 $in{"rec_$i"} =~ /^\d+$/ ||
338                                         &error(&text('save_ecopyrec', $d));
339                                 $copy->{'recurse'} = $in{"rec_$i"};
340                                 }
341
342                         &sdelete($copy, "size");
343                         if ($in{"size_mode_$i"} == 1) {
344                                 $in{"size1_$i"} ne '' ||
345                                         &error(&text('save_ecopysize', $d));
346                                 $copy->{'size'} = $in{"size1_$i"};
347                                 }
348                         elsif ($in{"size_mode_$i"} == 2) {
349                                 $in{"size2_$i"} ne '' ||
350                                         &error(&text('save_ecopysize', $d));
351                                 $copy->{'size'} = "<".$in{"size2_$i"};
352                                 }
353                         elsif ($in{"size_mode_$i"} == 3) {
354                                 $in{"size3_$i"} ne '' ||
355                                         &error(&text('save_ecopysize', $d));
356                                 $copy->{'size'} = ">".$in{"size3_$i"};
357                                 }
358
359                         &sdelete($copy, "backup");
360                         $copy->{'backup'} = 'false' if (!$in{"backup_$i"});
361
362                         &sdelete($copy, "force");
363                         $copy->{'force'} = 'true' if ($in{"force_$i"});
364
365                         &sdelete($copy, "purge");
366                         $copy->{'purge'} = 'true' if ($in{"purge_$i"});
367
368                         &sdelete($copy, "action");
369                         if ($in{"act_$i"}) {
370                                 $copy->{'action'} = $in{"act_$i"};
371                                 }
372
373                         push(@copies, $copy);
374                         }
375                 &unparse_directories($cls, @copies);
376                 }
377         elsif ($type eq "disable") {
378                 # Save disable lines
379                 local @odis = &parse_directories($cls);
380                 local @dis;
381                 for($i=0; defined($d = $in{"dir_$i"}); $i++) {
382                         next if ($in{"dir_def_$i"});
383                         local $dis = $odis[$i];
384                         $dis->{'_dir'} = $d;
385                         $d =~ /\S/ || &error(&text('save_edisfile', $i+1));
386
387                         &sdelete($dis, "rotate");
388                         if ($in{"rot_mode_$i"} == 1) {
389                                 $dis->{'rotate'} = 'empty';
390                                 }
391                         elsif ($in{"rot_mode_$i"} == 2) {
392                                 $in{"rot_$i"} =~ /^\d+$/ ||
393                                         &error(&text('save_edisrot', $d));
394                                 $dis->{'rotate'} = $in{"rot_$i"};
395                                 }
396
397                         &sdelete($dis, "type");
398                         if ($in{"type_$i"}) {
399                                 $dis->{'type'} = $in{"type_$i"};
400                                 }
401
402                         &sdelete($dis, "size");
403                         if ($in{"size_mode_$i"} == 1) {
404                                 $in{"size1_$i"} ne '' ||
405                                         &error(&text('save_edissize', $d));
406                                 $dis->{'size'} = $in{"size1_$i"};
407                                 }
408                         elsif ($in{"size_mode_$i"} == 2) {
409                                 $in{"size2_$i"} ne '' ||
410                                         &error(&text('save_edissize', $d));
411                                 $dis->{'size'} = "<".$in{"size2_$i"};
412                                 }
413                         elsif ($in{"size_mode_$i"} == 3) {
414                                 $in{"size3_$i"} ne '' ||
415                                         &error(&text('save_edissize', $d));
416                                 $dis->{'size'} = ">".$in{"size3_$i"};
417                                 }
418
419                         push(@dis, $dis);
420                         }
421                 &unparse_directories($cls, @dis);
422                 }
423         elsif ($type eq "editfiles") {
424                 # Save file-editing scripts
425                 for($i=0; defined($d = $in{"edit_$i"}); $i++) {
426                         local (@values, @valuelines);
427                         next if ($in{"edit_def_$i"});
428                         $d =~ /\S/ || &error(&text('save_eeditfile', $i+1));
429                         push(@values, $d);
430                         push(@valuelines, 0);
431                         push(@valuequotes, undef);
432
433                         $in{"script_$i"} =~ s/\r//g;
434                         local @lines = split(/\n/, $in{"script_$i"});
435                         for($j=0; $j<@lines; $j++) {
436                                 local ($st, $qu) = &split_str($lines[$j]);
437                                 push(@values, @$st);
438                                 push(@valuequotes, @$qu);
439                                 push(@valuelines, map { $j+1 } @$st);
440                                 }
441                         @values > 1 || &error(&text('save_eeditscript', $d));
442
443                         push(@lists, { 'values' => \@values,
444                                        'valuelines' => \@valuelines,
445                                        'valuequotes' => \@valuequotes } );
446                         }
447                 $cls->{'lists'} = \@lists;
448                 }
449         elsif ($type eq "ignore") {
450                 # Save list of ignored files
451                 local ($st, $qu) = &split_str($in{"ignore"});
452                 for($i=0; $i<@$st; $i++) {
453                         push(@values, $st->[$i]);
454                         push(@valuelines, $i);
455                         push(@valuequotes, $qu->[$i]);
456                         }
457                 $cls->{'values'} = \@values;
458                 $cls->{'valuelines'} = \@valuelines;
459                 $cls->{'valuequotes'} = \@valuequotes;
460                 }
461         elsif ($type eq "processes") {
462                 # Save managed processes list
463                 local $ostr;
464                 local @oprocs = &parse_processes($cls);
465                 local @procs;
466                 for($i=0; defined($p = $in{"proc_$i"}) || $i<@oprocs; $i++) {
467                         next if ($in{"proc_def_$i"});
468                         local $proc = $oprocs[$i];
469                         if ($proc->{'_options'}) {
470                                 push(@procs, $proc);
471                                 next;
472                                 }
473                         $proc->{'_match'} = $p;
474                         $p =~ /\S/ || &error(&text('save_eproc', $i+1));
475
476                         &sdelete($proc, "signal");
477                         if ($in{"sig_$i"}) {
478                                 $proc->{'signal'} = $in{"sig_$i"};
479                                 }
480
481                         &sdelete($proc, "action");
482                         if ($in{"act_$i"}) {
483                                 $proc->{'action'} = $in{"act_$i"};
484                                 }
485
486                         &sdelete($proc, "matches");
487                         if ($in{"mat_mode_$i"} == 1) {
488                                 $in{"mat1_$i"} ne '' ||
489                                         &error(&text('save_eprocmat', $d));
490                                 $proc->{'matches'} = $in{"mat1_$i"};
491                                 }
492                         elsif ($in{"mat_mode_$i"} == 2) {
493                                 $in{"mat2_$i"} ne '' ||
494                                         &error(&text('save_eprocmat', $d));
495                                 $proc->{'matches'} = "<".$in{"mat2_$i"};
496                                 }
497                         elsif ($in{"mat_mode_$i"} == 3) {
498                                 $in{"mat3_$i"} ne '' ||
499                                         &error(&text('save_eprocmat', $d));
500                                 $proc->{'matches'} = ">".$in{"mat3_$i"};
501                                 }
502
503                         delete($proc->{'_restart'});
504                         if (!$in{"restart_def_$i"}) {
505                                 $in{"restart_$i"} =~ /\S/ ||
506                                         &error(&text('save_eprocrestart', $p));
507                                 $proc->{'_restart'} = $in{"restart_$i"};
508                                 }
509
510                         &sdelete($proc, 'owner');
511                         if (!$in{"owner_def_$i"}) {
512                                 $in{"owner_$i"} =~ /^\S+$/ ||
513                                         &error(&text('save_eprocowner', $d));
514                                 $proc->{'owner'} = $in{"owner_$i"};
515                                 }
516
517                         &sdelete($proc, 'group');
518                         if (!$in{"group_def_$i"}) {
519                                 $in{"group_$i"} =~ /^\S+$/ ||
520                                         &error(&text('save_eprocgroup', $d));
521                                 $proc->{'group'} = $in{"group_$i"};
522                                 }
523
524                         push(@procs, $proc);
525                         }
526                 &unparse_processes($cls, @procs);
527                 }
528         elsif ($type eq "shellcommands") {
529                 # Save commands to execute
530                 local @ocmds = &parse_directories($cls);
531                 local @cmds;
532                 for($i=0; defined($in{"cmd_$i"}); $i++) {
533                         next if (!$in{"cmd_$i"});
534                         local $cmd = $ocmd[$i];
535                         $in{"cmd_$i"} =~ /\S/ ||
536                                 &error(&text('save_ecmd', $i+1));
537                         $cmd->{'_dir'} = $in{"cmd_$i"};
538
539                         &sdelete($cmd, 'owner');
540                         if ($in{"owner_$i"} ne "") {
541                                 $in{"owner_$i"} =~ /^\S+$/ ||
542                                         &error(&text('save_ecmdowner', $i+1));
543                                 $cmd->{'owner'} = $in{"owner_$i"};
544                                 }
545
546                         &sdelete($cmd, 'group');
547                         if ($in{"group_$i"} ne "") {
548                                 $in{"group_$i"} =~ /^\S+$/ ||
549                                         &error(&text('save_ecmdgroup', $i+1));
550                                 $cmd->{'group'} = $in{"group_$i"};
551                                 }
552
553                         &sdelete($cmd, "timeout");
554                         if ($in{"timeout_$i"} ne '') {
555                                 $in{"timeout_$i"} =~ /^\d+$/ ||
556                                         &error(&text('save_ecmdtimeout', $i+1));
557                                 $cmd->{'timeout'} = $in{"timeout_$i"};
558                                 }
559
560                         push(@cmds, $cmd);
561                         }
562                 &unparse_shellcommands($cls, @cmds);
563                 }
564         elsif ($type eq "tidy") {
565                 # Save tidied directories
566                 local @otidy = &parse_directories($cls);
567                 local @tidy;
568                 for($i=0; defined($d = $in{"dir_$i"}); $i++) {
569                         next if ($in{"dir_def_$i"});
570                         local $tidy = $otidy[$i];
571                         $d =~ /^\S+$/ || &error(&text('save_etidy', $i+1));
572                         $tidy->{'_dir'} = $d;
573
574                         &sdelete($tidy, "pattern");
575                         if (!$in{"pat_def_$i"}) {
576                                 $in{"pat_$i"} =~ /^\S+$/ ||
577                                         &error(&text('save_etidypat', $d));
578                                 $tidy->{'pattern'} = $in{"pat_$i"};
579                                 }
580
581                         &sdelete($tidy, "size");
582                         if ($in{"smode_$i"} == 1) {
583                                 $tidy->{'size'} = 'empty';
584                                 }
585                         elsif ($in{"smode_$i"} == 2) {
586                                 $in{"size_$i"} =~ /^\S+$/ ||
587                                         &error(&text('save_etidysize', $d));
588                                 $tidy->{'size'} = $in{"size_$i"};
589                                 }
590
591                         &sdelete($tidy, "age");
592                         &sdelete($tidy, "type");
593                         if ($in{"type_$i"}) {
594                                 $tidy->{'type'} = $in{"type_$i"};
595                                 }
596                         if (!$in{"age_def_$i"}) {
597                                 $in{"age_$i"} =~ /^\d+$/ ||
598                                         &error(&text('save_etidyage', $d));
599                                 $tidy->{'age'} = $in{"age_$i"};
600                                 }
601
602                         &sdelete($tidy, 'recurse');
603                         if ($in{"rec_def_$i"} == 2) {
604                                 $tidy->{'recurse'} = 'inf';
605                                 }
606                         elsif ($in{"rec_def_$i"} == 0) {
607                                 $in{"rec_$i"} =~ /^\d+$/ ||
608                                         &error(&text('save_etidyrec', $d));
609                                 $tidy->{'recurse'} = $in{"rec_$i"};
610                                 }
611
612                         push(@tidy, $tidy);
613                         }
614                 &unparse_directories($cls, @tidy);
615                 }
616         elsif ($type eq "miscmounts") {
617                 # Save mounted NFS filesystems
618                 local @omnts = &parse_miscmounts($cls);
619                 local @mnts;
620                 for($i=0; defined($d = $in{"src_$i"}); $i++) {
621                         next if (!$d);
622                         local $mnt = $omnts[$i];
623
624                         $d =~ /^\S+$/ ||
625                                 &error(&text('save_emiscsrc', $i+1));
626                         $mnt->{'_src'} = $d;
627
628                         $in{"dest_$i"} =~ /^\S+$/ ||
629                                 &error(&text('save_emiscdest', $d));
630                         $mnt->{'_dest'} = $in{"dest_$i"};
631
632                         &sdelete($mnt, "mode");
633                         $in{"mode_$i"} =~ /^\S*$/ ||
634                                 &error(&text('save_emiscmode', $d));
635                         $mnt->{'mode'} = $in{"mode_$i"} if ($in{"mode_$i"});
636
637                         push(@mnts, $mnt);
638                         }
639                 &unparse_miscmounts($cls, @mnts);
640                 }
641         elsif ($type eq "resolve") {
642                 # Save nameserver options
643                 $in{'ns'} =~ s/\r//g;
644                 local @ns = split(/\n/, $in{'ns'});
645                 $in{'other'} =~ s/\r//g;
646                 local @other = split(/\n/, $in{'other'});
647
648                 local $vl = 0;
649                 foreach $n (@ns) {
650                         push(@values, $n);
651                         push(@valuelines, $vl++);
652                         push(@valuequotes, "");
653                         }
654                 foreach $n (@other) {
655                         push(@values, $n);
656                         push(@valuelines, $vl++);
657                         push(@valuequotes, '"');
658                         }
659
660                 $cls->{'values'} = \@values;
661                 $cls->{'valuelines'} = \@valuelines;
662                 $cls->{'valuequotes'} = \@valuequotes;
663                 }
664         elsif ($type eq "defaultroute") {
665                 # Save default router options
666                 $in{'route'} =~ /^\S+$/ || &error($text{'save_eroute'});
667                 $cls->{'values'} = [ $in{'route'} ];
668                 $cls->{'valuelines'} = 0;
669                 $cls->{'valuequotes'} = [ ];
670                 }
671         elsif ($type eq "required" || $type eq "disks") {
672                 # Save filesystems to check
673                 local @oreqs = &parse_directories($cls);
674                 local @reqs;
675                 for($i=0; defined($d = $in{"fs_$i"}); $i++) {
676                         next if (!$d);
677                         local $req = $oreqs[$i];
678                         $d =~ /^\S+$/ || &error(&text('save_ereq', $i+1));
679                         $req->{'_dir'} = $d;
680
681                         &sdelete($req, "freespace");
682                         if (!$in{"free_def_$i"}) {
683                                 $in{"free_$i"} =~ /^\S+$/ ||
684                                         &error(&text('save_ereqfree', $d));
685                                 $req->{'freespace'} = $in{"free_$i"};
686                                 }
687
688                         push(@reqs, $req);
689                         }
690                 &unparse_directories($cls, @reqs);
691                 }
692
693         # Write to the config file
694         if ($in{'cidx'} ne '') {
695                 # Updating an existing class
696                 &lock_file($sec->{'file'});
697                 &save_directive($conf, $cls, $cls);
698                 &flush_file_lines();
699                 &unlock_file($sec->{'file'});
700                 &webmin_log("modify", "class", $sec->{'name'});
701                 }
702         elsif ($in{'idx'} ne '') {
703                 # Adding a class to an existing section
704                 &lock_file($sec->{'file'});
705                 &save_directive($sec->{'cls'}, undef, $cls);
706                 &flush_file_lines();
707                 &unlock_file($sec->{'file'});
708                 &webmin_log("create", "class", $sec->{'name'});
709                 }
710         else {
711                 # Creating a new section and class
712                 &lock_file($conf->[0]->{'file'});
713                 &save_directive($conf, undef, $sec);
714                 &flush_file_lines();
715                 &unlock_file($conf->[0]->{'file'});
716                 &webmin_log("create", "section", $sec->{'name'});
717                 }
718
719         &redirect($in{'cfd'} ? "edit_cfd.cgi" : "");
720         }
721
722 # save_define(&config, name, &values|undef)
723 sub save_define
724 {
725 local ($i, $old);
726 for($i=0; $i<@{$_[0]}; $i++) {
727         if ($_[0]->[$i]->{'name'} eq $_[1]) {
728                 $old = $_[0]->[$i];
729                 last;
730                 }
731         }
732 if ($old && $_[2]) {
733         $_[0]->[$i]->{'values'} = $_[2];
734         }
735 elsif ($old) {
736         splice(@{$_[0]}, $i, 1);
737         }
738 elsif ($_[2]) {
739         push(@{$_[0]}, { 'name' => $_[1], 'values' => $_[2] } );
740         }
741 }
742
743 # sdelete(&conf, name)
744 sub sdelete
745 {
746 local $i;
747 for($i=length($_[1]); $i>0; $i--) {
748         local $s = substr($_[1], 0, $i);
749         if (defined($_[0]->{$s})) {
750                 delete($_[0]->{$s});
751                 last;
752                 }
753         }
754 }
755