#!/usr/bin/perl

use strict;
use warnings;
use lib qw(lib);

use CGI qw(:cgi);
use CGI::Carp qw(fatalsToBrowser);
use HTML::Entities qw(encode_entities);
use URI::Escape qw(uri_escape);
use Time::Local qw(timelocal);
use RRDs;
use Fcntl (':flock');

use CapMan::Config qw(read_config get_rrdpath get_all_hosts get_host_config
	host_check_auth get_customer_names_by_user_name);
use CapMan::Services qw(:frontend load_plugins is_meta_service);

our $Debug = 0;

our $Action = '';
our $ThisCustomer = '';
our $ThisHost        = '';
our $ThisMetaService = '';
our $ThisRealService = '';
our $Begin   = '';
our $End     = '';
our $Timespan = '';
our $BaseURI         = defined ($ENV{'SCRIPT_NAME'}) ? $ENV{'SCRIPT_NAME'} : '';
our $User            = '';

$User = $ENV{'REMOTE_USER'} if ($ENV{'REMOTE_USER'});

our $RRDTimes =
{
	day	=> -86400,
	week	=> -604800,
	month	=> -2678400,
	year	=> -31622400
};

our $Actions =
{
	default => \&print_host_menu,
	showhost => \&print_meta_services_of_host,
	showtype => \&print_meta_service_page,
	showinst => \&print_real_service_page,
	graph => \&print_graph
};

read_config ();
load_plugins ();

# 
# Read parameters
#
{
	my $act = param ('action');
	$Action = 'default';
	if ($act && defined ($Actions->{$act}))
	{
		$Action = $act;
	}

	my @customers = get_customer_names_by_user_name ($User);
	die ("User not associated with any customer: $User") if (!@customers);

	if (@customers == 1)
	{
		($ThisCustomer) = @customers;
	}
	elsif (param ('customer'))
	{
		my $customer = param ('customer');
		$customer = lc ($customer);
		for (@customers)
		{
			if (lc ($_) eq $customer)
			{
				$ThisCustomer = $_;
				last;
			}
		}
	}

	if (param ('host'))
	{
		my @hosts = param ('host');
		$ThisHost = join (',', grep { host_check_auth ($ThisCustomer, $_, $User) } (@hosts));
	}

	if (param ('type'))
	{
		$ThisMetaService = param ('type');

		if (param ('inst'))
		{
			$ThisRealService = param ('inst');
			$ThisRealService = '' if ($ThisRealService eq '_');
		}
	}

	$Begin = 0;
	$End = 0;
	$Timespan = '';

	if (param ('timespan'))
	{
		my $ts = param ('timespan');

		if (defined ($RRDTimes->{$ts}))
		{
			$End = time ();
			$Begin = $End + $RRDTimes->{$ts};
			$Timespan = $ts;
		}
	}

	if (!$Begin && param ('b_epoch') && param ('e_epoch'))
	{
		$Begin = param ('b_epoch');
		$End = param ('e_epoch');
	}

	# Do this last, because it's the most error-prone.
	if (!$Begin
		&& defined (param ('b_hour'))
		&& param ('b_day')
		&& param ('b_month')
		&& param ('b_year')
		&& defined (param ('e_hour'))
		&& param ('e_day')
		&& param ('e_month')
		&& param ('e_year'))
	{

		my ($hour, $day, $mon, $year);

		$hour = param ('b_hour');
		$day  = param ('b_day');
		$mon  = param ('b_month');
		$year = param ('b_year');

		eval
		{
			$Begin = timelocal (0, 0, $hour, $day, $mon - 1, $year - 1900);
		};

		if ($@)
		{
			$Begin = 0;
		}
		else
		{
			$hour = param ('e_hour');
			$day  = param ('e_day');
			$mon  = param ('e_month');
			$year = param ('e_year');

			eval
			{
				$End = timelocal (59, 59, $hour, $day, $mon - 1, $year - 1900);
			};

			if ($@)
			{
				$Begin = 0;
				$End = 0;
			}
		}
	}
}

if (!$ThisCustomer)
{
	select_customer ();
}
else
{
	# When the selector-form is used, no action is provided. Go to the
	# `services of host' page if one or more hosts are given and go to the
	# host-list if no host is given. This way it should behave in a very
	# intuitive way. -octo
	if (($Action eq 'default') && ($ThisHost))
	{
		$Action = 'showhost';
	}

	confess ("Huh?!") if (!defined ($Actions->{$Action}));
	$Actions->{$Action}->();
}

