#!/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 (defined (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_java_script
{
	print <<EOF;
    <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>
EOF
} # print_java_script

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

	if (-e '/www/service.noris.net/htdocs/include/header.inc')
	{
		print "Content-Type: text/html\n\n";
		print_file ('/www/service.noris.net/htdocs/include/header.inc');
		if (-e 'menue-top.inc')
		{
			print_file ('/www/service.noris.net/htdocs/include/menue-top.inc');
		}
		print_java_script ();
		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>
  </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
	print_java_script ();
}

sub print_html_footer
{
	if (-e '/www/service.noris.net/htdocs/include/footer.inc')
	{
		print_file ('/www/service.noris.net/htdocs/include/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 $customer = lc ($ThisCustomer);
	my @hosts = get_all_hosts ($customer);
	my $rrdpath = get_rrdpath ();

	my @customers = get_customer_names_by_user_name ($User);

	if (@customers > 1)
	{
		print <<EOF;
    <p>Einen <a href="$BaseURI?${\get_args_string (action => 'default', customer => [])}">anderen Kunden ausw&auml;hlen</a>.</p>
EOF
	}

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

	print "    <h1>Verf&uuml;gbare Systeme</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 ('Kundenauswahl');

	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>Kundenauswahl</h1>

    <form action="$BaseURI" method="get">
    <fieldset>
      <legend>Verf&uuml;gbare Kunden</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="Absenden" />
    </fieldset>
EOF
      
	print_html_footer ();
}

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

	print_html_header ("Graphen von $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) . " und $host_name" if (@tmp);

		print "    <h1>Graphen&uuml;bersicht von $host_name</h1>\n\n";
	}
	print <<EOF if (@customers > 1);
    <p>Einen <a href="$BaseURI?${\get_args_string (action => 'default', customer => [])}">anderen Kunden ausw&auml;hlen</a>.</p>
EOF
	print qq#    <p>Zur&uuml;ck zur <a href="$BaseURI?${\get_args_string (action => 'default', host => [])}">Systemauswahl</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 (!defined ($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) . " und $host_name" if (@tmp);
	}

	print <<HTML;
    <h1>Kombinierte Detailgraphen von $ThisMetaService auf $host_name</h1>
HTML
	print <<EOF if (@customers > 1);
    <p>Einen <a href="$BaseURI?${\get_args_string (action => 'default', customer => [])}">anderen Kunden ausw&auml;hlen</a>.</p>
EOF
	print <<HTML;
    <p>Zur&uuml;ck zur <a href="$BaseURI?${\get_args_string (action => 'default', host => [])}">Systemauswahl</a>.</p>
    <p>Zur&uuml;ck zur <a href="$BaseURI?${\get_args_string (action => 'showhost', type => [], inst => [])}">Graphen&uuml;bersicht von $host_name</a>.</p>

    <h4>Einen einzelnen Graphen ansehen</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(Tag Woche Monat Jahr));

	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) . " und $host_name" if (@tmp);
	}

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

	print "    <h1>Detailgraphen von $srv auf $host_name</h1>\n\n";
	print <<EOF if (@customers > 1);
    <p>Einen <a href="$BaseURI?${\get_args_string (action => 'default', customer => [])}">anderen Kunden ausw&auml;hlen</a>.</p>
EOF
	print <<HTML;
    <p>Zur&uuml;ck zur <a href="$BaseURI?${\get_args_string (action => 'default', host => [])}">Systemauswahl</a>.</p>
    <p>Zur&uuml;ck zur <a href="$BaseURI?${\get_args_string (action => 'showhost', type => [], inst => [])}">Graphen&uuml;bersicht von $host_name</a>.</p>
HTML
	print qq#    <p>Zur&uuml;ck zu den <a href="$BaseURI?${\get_args_string (action => 'showtype', inst => [])}">kombinierten Detailgraphen von $ThisMetaService auf $host_name</a>.</p>\n# if (is_meta_service ($ThisMetaService));
	print "\n";

	my @ts   = (qw(day week month year));
	my @tsly = (qw(Tag Woche Monat Jahr));

	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"));
	}
	elsif ($filename =~ m/^netapp_capacity_([a-zA-Z]+)-(.*)\.rrd$/)
	{
		my $what = ucfirst (lc ($1));
		my $which = $2;
		$which =~ s#_#/#g;
		$which = "/$which" unless ($which =~ m#^/#);

		return (qq(Capacity: $what on $which));
	}
	elsif ($filename =~ m/^netscreen_(cpu|memory|sessions)\.rrd$/)
	{
		if ($1 eq 'cpu')
		{
			return ('NetScreen CPU utilisation');
		}
		elsif ($1 eq 'memory')
		{
			return ('NetScreen memory utilisation');
		}
		elsif ($1 eq 'sessions')
		{
			return ('NetScreen sessioncount');
		}
	}
	elsif ($filename =~ m/^(disk|partition)_(blocks|ops|time)-(.*)\.rrd$/)
	{
		my $type = ucfirst ($2);
		$type = 'Operations' if ($type eq 'Ops');
		return ("IO-$type (" . ucfirst ($1) . " $3)");
	}
	elsif ($filename eq 'swapinfo.rrd')
	{
		return ('Swap usage');
	}
	elsif ($filename =~ m/^charge-(.*).rrd$/)
	{
		my $inst = $1;
		$inst =~ s/_/ /g;
		return ("Charge of $inst");
	}
	elsif ($filename =~ m/^voltage-(.*).rrd$/)
	{
		my $inst = $1;
		$inst =~ s/_/ /g;
		return ("Voltage on $inst");
	}
	elsif ($filename =~ m/^duration-(.*).rrd$/)
	{
		my $inst = $1;
		$inst =~ s/_/ /g;
		return ("Duration of $inst");
	}
	elsif ($filename eq 'upsstatus.rrd')
	{
		return ("UPS Status");
	}

	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>Systeme</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>Zeitspanne</legend>
				  <select name="timespan" onChange="onChangeTimespan ();">
FORM
	my %trans = (day => 'Tag', week => 'Woche', month => 'Monat', year => 'Jahr', other => 'Auswahl');
	for (sort { $RRDTimes->{$b} <=> $RRDTimes->{$a} } (keys (%$RRDTimes)), 'other')
	{
		my $key = encode_entities ($_);
		my $key_print = $trans{$_};
		my $selected = ($_ eq $Timespan) ? ' selected="selected"' : '';
		print qq(\t\t\t\t    <option value="$key"$selected>$key_print</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>Beginn</legend>
FORM
	print_time_selector ($b_epoch, 'b_');
	print <<FORM;
				</fieldset>
			</td>
		</tr>
		<tr>
			<td style="vertical-align: top;">
				<fieldset class="timeselect"><legend>Ende</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="Absenden" />
			</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);
}
