Handle hostnames with upper-case letters
[webmin.git] / config-lib.pl
1 # config-lib.pl
2 # Common functions for parsing config.info files
3 # Each module has a number of configurable parameters (stored in the config and
4 # config-* files in the module directory). Descriptions and possible values for
5 # each option are stored in the file config.info in the module directory.
6 # Each line of config.info looks like
7 # name=desc,type[,options]
8 #  desc - A description of the parameter
9 #  type - Possible types (and options) are
10 #       0 - Free text
11 #       1 - One of many (options are possibilities)
12 #       2 - Many of many (options are possibilities)
13 #       3 - Optional free text
14 #       4 - Like 1, but uses a pulldown menu
15 #       5 - User name
16 #       6 - Group name
17 #       7 - Directory
18 #       8 - File
19 #       9 - Multiline text
20 #       10 - Like 1, but with free text option
21 #       11 - Section header
22 #       12 - Password free text, with don't change option
23 #       13 - Like 2, but uses a list box
24 #       14 - Parameter is the name of a function in config_info.pl that
25 #            returns an alternate set of config.info values.
26 #       15 - Parameter is the suffix for a pair of functions with show_
27 #            and parse_ prepended.
28 #       16 - Password free text
29
30 # generate_config(&config, info-file, [module], [&can-config], [checkbox-name],
31 #                 [only-section])
32 # Prints HTML for 
33 sub generate_config
34 {
35 my ($configref, $file, $module, $canconfig, $cbox, $section) = @_;
36 my %config = %$configref;
37
38 # Read the .info file in the right language
39 my (%info, @info_order, %einfo, $o);
40 &read_file($file, \%info, \@info_order);
41 %einfo = %info;
42 foreach $o (@lang_order_list) {
43         &read_file("$file.$o", \%info, \@info_order);
44         }
45 @info_order = &unique(@info_order);
46
47 if ($section) {
48         # Limit to settings in one section
49         @info_order = &config_in_section($section, \@info_order, \%info);
50         }
51
52 # Show the parameter editors
53 foreach my $c (@info_order) {
54         my $checkhtml;
55         if ($cbox) {
56                 # Show checkbox to allow configuring
57                 $checkhtml = &ui_checkbox($cbox, $c, "",
58                                           !$canconfig || $canconfig->{$c});
59                 }
60         else {
61                 # Skip those not allowed to be configured
62                 next if ($canconfig && !$canconfig->{$c});
63                 }
64         my @p = split(/,/, $info{$c});
65         my @ep = split(/,/, $einfo{$c});
66         if (scalar(@ep) > scalar(@p)) {
67                 push(@p, @ep[scalar(@p) .. @ep-1]);
68                 }
69         if ($p[1] == 14) {
70                 $module || &error($text{'config_ewebmin'});
71                 &foreign_require($module, "config_info.pl");
72                 my @newp = &foreign_call($module, $p[2], @p);
73                 $newp[0] ||= $p[0];
74                 @p = @newp;
75                 }
76         if ($p[1] == 11) {
77                 # Title row
78                 print &ui_table_row(undef, "<b>$p[0]</b>", 2, [ undef, $tb ]);
79                 next;
80                 }
81         if ($p[1] == 16 && $gconfig{'config_16_insecure'}) {
82                 # Don't allow mode 16
83                 $p[1] = 12;
84                 }
85         my $label;
86         if ($module && -r &help_file($module, "config_$c")) {
87                 $label = $checkhtml." ".
88                          &hlink($p[0], "config_$c", $module);
89                 }
90         else {
91                 $label = $checkhtml." ".$p[0];
92                 }
93         my $field;
94         if ($p[1] == 0) {
95                 # Text value
96                 $field = &ui_textbox($c, $config{$c}, $p[2] || 40, 0, $p[3]).
97                          " ".$p[4];
98                 }
99         elsif ($p[1] == 1) {
100                 # One of many
101                 my $len = 0;
102                 for(my $i=2; $i<@p; $i++) {
103                         $p[$i] =~ /^(\S*)\-(.*)$/;
104                         $len += length($2);
105                         }
106                 my @opts;
107                 for($i=2; $i<@p; $i++) {
108                         $p[$i] =~ /^(\S*)\-(.*)$/;
109                         push(@opts, [ $1, $2.($len > 50 ? "<br>" : "") ]);
110                         }
111                 $field = &ui_radio($c, $config{$c}, \@opts);
112                 }
113         elsif ($p[1] == 2) {
114                 # Many of many
115                 my %sel;
116                 map { $sel{$_}++ } split(/,/, $config{$c});
117                 for($i=2; $i<@p; $i++) {
118                         $p[$i] =~ /^(\S*)\-(.*)$/;
119                         $field .= &ui_checkbox($c, $1, $2, $sel{$1});
120                         }
121                 }
122         elsif ($p[1] == 3) {
123                 # Optional value
124                 my $none = $p[2] || $text{'config_none'};
125                 $field = &ui_opt_textbox($c, $config{$c}, $p[3] || 20, $none,
126                                          $p[6], 0, undef, $p[4])." ".$p[5];
127                 }
128         elsif ($p[1] == 4) {
129                 # One of many menu
130                 my @opts;
131                 for($i=2; $i<@p; $i++) {
132                         $p[$i] =~ /^(\S*)\-(.*)$/;
133                         push(@opts, [ $1, $2 ]);
134                         }
135                 $field = &ui_select($c, $config{$c}, \@opts);
136                 }
137         elsif ($p[1] == 5) {
138                 # User chooser
139                 if ($p[2]) {
140                         $field = &ui_radio($c."_def", $config{$c} ? 0 : 1,
141                                            [ [ 1, $p[2] ], [ 0, " " ] ]);
142                         }
143                 if ($p[3]) {
144                         $field .= &ui_textbox($c, $config{$c}, 30)." ".
145                                   &user_chooser_button($c, 1);
146                         }
147                 else {
148                         $field .= &unix_user_input($c, $config{$c});
149                         }
150                 }
151         elsif ($p[1] == 6) {
152                 # Group chooser
153                 if ($p[2]) {
154                         $field = &ui_radio($c."_def", $config{$c} ? 0 : 1,
155                                            [ [ 1, $p[2] ], [ 0, " " ] ]);
156                         }
157                 if ($p[3]) {
158                         $field .= &ui_textbox($c, $config{$c}, 30)." ".
159                                   &group_chooser_button($c, 1);
160                         }
161                 else {
162                         $field .= &unix_group_input($c, $config{$c});
163                         }
164                 }
165         elsif ($p[1] == 7) {
166                 # Directory chooser
167                 $field = &ui_textbox($c, $config{$c}, 40)." ".
168                          &file_chooser_button($c, 1);
169                 }
170         elsif ($p[1] == 8) {
171                 # File chooser
172                 $field = &ui_textbox($c, $config{$c}, 40)." ".
173                          &file_chooser_button($c, 0);
174                 }
175         elsif ($p[1] == 9) {
176                 # Text area
177                 my $cols = $p[2] || 40;
178                 my $rows = $p[3] || 5;
179                 my $sp = $p[4] ? eval "\"$p[4]\"" : " ";
180                 $field = &ui_textarea($c, join("\n", split(/$sp/, $config{$c})),
181                                       $rows, $cols);
182                 }
183         elsif ($p[1] == 10) {
184                 # Radios with freetext option
185                 my $len = 20;
186                 for(my $i=2; $i<@p; $i++) {
187                         if ($p[$i] =~ /^(\S*)\-(.*)$/) {
188                                 $len += length($2);
189                                 }
190                         else {
191                                 $len += length($p[$i]);
192                                 }
193                         }
194                 my $fv = $config{$c};
195                 my @opts;
196                 for(my $i=2; $i<@p; $i++) {
197                         ($p[$i] =~ /^(\S*)\-(.*)$/) || next;
198                         push(@opts, [ $1, $2.($len > 50 ? "<br>" : "") ]);
199                         $fv = undef if ($config{$c} eq $1);
200                         }
201                 push(@opts, [ "free", $p[$#p] !~ /^(\S*)\-(.*)$/ ? $p[$#p]
202                                                                  : " " ]);
203                 $field = &ui_radio($c, $fv ? "free" : $config{$c}, \@opts)." ".
204                          &ui_textbox($c."_free", $fv, 20);
205                 }
206         elsif ($p[1] == 12) {
207                 # Password field
208                 $field = &ui_radio($c."_nochange", 1,
209                                    [ [ 1, $text{'config_nochange'} ],
210                                      [ 0, $text{'config_setto'} ] ])." ".
211                          &ui_password($c, undef, $p[2] || 40, 0, $p[3]);
212                 }
213         elsif ($p[1] == 13) {
214                 # Multiple selections from menu
215                 my @sel = split(/,/, $config{$c});
216                 my @opts;
217                 for($i=2; $i<@p; $i++) {
218                         $p[$i] =~ /^(\S*)\-(.*)$/;
219                         push(@opts, [ $1, $2 ]);
220                         }
221                 $field = &ui_select($c, \@sel, \@opts, 5, 1);
222                 }
223         elsif ($p[1] == 15) {
224                 # Input generated by function
225                 $module || &error($text{'config_ewebmin'});
226                 &foreign_require($module, "config_info.pl");
227                 $field = &foreign_call($module, "show_".$p[2],
228                                        $config{$c}, @p);
229                 }
230         elsif ($p[1] == 16) {
231                 # Password free text
232                 $field = &ui_password($c, undef, $p[2] || 40, 0, $p[3]);
233                 }
234         $label = "<a name=$c>$label</a>";
235         print &ui_table_row($label, $field, 1, [ "width=30% nowrap" ]);
236         }
237 }
238
239 # parse_config(&config, info-file, [module], [&canconfig], [section])
240 # Updates the specified configuration with values from %in
241 sub parse_config
242 {
243 my ($config, $file, $module, $canconfig, $section) = @_;
244
245 # Read the .info file
246 my (%info, @info_order, $o);
247 &read_file($file, \%info, \@info_order);
248 foreach $o (@lang_order_list) {
249         &read_file("$file.$o", \%info, \@info_order);
250         }
251 @info_order = &unique(@info_order);
252
253 if ($section) {
254         # Limit to settings in one section
255         @info_order = &config_in_section($section, \@info_order, \%info);
256         }
257
258 # Actually parse the inputs
259 foreach my $c (@info_order) {
260         next if ($canconfig && !$canconfig->{$c});
261         my @p = split(/,/, $info{$c});
262         if ($p[1] == 14) {
263                 $_[2] || &error($text{'config_ewebmin'});
264                 &foreign_require($_[2], "config_info.pl");
265                 my @newp = &foreign_call($_[2], $p[2]);
266                 $newp[0] ||= $p[0];
267                 @p = @newp;
268                 }
269         if ($p[1] == 16 && $gconfig{'config_16_insecure'}) {
270                 # Don't allow mode 16
271                 $p[1] = 12;
272                 }
273         if ($p[1] == 0 || $p[1] == 7 || $p[1] == 8 || $p[1] == 16) {
274                 # Free text input
275                 $config->{$c} = $in{$c};
276                 }
277         elsif ($p[1] == 1 || $p[1] == 4) {
278                 # One of many
279                 $config->{$c} = $in{$c};
280                 }
281         elsif ($p[1] == 5 || $p[1] == 6) {
282                 # User or group
283                 $config->{$c} = ($p[2] && $in{$c."_def"} ? "" : $in{$c});
284                 }
285         elsif ($p[1] == 2 || $p[1] == 13) {
286                 # Many of many
287                 $in{$c} =~ s/\0/,/g;
288                 $config->{$c} = $in{$c};
289                 }
290         elsif ($p[1] == 3) {
291                 # Optional free text
292                 if ($in{$c."_def"}) { $config->{$c} = ""; }
293                 else { $config->{$c} = $in{$c}; }
294                 }
295         elsif ($p[1] == 9) {
296                 # Multilines of free text
297                 my $sp = $p[4] ? eval "\"$p[4]\"" : " ";
298                 $in{$c} =~ s/\r//g;
299                 $in{$c} =~ s/\n/$sp/g;
300                 $in{$c} =~ s/\s+$//;
301                 $config->{$c} = $in{$c};
302                 }
303         elsif ($p[1] == 10) {
304                 # One of many or free text
305                 if ($in{$c} eq 'free') {
306                         $config->{$c} = $in{$c.'_free'};
307                         }
308                 else {
309                         $config->{$c} = $in{$c};
310                         }
311                 }
312         elsif ($p[1] == 12) {
313                 # Optionally changed password
314                 if (!$in{"${c}_nochange"}) {
315                         $config->{$c} = $in{$c};
316                         }
317                 }
318         elsif ($p[1] == 15) {
319                 # Parse custom HTML field
320                 $_[2] || &error($text{'config_ewebmin'});
321                 &foreign_require($_[2], "config_info.pl");
322                 $config->{$c} = &foreign_call($_[2], "parse_".$p[2],
323                                             $config->{$c}, @p);
324                 }
325         }
326 }
327
328 # config_in_section(&section, &order, &config-info)
329 # Returns a list of config names that are in some section
330 sub config_in_section
331 {
332 my ($section, $info_order, $info) = @_;
333 my @new_order = ( );
334 my $in_section = 0;
335 foreach my $c (@$info_order) {
336         my @p = split(/,/, $info->{$c});
337         if ($p[1] == 11 && $c eq $section) {
338                 $in_section = 1;
339                 }
340         elsif ($p[1] == 11 && $c ne $section) {
341                 $in_section = 0;
342                 }
343         elsif ($in_section) {
344                 push(@new_order, $c);
345                 }
346         }
347 return @new_order;
348 }
349
350 1;
351