exit (0);

sub print_file
{
	my $file = shift;
	my $ofh = @_ ? shift : \*STDOUT;
	my $ifh;

	local $/ = undef;

	open ($ifh, "< $file") or die ("open ($file): $!");
	flock ($ifh, LOCK_SH) or die ("flock ($file): $!");

	while (my $line = <$ifh>)
	{
		print $ofh $line;
	}

	close ($ifh);
}

sub print_html_header
{
	my $title = @_ ? encode_entities (shift) : 'The Capacity Management Database of noris network AG';

	if (-e 'header.inc')
	{
		print "Content-Type: text/html\n\n";
		print_file ('header.inc');
		return;
	}

	print <<"EOF";
Content-Type: text/html

<html>
  <head>
    <title>$title - CapMan</title>
    <style type="text/css">
    <!--
    h2
    {
      font-size: large;
      margin: 2em 0px 0.5em 0px;
    }
    img
    {
      border: none;
    }
    img.graph
    {
      display: block;
    }
    -->
    </style>
    <script type="text/javascript">
      function onChangeTimespan ()
      {
        var arrTimespan = document.getElementsByName ("timespan");
	var arrSelects  = document.getElementsByTagName ("select");
	var arrFieldsets = document.getElementsByTagName ("fieldset");
	var bolDisable  = new Boolean (true);

	if (arrTimespan.length != 1)
	  return;
	if (!arrSelects)
	  return;

	if (arrTimespan[0].value == "other")
	{
	  bolDisable = false;
	}

	for (var i = 0; i < arrSelects.length; i++)
	{
	  if ((arrSelects[i].name == "b_year")
	       || (arrSelects[i].name == "b_month")
	       || (arrSelects[i].name == "b_day")
	       || (arrSelects[i].name == "b_hour")
	       || (arrSelects[i].name == "e_year")
	       || (arrSelects[i].name == "e_month")
	       || (arrSelects[i].name == "e_day")
	       || (arrSelects[i].name == "e_hour"))
	  {
	    arrSelects[i].disabled = bolDisable;
	    /*
	    if (bolDisable)
	    {
	      arrSelects[i].style.color = "silver";
	      arrSelects[i].style.backgroundColor = "white";
            }
	    else
	    {
	      arrSelects[i].style.color = "black";
	      arrSelects[i].style.backgroundColor = "white";
            }
	    */
	  }
	} // for (arrSelects)

	for (var i = 0; i < arrFieldsets.length; i++)
	{
	  var class = arrFieldsets[i].getAttribute ("class");

	  if (!class || (class != "timeselect"))
	    continue;

	  arrFieldsets[i].style.visibility = (bolDisable ? "hidden" : "visible");
	} // for (arrFieldsets)
      } // function onChangeTimespan
    </script>
  </head>

  <body>
EOF
#	print <<EOF;
#	<table>
#	  <caption>Debug information</caption>
#	  <tr>
#	    <th>Begin</th>
#	    <td><code>${\encode_entities ($Begin)}</code></td>
#	  </tr>
#	  <tr>
#	    <th>End</th>
#	    <td><code>${\encode_entities ($End)}</code></td>
#	  </tr>
#	  <tr>
#	    <th>ThisHost</th>
#	    <td><code>${\encode_entities ($ThisHost)}</code></td>
#	  </tr>
#	  <tr>
#	    <th>ThisMetaService</th>
#	    <td><code>${\encode_entities ($ThisMetaService)}</code></td>
#	  </tr>
#	  <tr>
#	    <th>ThisRealService</th>
#	    <td><code>${\encode_entities ($ThisRealService)}</code></td>
#	  </tr>
#	  <tr>
#	    <th>User</th>
#	    <td><code>${\encode_entities ($User)}</code></td>
#	  </tr>
#	  <tr>
#	    <th>ThisCustomer</th>
#	    <td><code>${\encode_entities ($ThisCustomer)}</code></td>
#	  </tr>
#	</table>
#EOF
}

sub print_html_footer
{
	if (-e 'footer.inc')
	{
		print_file ('footer.inc');
		return;
	}

	print <<EOF;
  </body>
</html>
EOF
}

