Handle hostnames with upper-case letters
[webmin.git] / sarg / sarg-lib.pl
1 # Functions for reading and writing sarg.conf
2
3 BEGIN { push(@INC, ".."); };
4 use WebminCore;
5 &init_config();
6
7 $cron_cmd = "$module_config_directory/generate.pl";
8 %needs_quotes = map { $_, 1 } ("title", "logo_text", "user_invalid_char",
9                                "privacy_string", "include_users",
10                                "exclude_string", "datafile_delimiter");
11
12 # get_config()
13 # Parses the sarg config file into directives
14 sub get_config
15 {
16 if (!scalar(@get_config_cache)) {
17         local $lnum = 0;
18         open(CONF, $config{'sarg_conf'});
19         while(<CONF>) {
20                 s/\r|\n//g;
21                 if (/^\s*(#*)\s*(\S+)\s+"([^"]*)"/ ||
22                     /^\s*(#*)\s*(\S+)\s+'([^"]*)'/ ||
23                     /^\s*(#*)\s*(\S+)\s+(.*\S)/) {
24                         push(@get_config_cache,
25                                   { 'name' => $2,
26                                     'value' => $3,
27                                     'enabled' => !$1,
28                                     'line' => $lnum });
29                         }
30                 $lnum++;
31                 }
32         close(CONF);
33         }
34 return \@get_config_cache;
35 }
36
37 # find(name, &config, [and-disabled])
38 sub find
39 {
40 local @rv;
41 local $c;
42 foreach $c (@{$_[1]}) {
43         if (lc($c->{'name'}) eq lc($_[0])) {
44                 if ($c->{'enabled'} && $_[2] == 0 ||
45                     !$c->{'enabled'} && $_[2] == 2 ||
46                                        $_[2] == 1) {
47                         push(@rv, $c);
48                         }
49                 }
50         }
51 return wantarray ? @rv : $rv[0];
52 }
53
54 # find_value(name, &config)
55 sub find_value
56 {
57 local @rv = map { $_->{'value'} } &find(@_);
58 return wantarray ? @rv : $rv[0];
59 }
60
61 # save_directive(&config, name, &values)
62 # Update the config file with some new values for an option
63 sub save_directive
64 {
65 local ($conf, $name, $values) = @_;
66 local $lref = &read_file_lines($config{'sarg_conf'});
67 local @old = &find($name, $conf);
68 local @new = @$values;
69 local $i;
70 local $changed;
71 for($i=0; $i<@old || $i<@new; $i++) {
72         local $newline;
73         if ($i<@new) {
74                 if ($needs_quotes{$name}) {
75                         $newline = $name." \"".$new[$i]."\"";
76                         }
77                 else {
78                         $newline = $name." ".$new[$i];
79                         }
80                 }
81
82         if ($i<@old && $i<@new) {
83                 # Updating some directive
84                 $lref->[$old[$i]->{'line'}] = $newline;
85                 $old[$i]->{'value'} = $new[$i];
86                 $changed = $old[$i];
87                 }
88         elsif ($i<@old) {
89                 # Removing some directive (need to renumber)
90                 splice(@$lref, $old[$i]->{'line'}, 1);
91                 splice(@$conf, $old[$i]->{'index'}, 1);
92                 &renumber($conf, 'line', $old[$i]->{'line'}, -1);
93                 &renumber($conf, 'index', $old[$i]->{'index'}, -1);
94                 }
95         elsif ($i<@new) {
96                 # Adding some directive (perhaps after comment)
97                 local $nd = { 'name' => $name,
98                               'value' => $new[$i] };
99                 local $cmt = &find($name, $conf, 1);
100                 if ($cmt && !$cmt->{'enabled'}) {
101                         # Add after comment line
102                         $nd->{'line'} = $cmt->{'line'} + 1;
103                         $nd->{'index'} = $cmt->{'index'} + 1;
104                         &renumber($conf, 'line', $nd->{'line'}-1, 1);
105                         &renumber($conf, 'index', $nd->{'index'}-1, 1);
106                         splice(@$lref, $nd->{'line'}, 0, $newline);
107                         splice(@$conf, $nd->{'index'}, 0, $nd);
108                         }
109                 else {
110                         # Add to end
111                         $nd->{'line'} = scalar(@$lref);
112                         $nd->{'index'} = scalar(@$conf);
113                         push(@$lref, $newline);
114                         push(@$conf, $nd);
115                         }
116                 }
117         }
118 }
119
120 # renumber(&conf, field, pos, offset)
121 sub renumber
122 {
123 local ($conf, $field, $pos, $offset) = @_;
124 local $c;
125 foreach $c (@$conf) {
126         if ($c->{$field} > $pos) {
127                 $c->{$field} += $offset;
128                 }
129         }
130 }
131
132
133
134 # config_textbox(&config, name, size, cols, [prefix])
135 # Returns HTML for a table row for some text field
136 sub config_textbox
137 {
138 local ($conf, $name, $size, $cols, $prefix) = @_;
139 $cols ||= 1;
140 local $val = join(" ", &find_value($name, $conf));
141 return &ui_table_row($text{$config_prefix.$name},
142                      &ui_textbox($name, $val, $size)." ".$prefix,
143                      $cols, \@ui_tds);
144 }
145
146 # save_textbox(config, name, [function])
147 sub save_textbox
148 {
149 local ($conf, $name, $func) = @_;
150 local @vals = $split ? split(/\s+/, $in{$name}) : ( $in{$name} );
151 local $v;
152 foreach $v (@vals) {
153         &error_check($name, $func, $v);
154         }
155 &save_directive($conf, $name, \@vals);
156 }
157
158 # error_check(name, func, value)
159 sub error_check
160 {
161 local ($name, $func, $val) = @_;
162 return if (!$func);
163 local $err = &$func($val);
164 &error($text{$config_prefix.$name}." : ".$err) if ($err);
165 }
166
167 # config_opt_textbox(&config, name, size, cols, [default])
168 # Returns HTML for a table row for some optional text field
169 sub config_opt_textbox
170 {
171 local ($conf, $name, $size, $cols, $default) = @_;
172 $cols ||= 1;
173 local $val = &find_value($name, $conf);
174 local $defstr = &find($name, $conf, 2);
175 local $def = $defstr && $defstr->{'value'} ? " ($defstr->{'value'})" : undef;
176 return &ui_table_row($text{$config_prefix.($name eq "title" ? "title2" :$name)},
177                      &ui_radio($name."_def", $val ? 0 : 1,
178                                [ [ 1, $default || $text{'default'}.$def ],
179                                  [ 0, " " ] ])." ".
180                      &ui_textbox($name, $val, $size), $cols, \@ui_tds);
181 }
182
183 # save_opt_textbox(config, name, [function], [split])
184 sub save_opt_textbox
185 {
186 local ($conf, $name, $func, $split) = @_;
187 if ($in{$name."_def"}) {
188         &save_directive($conf, $name, [ ]);
189         }
190 else {
191         &save_textbox(@_);
192         }
193 }
194
195 # config_yesno(&config, name, [yes], [no], [&others])
196 # Returns HTML for a table row for yes/no checkboxes
197 sub config_yesno
198 {
199 local ($conf, $name, $yes, $no, $others, $width) = @_;
200 local $val = &find_value($name, $conf);
201 local $default = &find($name, $conf, 2);
202 #local $defstr = $default ? " ($default->{'value'})" : "";
203 return &ui_table_row($text{$config_prefix.$name},
204                      &ui_radio($name, $val,
205                                [ [ "yes", $yes || $text{'yes'} ],
206                                  [ "no", $no || $text{'no'} ],
207                                  @$others,
208                                  [ "", $text{'default'}.$defstr ] ]),
209                      $width, \@ui_tds);
210 }
211
212 # save_yesno(&config, name)
213 sub save_yesno
214 {
215 local ($conf, $name) = @_;
216 if (!$in{$name}) {
217         &save_directive($conf, $name, [ ]);
218         }
219 else {
220         &save_directive($conf, $name, [ $in{$name} ]);
221         }
222 }
223
224 # config_radio(&config, name, [&options], [width])
225 # Returns HTML for a table row for a bunch of checkboxes
226 sub config_radio
227 {
228 local ($conf, $name, $others, $width) = @_;
229 local $val = &find_value($name, $conf);
230 local $default = &find($name, $conf, 2);
231 #local $defstr = $default ? " ($default->{'value'})" : "";
232 return &ui_table_row($text{$config_prefix.$name},
233                      &ui_radio($name, $val,
234                                [ @$others,
235                                  [ "", $text{'default'}.$defstr ] ]),
236                      $width, \@ui_tds);
237 }
238
239 sub save_radio
240 {
241 &save_yesno(@_);        # same logic works
242 }
243
244 # config_sortfield(&config, name, &opts, [&sort-opts])
245 # Returns HTML for a table row for selecting a sort mode
246 sub config_sortfield
247 {
248 local ($conf, $name, $opts, $sort) = @_;
249 local $val = &find_value($name, $conf);
250 local $default = &find($name, $conf, 2);
251 #local $defstr = $default ? " ($default->{'value'})" : "";
252 local @vals = split(/\s+/, $val);
253 $sort ||= [ [ "reverse", $text{'report_reverse'} ],
254             [ "forward", $text{'report_forward'} ] ];
255 return &ui_table_row($text{$config_prefix.$name},
256         &ui_oneradio($name."_def", 1, $text{'default'}.$defstr, !$val)."\n".
257         &ui_oneradio($name."_def", 0, $text{'report_field'}, $val)."\n".
258         &ui_select($name."_field", $vals[0],
259                    [ map { [ $_, $text{'report_by_'.lc($_)} ] } @$opts ])."\n".
260         &ui_select($name."_order", $vals[1], $sort), 3);
261 }
262
263 sub save_sortfield
264 {
265 local ($conf, $name) = @_;
266 if ($in{$name."_def"}) {
267         &save_directive($conf, $name, [ ]);
268         }
269 else {
270         &save_directive($conf, $name, [ $in{$name."_field"}." ".
271                                         $in{$name."_order"} ]);
272         }
273 }
274
275 # config_language(&config, name, width, file)
276 sub config_language
277 {
278 local ($conf, $name, $width, $file) = @_;
279 local $val = &find_value($name, $conf);
280 local $default = &find($name, $conf, 2);
281 local $defstr = $default ? " ($default->{'value'})" : "";
282 local $found;
283 local @langs = ( [ "", $text{'default'}.$defstr ] );
284 open(LANGS, $file);
285 while(<LANGS>) {
286         chop;
287         if (/^(\S+)\t+(\S.*)/) {
288                 push(@langs, [ $1, "$1 - $2" ]);
289                 }
290         elsif (/^(\S+)/) {
291                 push(@langs, [ $1 ]);
292                 }
293         $found++ if ($val eq $_);
294         }
295 close(LANGS);
296 push(@langs, [ $val ]) if (!$found && $val);
297 return &ui_table_row($text{$config_prefix.$name},
298                      &ui_select($name, $val, \@langs), $width);
299 }
300
301 # save_language(&config, name)
302 sub save_language
303 {
304 &save_yesno(@_);        # same logic works
305 }
306
307 # config_select(&config, name, &options, default, width)
308 sub config_select
309 {
310 local ($conf, $name, $others, $default, $width) = @_;
311 local $val = &find_value($name, $conf);
312 local @vals = split(/\s+/, $val);
313 return &ui_table_row($text{$config_prefix.$name},
314                      &ui_radio($name."_def", $val ? 0 : 1,
315                                [ [ 1, $default || $text{'default'} ],
316                                  [ 0, $text{'report_below'} ] ])."<br>".
317                      &ui_select($name, \@vals, $others, "5 ", 1),
318                      $width, \@ui_tds);
319
320 }
321
322 sub save_select
323 {
324 local ($conf, $name) = @_;
325 $in{$name} =~ s/\0/ /g;
326 $in{$name."_def"} || $in{$name} || &error($text{'report_eselect'});
327 &save_opt_textbox(@_);  # same logic works
328 }
329
330 # config_colons(&config, name, separator, default, width)
331 sub config_colons
332 {
333 local ($conf, $name, $sep, $default, $width) = @_;
334 local $val = &find_value($name, $conf);
335 return &ui_table_row($text{$config_prefix.$name},
336                      &ui_radio($name."_def", $val ? 0 : 1,
337                                [ [ 1, $default || $text{'default'} ],
338                                  [ 0, $text{'report_below2'} ] ])."<br>".
339                      &ui_textarea($name, join("\n", split($sep, $val)),
340                                   3, 30),
341                      $width, \@ui_tds);
342 }
343
344 # save_colons(&config, name, separator)
345 sub save_colons
346 {
347 local ($conf, $name, $sep) = @_;
348 $in{$name} =~ s/\r//g;
349 $in{$name} =~ s/\s+$//;
350 $in{$name} =~ s/^\s+//;
351 $in{$name} =~ s/\n/$sep/g;
352 $in{$name."_def"} || $in{$name} || &error($text{'report_eenter'});
353 &save_opt_textbox($conf, $name);        # same logic works
354 }
355
356 # config_range(&config, name, start, end, width)
357 sub config_range
358 {
359 local ($conf, $name, $start, $end, $width) = @_;
360 local $val = &find_value($name, $conf);
361 local (%sel, $v, $vn);
362 foreach $v (split(/,/, $val)) {
363         if ($v =~ /^(\d+)\-(\d+)$/) {
364                 foreach $vn ($1 .. $2) { $sel{$vn}++; }
365                 }
366         else { $sel{$v}++; }
367         }
368 local $table = "<table>\n";
369 local $i;
370 for($i=$start; $i<=$end; $i++) {
371         $table .= "<tr>\n" if ($i%12 == 0);
372         $table .= "<td>".&ui_checkbox($name, $i,
373                 $text{$config_prefix.$name.$i} || $i, $sel{$i})."</td>\n";
374         $table .= "</tr>\n" if ($i%12 == 11);
375         }
376 $table .= "</tr>\n" if ($i%12 != 0);
377 $table .= "</table>\n";
378 return &ui_table_row($text{$config_prefix.$name},
379                      &ui_radio($name."_def", $val ? 0 : 1,
380                                [ [ 1, $text{'default'} ],
381                                  [ 0, $text{'report_below'} ] ]).
382                      "<br>".$table, $width);
383 }
384
385 # save_range(&config, name)
386 sub save_range
387 {
388 local ($conf, $name) = @_;
389 if ($in{$name."_def"}) {
390         &save_directive($conf, $name, [ ]);
391         }
392 else {
393         local @vals = split(/\0/, $in{$name});
394         &save_directive($conf, $name, [ join(",", @vals) ]);
395         }
396 }
397
398 # generate_report(handle, escape, clear-output, from, to)
399 # Immediately generate a report
400 sub generate_report
401 {
402 local ($h, $esc, $clear, $from, $to) = @_;
403 local $conf = &get_config();
404 local $odir = &find_value("output_dir", $conf);
405 $odir ||= &find_value("output_dir", $conf, 1);
406
407 if ($clear) {
408         # Delete all old files
409         unlink("$odir/index.html");
410         opendir(DIR, $odir);
411         while($f = readdir(DIR)) {
412                 if (-r "$odir/$f/usuarios") {
413                         &system_logged("rm -rf ".quotemeta("$odir/$f"));
414                         }
415                 }
416         closedir(DIR);
417         }
418
419 # Work out date range
420 local $rangearg;
421 if ($from || $to) {
422         if ($from < $to) {
423                 ($from, $to) = ($to, $from);
424                 }
425         local $now = time();
426         local @fromtm = localtime($now - $from*24*60*60);
427         local @totm = localtime($now - $to*24*60*60);
428         $rangearg = sprintf "-d %2.2d/%2.2d/%4.4d-%2.2d/%2.2d/%4.4d",
429                         $fromtm[3], $fromtm[4]+1, $fromtm[5]+1900,
430                         $totm[3], $totm[4]+1, $totm[5]+1900;
431         }
432
433 local $sfile = &find_value("access_log", $conf);
434 local @all = &all_log_files($sfile);
435 foreach $file (@all) {
436         local $cmd = "$config{'sarg'} -l $file $rangearg";
437         print $h $cmd,"\n";
438         open(OUT, "$cmd 2>&1 |");
439         while(<OUT>) {
440                 print $h $esc ? &html_escape($_) : $_;
441                 }
442         close(OUT);
443         return 0 if ($?);
444         &additional_log("exec", undef, $cmd);
445         }
446 return 1;
447 }
448
449 # all_log_files(file)
450 sub all_log_files
451 {
452 $_[0] =~ /^(.*)\/([^\/]+)$/;
453 local $dir = $1;
454 local $base = $2;
455 local ($f, @rv);
456 opendir(DIR, $dir);
457 foreach $f (readdir(DIR)) {
458         if ($f =~ /^\Q$base\E/ && -f "$dir/$f") {
459                 push(@rv, "$dir/$f");
460                 }
461         }
462 closedir(DIR);
463 return @rv;
464 }
465
466 sub gen_clear_input
467 {
468 return &ui_radio("clear", int($config{'clear'}),
469                       [ [ 1, $text{'yes'} ],
470                         [ 0, $text{'no'} ] ]);
471 }
472
473 sub gen_range_input
474 {
475 local ($rfrom, $rto) = split(/\s+/, $config{'range'});
476 local $range = &text('sched_rsel',
477                &ui_textbox("rfrom", $rfrom, 3),
478                &ui_textbox("rto", $rto, 3));
479 return &ui_radio("range_def", $config{'range'} ? 0 : 1,
480                       [ [ 1, $text{'sched_rall'} ],
481                         [ 0, $range ] ]);
482 }
483
484 sub lock_sarg_files
485 {
486 &lock_file($config{'sarg_conf'});
487 }
488
489 sub unlock_sarg_files
490 {
491 &unlock_file($config{'sarg_conf'});
492 }
493
494 1;
495