sub print_host_menu
{
	confess ("No customer given") unless ($ThisCustomer);

	print_html_header ("Hosts for $ThisCustomer");
	print_selector ();

	my @hosts = get_all_hosts (lc ($ThisCustomer));
	my $rrdpath = get_rrdpath ();

	my @customers = get_customer_names_by_user_name ($User);

	print <<EOF if (@customers > 1);
	<p>Select <a href="$BaseURI?${\get_args_string (action => 'default', customer => [])}">a different customer</a>.</p>
EOF

	@hosts = sort (grep { (-d "$rrdpath/$ThisCustomer/$_") and (host_check_auth ($ThisCustomer, $_, $User)) } (@hosts));

	print "    <h1>List of hosts</h1>\n\n",
	"    <ul>\n";
	for (@hosts)
	{
		my $host = $_;
		my $args = get_args_string (action => 'showhost', host => $host);
		print qq#      <li><a href="$BaseURI?$args">$_</a></li>\n#;
	}
	print "    </ul>\n";

	print_html_footer ();
}

sub select_customer
{
	print_html_header ("Select a view");

	my $rrdpath = get_rrdpath ();
	my @customers = get_customer_names_by_user_name ($User);
	@customers = grep { -d "$rrdpath/" . lc ($_) } (@customers);
	@customers = map { encode_entities ($_) } (@customers);

	if (!@customers)
	{
		print <<EOF;
    <h1>Access denied</h1>

    <p>Either you do not have access to this service, or there don't exist any
    graphs for the customer your username is associated with. So in short, you
    either may not see anything or there isn't anything to see.</p>

    <p>If you think this is an error please don't hesitate to contact <a
    href="mailto:support\@noris.net?subject=CapMan: Access for user $User">the
    noris&nbsp;network Support-Team</a>.</p>
EOF
		print_html_footer ();
		return;
	}

	print <<EOF;
    <h1>Please select a customer</h1>

    <form action="$BaseURI" method="get">
    <fieldset>
      <legend>Available customers</legend>
EOF
	print qq(      <input type="hidden" name="b_epoch" value="$Begin" />\n)
	. qq(      <input type="hidden" name="e_epoch" value="$End" />\n) if ($Begin and $End);

	print <<EOF;
      <select name="customer">
EOF
	for (@customers)
	{
		print qq(        <option value="$_">$_</option>\n);
	}
	print <<EOF;
      </select>
      <input type="submit" name="submit" value="Submit" />
    </fieldset>
EOF
      
	print_html_footer ();
}

sub print_meta_services_of_host
{
	confess ("No host given") if (!$ThisHost);

	print_html_header ("Services on $ThisHost");
	print_selector ();

	my $dh;
	my $files = {};
	my $rrdpath = get_rrdpath ();
	my @hosts = split (m/,/, $ThisHost);

	my $uri_all_host = uri_escape ($ThisHost);

	my @customers = get_customer_names_by_user_name ($User);

	for (@hosts)
	{
		my $host = $_;
		my @files;
		my $meta;
		my $real;

		my $dir = "$rrdpath/" . lc ($ThisCustomer) . "/$host";

		opendir ($dh, $dir) or confess ("opendir ($dir): $!");
		@files = sort (grep { (-f "$dir/$_") and ($_ !~ m/^\./) } (readdir ($dh)));
		closedir ($dh);

		for (@files)
		{
			my $file = $_;

			($meta, $real) = file_to_service ($file);

			$files->{$meta}{$host} = [] unless (defined ($files->{$meta}{$host}));
			push (@{$files->{$meta}{$host}}, $file);
		}
	}

	{;
		my @tmp = @hosts;
		my $host_name = pop (@tmp);
		$host_name = join (', ', @tmp) . " and $host_name" if (@tmp);

		print "    <h1>List of services on $host_name</h1>\n\n";
	}
	print <<EOF if (@customers > 1);
    <p>Select <a href="$BaseURI?${\get_args_string (action => 'default', customer => [])}">a different customer</a>.</p>
EOF
	print qq#    <p>Back to <a href="$BaseURI?${\get_args_string (action => 'default', host => [], type => [], inst => [])}">list of hosts</a>.</p>\n\n#;

	print "    <table>\n",
	"      <tr>\n",
	(map { "        <th>$_</th>\n" } (@hosts)),
	"      </tr>\n";

	for (sort (keys %$files))
	{
		my $meta = $_;
		my $uri_meta = uri_escape ($meta);

		print "      <tr>\n";

		if (is_meta_service ($meta))
		{
			for (@hosts)
			{
				my $host = $_;
				my $uri_this_host = uri_escape ($host);

				if (!defined ($files->{$meta}{$host}))
				{
					print "        <td>&nbsp;</td>\n";
				}
				else
				{
					my $page_args = get_args_string (action => 'showtype',
						host => [grep { defined ($files->{$meta}{$_}) } (@hosts)],
						type => $meta,
						inst => []);
					my $grph_args = get_args_string (action => 'graph',
						host => $host,
						type => $meta,
						inst => [],
						(!$Begin && !$End ? (timespan => 'day') : ()));

					print "<!-- Meta service. -->\n";
					print qq#        <td><a href="$BaseURI?$page_args">#,
					qq#<img src="$BaseURI?$grph_args" class="graph" /></a></td>\n#;
				}
			}
		}
		else
		{
			my $real_map = {};
			my $count = 0;
			
			for (@hosts)
			{
				my $host = $_;
				my $m;
				my $r;

				next unless (defined ($files->{$meta}{$host}));

				for (@{$files->{$meta}{$host}})
				{
					($m, $r) = file_to_service ($_);
					$r = '_' if (!$r);
					$real_map->{$r}{$host} = $_;
				}
			}

			for (sort (keys %$real_map))
			{
				my $real = $_;
				my $uri_real = uri_escape ($real);

				print "      </tr>\n      <tr>\n" if ($count++);

				for (@hosts)
				{
					my $host = $_;
					my $uri_this_host = uri_escape ($host);

					if (!defined ($real_map->{$real}{$host}))
					{
						print "        <td>&nbsp;</td>\n";
					}
					else
					{
						my $page_args = get_args_string (action => 'showinst',
							host => [grep { defined ($real_map->{$real}{$_}) } (@hosts)],
							type => $meta,
							inst => $real);
						my $grph_args = get_args_string (action => 'graph',
							host => $host,
							type => $meta,
							inst => $real,
							(!$Begin && !$End ? (timespan => 'day') : ())
						);

						print "<!-- Real service. -->\n";
						print qq#        <td><a href="$BaseURI?$page_args">#,
						qq#<img src="$BaseURI?$grph_args" class="graph" /></a></td>\n#;
					}
				}
			}
		}

		print "      </tr>\n";
	}

	print <<EOF;
    </table>
EOF
	print_html_footer ();
}

sub print_meta_service_page
{
	my @host = split (m/,/, $ThisHost);
	my $uri_host = uri_escape ($ThisHost);
	my @uri_host = map { uri_escape ($_) } (@host);
	my $uri_meta = uri_escape ($ThisMetaService);

	my $files = {};
	my $rrdpath = get_rrdpath ();
	
	my @customers = get_customer_names_by_user_name ($User);

	print_html_header ("Service $ThisMetaService on $ThisHost");
	print_selector ();

	confess ("You shouldn't get here!") unless defined (is_meta_service ($ThisMetaService));

	for (my $i = 0; $i < @host; $i++)
	{
		next if ($host[$i] =~ m#/#);
		my $dir = $rrdpath . '/' . lc ($ThisCustomer) . '/' . $host[$i];
		my $dh;

		if (!opendir ($dh, $dir))
		{
			cluck ("opendir ($dir): $!");
			next;
		}

		$files->{$host[$i]} = [];
		my @files = sort (grep { (-f "$dir/$_") and ($_ !~ m/^\./) } (readdir ($dh)));
		print "<!-- $dir: " . join (', ', @files) . " -->\n";
		for (@files)
		{
			my ($meta, $real) = file_to_service ($_);
			if (($meta eq $ThisMetaService) && defined ($real) && ($real ne ''))
			{
				push (@{$files->{$host[$i]}}, $_);
			}
		}

		closedir ($dh);

		delete ($files->{$host[$i]}) if (!@{$files->{$host[$i]}});
	}

	my $host_name;
	{;
		my @tmp = @host;
		$host_name = pop (@tmp);
		$host_name = join (', ', @tmp) . " and $host_name" if (@tmp);
	}

	print <<HTML;
    <h1>Service overview of $ThisMetaService on $host_name</h1>
HTML
	print <<EOF if (@customers > 1);
    <p>Select <a href="$BaseURI?${\get_args_string (action => 'default', customer => [])}">a different customer</a>.</p>
EOF
	print <<HTML;
    <p>Back to <a href="$BaseURI?${\get_args_string (action => 'default', host => [])}">list of hosts</a>.</p>
    <p>Back to <a href="$BaseURI?${\get_args_string (action => 'showhost', inst => [], type => [])}">services on $host_name</a>.</p>

    <h4>View a single RRD file</h4>
    <ul>
HTML
	for (my $i = 0; $i < @host; $i++)
	{
		print "      <li>" . encode_entities ($host[$i]) . "\n"
		. "\t<ul>\n";
		for (my $j = 0; $j < @{$files->{$host[$i]}}; $j++)
		{
			my $file = $files->{$host[$i]}[$j];
			$file =~ s/\.rrd$//i;

			my ($type, $inst) = split (m/-/, $file, 2);

			my $args = get_args_string (action => 'showinst',
				host => $host[$i],
				type => $ThisMetaService,
				inst => $inst);

			my $inst_esc = encode_entities ($inst);

			print qq(\t  <li><a href="$BaseURI?$args">$inst_esc</a></li>\n);
		}
		print "\t</ul>\n      </li>\n";
	}
	print "    </ul>\n";

	my @ts   = (qw(day week month year));
	my @tsly = (qw(Daily Weekly Monthly Yearly));

	print "    <table>\n      <tr>\n        <td></td>\n";
	print "        <th>$_</th>\n" for (map { encode_entities ($_) } (@host));
	print "      </tr>\n";

	for (my $i = 0; $i < scalar (@ts); $i++)
	{
		print "      <tr>\n        <th>", $tsly[$i], "</th>\n";
		for (my $j = 0; $j < @host; $j++)
		{
			my $args = get_args_string (action => 'graph',
				timespan => $ts[$i],
				b_epoch => [],
				e_epoch => [],
				host => $host[$j],
				type => $ThisMetaService,
				inst => []);
			print qq(        <td><img src="$BaseURI?$args" class="graph" /></td>\n);
		}
		print "      </tr>\n";
	}
	print "    </table>\n\n";

	print_html_footer ();
}

sub print_real_service_page
{
	my @host = split (m/,/, $ThisHost);
	my $uri_host = uri_escape ($ThisHost);
	my @uri_host = map { uri_escape ($_) } (@host);
	my $uri_meta = uri_escape ($ThisMetaService);
	my $uri_real = $ThisRealService ne '' ?  uri_escape ($ThisRealService) : '_';

	my @customers = get_customer_names_by_user_name ($User);

	print_html_header ("Service $ThisMetaService/$ThisRealService on $ThisHost");
	print_selector ();

	my $host_name;
	{;
		my @tmp = @host;
		$host_name = pop (@tmp);
		$host_name = join (', ', @tmp) . " and $host_name" if (@tmp);
	}

	my $srv = $ThisRealService eq '' ? $ThisMetaService : "$ThisMetaService ($ThisRealService)";

	print "    <h1>Service overview of $srv on $host_name</h1>\n\n";
	print <<EOF if (@customers > 1);
	<p>Select <a href="$BaseURI?${\get_args_string (action => 'default', customer => [])}">a different customer</a>.</p>
EOF
	print <<HTML;
    <p>Back to <a href="$BaseURI?${\get_args_string (type => [], inst => [], host => [])}">list of hosts</a>.</p>
    <p>Back to <a href="$BaseURI?${\get_args_string (type => [], inst => [])}">services on $host_name</a>.</p>
HTML
	print qq#    <p>Back to <a href="$BaseURI?${\get_args_string (inst => [])}">$ThisMetaService on $host_name</a>.</p>\n# if (is_meta_service ($ThisMetaService));
	print "\n";

	my @ts   = (qw(day week month year));
	my @tsly = (qw(Daily Weekly Monthly Yearly));

	print "    <table>\n      <tr>\n        <td></td>\n";
	print "        <th>$_</th>\n" for (map { encode_entities ($_) } (@host));
	print "      </tr>\n";

	for (my $i = 0; $i < @ts; $i++)
	{
		print "      <tr>\n        <th>", $tsly[$i], "</th>\n";
		for (my $j = 0; $j < @host; $j++)
		{
			my $args = get_args_string (action => 'graph',
				b_epoch => [],
				e_epoch => [],
				timespan => $ts[$i],
				host => $host[$j],
				type => $ThisMetaService,
				inst => $ThisRealService);
			print qq(        <td><img src="$BaseURI?$args" class="graph" /></td>\n);
		}
		print "      </tr>\n";
	}
	print "    </table>\n\n";

	print_html_footer ();
}

sub print_graph
{
	confess ("No host given") if (!$ThisHost);
	confess ("No meta-service given") if (!$ThisMetaService);

	if (!is_meta_service ($ThisMetaService) or ($ThisRealService ne ''))
	{
		print_real_service_graph ();
	}
	else
	{
		print_meta_service_graph ();
	}
}

sub print_meta_service_graph
{
	my $dh;
	my @files = ();
	my $rrdpath = get_rrdpath ();

	my $changed = 0;

	die unless (is_meta_service ($ThisMetaService));

	my $dir = "$rrdpath/" . lc ($ThisCustomer) . "/$ThisHost";
	die ("No such directory: $dir") if (!-d $dir);
	
	opendir ($dh, $dir) or die ("opendir ($dir): $!");
	@files = sort (grep { (-f "$dir/$_") and ($_ !~ m/^\./) } (readdir ($dh)));
	closedir ($dh);

	for (@files)
	{
		my $mtime = (stat ("$dir/$_"))[9];
		$changed = $mtime if ($changed < $mtime);
	}

	$changed ||= time ();

	@files = grep { (file_to_service ($_))[0] eq $ThisMetaService } (@files);
	die ('No files found') unless (@files);

	{
		# RRDs' writes are unbuffered, so we have to force a flush
		# here..
		my $tmp = $|;
		$| = 1;
		#print "Content-Type: image/png\n\n";
		print header (-Content_Type => 'image/png',
			-Last_Modified => epoch_to_rfc1123 ($changed),
			-Expires => '+1m');
		$| = $tmp;
	}

	run_graph_callback ($ThisMetaService, lc ($ThisCustomer), $ThisHost, $Begin, $End, @files);
}

sub print_real_service_graph
{
	my $rrdpath = get_rrdpath ();

	my $filename = "$rrdpath/" . lc ($ThisCustomer) . "/$ThisHost/$ThisMetaService";
	my $desc;
	
	my $defs = scalar get_graph_definition ($ThisMetaService);

	if ($ThisRealService ne '')
	{
		$filename .= '-' . $ThisRealService;
	}
	$filename .= '.rrd';
	
	die ("No such file: $filename") unless (-e $filename);
	die ("FATAL: No RRD definition found for ``$ThisMetaService''") unless (@$defs);

	my $mtime = (stat ($filename))[9];
	$mtime ||= time ();

	$desc = get_service_description ($ThisRealService eq '' ? "$ThisMetaService.rrd" : "$ThisMetaService-$ThisRealService.rrd");

	{
		# RRDs' writes are unbuffered, so we have to force a flush
		# here..
		my $tmp = $|;
		$| = 1;
		print header (-Content_Type => 'image/png',
			-Last_Modified => epoch_to_rfc1123 ($mtime),
			-Expires => '+1m');
		$| = $tmp;
	}

	s/{filename}/$filename/g for (@$defs);

	RRDs::graph ('-', '-s', $Begin, '-e', $End, '-a', 'PNG', '-t', "$desc on $ThisHost", @$defs);

	die (RRDs::error ()) if (RRDs::error ());
}

sub file_to_service
{
	my $file = shift;

	if ($file =~ m/^(\w+)(?:-([^\.]+))?\.rrd$/)
	{
		return ($1, $2);
	}
}

sub get_service_description
{
	my $filename = shift;

	if ($filename eq "load.rrd")
	{
		return ('System Load');
	}
	elsif ($filename eq 'meminfo.rrd' or $filename eq 'memory.rrd')
	{
		return ('Memory Usage');
	}
	elsif ($filename eq 'users.rrd')
	{
		return ('Active Users');
	}
	elsif ($filename eq 'bind.rrd')
	{
		return ('Bind (named)');
	}
	elsif ($filename eq 'mysql_threads.rrd')
	{
		return ('MySQL threads');
	}
	elsif ($filename eq 'mysql_qcache.rrd')
	{
		return ('MySQL query cache');
	}
	elsif ($filename eq 'mysql_traffic.rrd')
	{
		return ('MySQL traffic');
	}
	elsif ($filename =~ m#^mysql_command-([^\.]*)\.rrd$#)
	{
		my $cmd = uc ($1);
		return ("MySQL command $cmd");
	}
	elsif ($filename =~ m#^mysql_handler-([^\.]*)\.rrd$#)
	{
		my $handler = uc ($1);
		return ("MySQL handler $handler");
	}
	elsif ($filename =~ m#^mysql_keyblock_access-([^\.]+)\.rrd$#)
	{
		return ("MySQL keyblock $1s");
	}
	elsif ($filename =~ m#^interfaces-(.*)\.rrd$#)
	{
		return ("Traffic of interface $1");
	}
	elsif ($filename =~ m/^disk_io-(.+)\.rrd$/)
	{
		my $d = $1;
		$d =~ s/_/,/g;

		return ('disk_io', "Disk-IO ($d)");
	}
	elsif ($filename =~ m/^io-(.+)\.rrd$/)
	{
		return (ucfirst ($1) . '-IO');
	}
	elsif ($filename =~ m/^(contextswitches|interrupts|forkrate)/)
	{
		my $name = ucfirst ($1);
		return ($name);
	}
	elsif ($filename =~ m/^temperature-(.+)\.rrd$/)
	{
		my $name = join (' ', map { ucfirst ($_) } (split (m/_/, $1)));

		return ("Temperature \"$name\"");
	}
	elsif ($filename =~ m/^humidity-(.+)\.rrd$/)
	{
		my $name = join (' ', map { ucfirst ($_) } (split (m/_/, $1)));

		return ("Humidity \"$name\"");
	}
	elsif ($filename =~ m/^binary-(.+)\.rrd$/)
	{
		my $name = join (' ', map { ucfirst ($_) } (split (m/_/, $1)));

		if ($name =~ m/^(Input|Output|Alarm|UPS) (.+)$/)
		{
			return (qq($1 "$2"));
		}
		return (qq(Digital-I/O "$name"));
	}

	return ("Unmatched filename: $filename");
}

sub print_option_list
{
	my $name = shift;
	my $values = shift;
	my $selected = shift;
	my $multiple = shift;

	my %opts = map { $_ => 0 } @$values;
	$opts{$_} = 1 for (@$selected);

	#print "Values: (" . join (', ', @$values) . "), Seleced: (" . join (', ', @$selected) . ")\n";
	print qq(<select name="$name"), $multiple ? ' multiple="multiple" size="7"' : '', ">\n";
	for (@$values)
	{
		my $val = $_;
		print qq(\t<option value="$val"), ($opts{$val} ? ' selected="selected"' : ''), ">$val</option>\n";
	}
	print "</select>";
}

sub print_time_selector
{
	my $epoch = shift;
	my $prefix = shift;
	my ($year, $month, $day, $hour) = (localtime ($epoch))[5,4,3,2];
	$year += 1900; $month++;
	#print "($epoch -> $year-$month-$day:$hour)";

	my $this_year = (localtime ())[5]; $this_year += 1900;

	print_option_list ($prefix . 'year', [$this_year, $this_year - 1, $this_year - 2], [$year], 0);
	print '-';
	print_option_list ($prefix . 'month', [1 .. 12], [$month], 0);
	print '-';
	print_option_list ($prefix . 'day', [1 .. 31], [$day], 0);
	print ', &nbsp;';
	print_option_list ($prefix . 'hour', [0 .. 23], [$hour], 0);
}

sub print_selector
{
	my %hosts = ();

	my $b_epoch = $Begin ? $Begin : time () - 86400;
	my $e_epoch = $End ? $End : time ();

	$hosts{$_} = 0 for (grep { host_check_auth ($ThisCustomer, $_, $User) } (get_all_hosts (lc $ThisCustomer)));

	my @customers = get_customer_names_by_user_name ($User);

	for (split (m/,/, $ThisHost))
	{
		my $host = $_;
		next unless defined ($hosts{$host});
		$hosts{$host} = 1;
	}
	
	print <<FORM;
<form action="$BaseURI" method="get">
	<input type="hidden" name="customer" value="${\lc ($ThisCustomer)}" />
	<table>
		<tr>
			<td rowspan="4" style="vertical-align: top;">
			<fieldset><legend>Hosts</legend>
FORM
	print_option_list ('host', [sort keys %hosts], [grep { $hosts{$_} } (keys %hosts)], 1);
	print <<FORM;
			</fieldset>
			</td>
			<td style="vertical-align: top;">
				<fieldset>
				  <legend>Timespan</legend>
				  <select name="timespan" onChange="onChangeTimespan ();">
FORM
	for (sort { $RRDTimes->{$b} <=> $RRDTimes->{$a} } (keys (%$RRDTimes)), 'other')
	{
		my $key = encode_entities ($_);
		my $selected = ($_ eq $Timespan) ? ' selected="selected"' : '';
		print qq(\t\t\t\t    <option value="$key"$selected>$key</option>\n);
	}
	print <<FORM;
				  </select>
				</fieldset>
			</td>
FORM
	# XXX This is disabled on purpose.
	if (0 and @customers > 1)
	{
		my $rrdpath = get_rrdpath ();
		print qq(\t\t\t<td rowspan="4" style="vertical-align: top;"><fieldset><legend>Kunde</legend><select name="customer">\n);
		# FIXME: use `print_option_list'
		for (@customers)
		{
			my $cust_orig = $_;
			my $cust_lc = lc ($cust_orig);
			my $sel = '';

			next if (!-d "$rrdpath/$cust_lc");

			$sel = ' selected="selected"' if ($cust_lc eq $ThisCustomer);

			$cust_lc = encode_entities ($cust_lc);
			$cust_orig = encode_entities ($cust_orig);

			print qq(\t\t\t\t<option value="$cust_lc"$sel>$cust_orig</option>\n);
		}
		print qq(\t\t\t</select></fieldset></td>\n);
	}
	print <<FORM;
		</tr>
		<tr>
			<td style="vertical-align: top;">
				<fieldset class="timeselect"><legend>Begin</legend>
FORM
	print_time_selector ($b_epoch, 'b_');
	print <<FORM;
				</fieldset>
			</td>
		</tr>
		<tr>
			<td style="vertical-align: top;">
				<fieldset class="timeselect"><legend>End</legend>
FORM
	print_time_selector ($e_epoch, 'e_');
	print <<FORM;
				</fieldset>
			</td>
		</tr>
		<tr>
			<td>
				<!-- FIXME <input type="hidden" name="action" value="showhost" /> -->
				<input type="submit" name="submit" value="Go" />
			</td>
		</tr>
	</table>
</form>
<script type="text/javascript">
  // Disable elements that are not used.
  onChangeTimespan ();
</script>
FORM
}

{
	my $defaults = {};

	sub get_args_string
	{
		my %overrides = @_;
		my %all_keys = ();
		my @args;

		if (!keys %$defaults)
		{
			if ($ThisCustomer)
			{
				$defaults->{'customer'} = lc ($ThisCustomer);
			}
			if ($ThisHost)
			{
				$defaults->{'host'} = [split (m/,/, $ThisHost)];
			}
			if ($ThisMetaService)
			{
				$defaults->{'type'} = $ThisMetaService;

				if ($ThisRealService)
				{
					$defaults->{'inst'} = $ThisRealService;
				}
			}
			if ($Begin && $End)
			{
				$defaults->{'b_epoch'} = $Begin;
				$defaults->{'e_epoch'} = $End;
			}
			if (param ('timespan'))
			{
				my $ts = param ('timespan');
				$defaults->{'timespan'} = $ts if (defined ($RRDTimes->{$ts}));
			}
		}

		$all_keys{$_} = 1 for (keys %$defaults);
		$all_keys{$_} = 1 for (keys %overrides);

		for (keys (%all_keys))
		{
			my $key = $_;
			my $val = defined ($overrides{$key})
					? $overrides{$key}
					: $defaults->{$key};

			if (ref ($val) eq 'ARRAY')
			{
				push (@args, $key . '=' . uri_escape ($_)) for (@$val);
			}
			elsif (!ref ($val))
			{
				push (@args, $key . '=' . uri_escape ($val));
			}
			else { confess ("What IS this value?"); }
		}

		return (join ('&', @args));
	} # sub get_args_string
}

sub epoch_to_rfc1123
{
	my @days = (qw(Sun Mon Tue Wed Thu Fri Sat));
	my @months = (qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec));

	my $epoch = @_ ? shift : time ();
	my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday) = gmtime($epoch);
        my $string = sprintf ('%s, %02d %s %4d %02d:%02d:%02d GMT', $days[$wday], $mday,
		$months[$mon], 1900 + $year, $hour ,$min, $sec);
	return ($string);
}
