#!/opt/bin/perl

# webadmin.pl
# - webbased configuration manager and stream manager for iceDJ.pl
#
# Copyright (c) 1999 Arne Claassen <icedj@remixradio.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
# 7
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
#

use strict;
use Time::Local;
use IO::Socket;
use POSIX qw(:signal_h :errno_h :sys_wait_h);

print "Content-type: text/html\n\n";
$, = "\n";
#$|=1;
#*STDERR=*STDOUT;




# ____ Define Globals _________________________________________________________
use vars qw($PIDFILE $LOGFILE $REQUESTLOG $AVAILABLE $USED $LOCKFILE $LOCKED);
use vars qw($SOCK $F_STREAMER $STREAMERMSG $F_SERVER $SERVERMSG $F_LOG $LOGTAIL);
use vars qw($HUPFILE $ERROR $ICEDJPATH $ACTIONMSG);
use vars qw(%CONF %DESC @FIELDS @SONGINFO);

# _____ Get the CGI Input _____________________________________________________
my($buffer, $pair, @pairs, $name, $value, %in, @query_vals, $couple);
read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'});
@pairs = split(/&/, $buffer);
foreach $pair (@pairs) {
	($name, $value) = split(/=/, $pair);
	$value =~ tr/+/ /;
	$value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
	$value =~ s/\t/ /g;
	$in{$name} = $value;
	}

if ($ENV{'QUERY_STRING'}) {
	@query_vals = split(/&/, $ENV{'QUERY_STRING'});
	foreach $couple (@query_vals) {
		($name, $value) = split(/=/, $couple);
		$in{$name} = $value;
		}
	}

# ____ Get Config _____________________________________________________________
my($DOCROOT) = $ENV{'DOCUMENT_ROOT'};

$CONF{'datadir'} = "$DOCROOT/data/";

$CONF{'datadir'} = "/web/r/www.remixradio.com/data/";

login() if(!$in{'conf'});

$CONF{'conf'} = $in{'conf'};

open(CONF, $CONF{'datadir'}.$CONF{'conf'})
	|| ScriptError("OPEN ERR: CONFIG","Couldn't read config file");
my($description);
while (<CONF>) {
	chomp;
	if(/^#.*/) { #comment, keep for later
		$description .= "$_";
		}
	elsif(/^\s*$/) {
		$description = "";
		}
	else {
		/(\S+)\s+(.*)$/;
		$CONF{$1} = $2;
		$DESC{$1} = $description;
		push(@FIELDS, $1);
		$description = "";
		}
	}
close(CONF);


if($in{'serverpass'} ne $CONF{'password'}&&$CONF{'conf'} !~ /example.conf/) {
	login("Password for $CONF{'name'} is incorrect!");
	}

# ____ Set up Constants _______________________________________________________
$ICEDJPATH = "$CONF{'scriptpath'}icedj.pl";
$PIDFILE = "$CONF{'datadir'}icedj-$CONF{'logname'}.pid";
$HUPFILE = "$CONF{'datadir'}icedj-$CONF{'logname'}.hup";
$AVAILABLE = "$CONF{'datadir'}avail-$CONF{'logname'}";
$USED = "$CONF{'datadir'}used-$CONF{'logname'}";
$LOCKFILE = "$CONF{'datadir'}icedj-$CONF{'logname'}.LCK";
$LOGFILE = "$CONF{'datadir'}icedj-$CONF{'logname'}.log";
@SONGINFO = ('request','path','artist','title');
push(@SONGINFO, split(/\s+/,$CONF{'songinfo'}));

	print <<EOM;
<HTML>
<HEAD>
<TITLE>IceDJ Admin for $CONF{'conf'}</TITLE>
</HEAD>
<BODY>
EOM

# ____ Check for Things to Do _________________________________________________
if($in{'action'}) {
	if($in{'action'} eq 'streamer') {
		if($in{'streamer'} eq 'reload') {
			open(HUPFILE, ">$HUPFILE");
			print HUPFILE "$$\n";
			close(HUPFILE);
			$ACTIONMSG = "<P>Streamer has been sent a message to reset itself. This may take a few minutes. Click refresh logs to see current status";
			}
		elsif($in{'streamer'} eq 'kill') {
			unlink($PIDFILE);
			$ACTIONMSG = "<P>Streamer has been sent a message to kill itself. This may take a few minutes. Click refresh logs to see current status";
			}
		elsif($in{'streamer'} eq 'start') {
			my ($pid1, $pid2);
			if ($pid1 = fork()) { # Parent
				waitpid($pid1,0);
				}
			elsif (defined($pid1)) { ## child
				if ($pid2 = fork())    { ## still child
					exit(1)
					}
				elsif (defined($pid2)) { ## grandchild
					exec($ICEDJPATH,"--silent","$CONF{'datadir'}$CONF{'conf'}");
					exit;
					}
				else { ## child; 2nd fork failed
					exit(1);
					}
				}
			else {              ## parent - fork failed
				$ERROR = "couldn't start IceDJ with '$ICEDJPATH $CONF{'datadir'}$CONF{'conf'} &'";
				}
#			system("$ICEDJPATH $CONF{'datadir'}$CONF{'conf'} &")
#				&&{ $ERROR = "couldn't start IceDJ with '$ICEDJPATH $CONF{'datadir'}$CONF{'conf'} &'"; };
			$ACTIONMSG = "<P>Streamer has been sent a message to start up. This may take a few minutes. Click refresh logs to see current status";
			}
		sleep(10);
		}
	elsif($in{'action'} eq 'config') {
		my($line);
		my($oldconf) = "$CONF{'datadir'}$CONF{'conf'}";
		my($newconf) = "$CONF{'datadir'}$in{'saveconf'}";
		open(CONF, $oldconf);
		open(CONFTMP, ">$newconf.tmp");
		while($line = <CONF>) {
			if($line =~ /^(#.*|\s*)$/) { 
				print CONFTMP $line;
				}
			else {
				$line =~ /(\S+)\s+(.*)$/;
				my($value) = $in{"f_$1"};
				print CONFTMP "$1\t$value\n";
				$CONF{$1} = $value;
				}
			}
		close(CONF);
		close(CONFTMP);
		chmod(0666,"$newconf.tmp");
		if($CONF{'conf'} eq $in{'saveconf'}) {
			unlink($oldconf);
			rename("$newconf.tmp",$oldconf);
			}
		else { rename("$newconf.tmp",$newconf); }
		}
	}


# ____ Check for streamer _____________________________________________________
if (-e $PIDFILE) {
	open(PIDFILE, $PIDFILE);
	my($pid) = <PIDFILE>;
	close(PIDFILE);
	chomp $pid;
#	if(kill -0 => $pid) {
		$F_STREAMER = 1;
		$STREAMERMSG = "Running";
		
#		}
#	else {
#		$F_STREAMER = 0;
#		$STREAMERMSG = "Not Running";
#		unlink($PIDFILE);
#		}
	}
else {
	$F_STREAMER = 0;
	$STREAMERMSG = "Not Running";
	}

# ____ Get log tail _____________________________________________________
if(-e $LOGFILE) {
	$F_LOG = 1;
	$LOGTAIL = `tail $LOGFILE`;
	}

# ____ Check for server _____________________________________________________
$SOCK = IO::Socket::INET->new(	Proto => "tcp",
								 PeerAddr => $CONF{'server'},
								 PeerPort => $CONF{'port'});
if($SOCK) { 
	$F_SERVER = 1;
	$SERVERMSG = "Server is accepting sources";
	close($SOCK);
	}
else {
	$F_SERVER = 0;
	$SERVERMSG = "Server unreachable";
}

# ____ Something go wrong? ____________________________________________________
if($ERROR) {
	print "<CENTER><FONT COLOR=Red>$ERROR</FONT></CENTER>";
	}

# ____ This is where the action is ____________________________________________
if($CONF{'conf'} !~ /example.conf/) {
	print <<EOM;
<H1>Streamer Config for $CONF{'name'}</H1>

<BR><B>Streamer Status:<FONT COLOR=Red>$STREAMERMSG</FONT></B>
<BR><B>Server Status:<FONT COLOR=Red>$SERVERMSG</FONT></B>
<BR><B>Most recent log entries:</B>
<BLOCKQUOTE><FONT SIZE=-1><PRE>$LOGTAIL</PRE></FONT></BLOCKQUOTE>
<FORM METHOD=POST ACTION="webadmin.pl">
<INPUT TYPE="hidden" NAME="conf" VALUE="$CONF{'conf'}">
<INPUT TYPE="hidden" NAME="serverpass" VALUE="$CONF{'password'}">
<INPUT TYPE="submit" VALUE="Refresh Log">
</FORM>
<HR>
<FORM METHOD=POST ACTION="webadmin.pl">
<INPUT TYPE="hidden" NAME="action" VALUE="streamer">
<INPUT TYPE="hidden" NAME="conf" VALUE="$CONF{'conf'}">
<INPUT TYPE="hidden" NAME="serverpass" VALUE="$CONF{'password'}">
$ACTIONMSG
EOM
	
	if($F_SERVER) {
		if($F_STREAMER) {
			print <<EOM;
<BLOCKQUOTE>
<INPUT TYPE="radio" NAME="streamer" VALUE="reload" CHECKED>Reload Playlist and Configuration
<BR><INPUT TYPE="radio" NAME="streamer" VALUE="kill">Kill streamer
<BR><INPUT TYPE="submit"> (action happens after next song or show)
</BLOCKQUOTE>
EOM
			}
		else {
			print <<EOM;
<BLOCKQUOTE>
<BR><INPUT TYPE="submit" VALUE="Start Streamer">
<INPUT TYPE="hidden" NAME="streamer" VALUE="start">
</BLOCKQUOTE>
EOM
			}
		}
	else {
		print <<EOM;
<BLOCKQUOTE>
<FONT COLOR=Red>
You cannot start your streamer until the server specified in your configuration file is reachable. Please contact your server adminstrator.
</FONT>
</BLOCKQUOTE>
EOM
		}
	}
if($CONF{'conf'} !~ /example.conf/) {
	print "<HR><H2>Current Configuration</H2>\n";
	$CONF{'saveconf'} = $CONF{'conf'};
	}
else {
	print "<HR><H2>New Configuration</H2>\n";
	$CONF{'saveconf'} = "newstreamer.conf";
	}
print <<EOM;
</FORM>
<FORM METHOD=POST ACTION="webadmin.pl">
<INPUT TYPE="hidden" NAME="action" VALUE="config">
<INPUT TYPE="hidden" NAME="conf" VALUE="$CONF{'conf'}">
<INPUT TYPE="hidden" NAME="serverpass" VALUE="$CONF{'password'}">
<B>Save config as:</B>
<BR><INPUT TYPE="text" SIZE=20 NAME="saveconf" VALUE="$CONF{'saveconf'}">
<INPUT TYPE="reset">  <INPUT TYPE="submit" VALUE="Save Config">
<P>
<TABLE WIDTH=100% CELLSPACING=0 CELLPADDING=0>
EOM
my($field);
foreach $field (@FIELDS) {
	s/"/\\"/;
	my($description) = $DESC{$field};
	$description =~ s#<(.*?)>#<B>&lt;$1&gt;</B>#ig;
	$description =~ s/#/ /gi;
	print <<EOM;
<TR>
<TD VALIGN=TOP><B>$field</B>&nbsp;</TD>
<TD><FONT SIZE=-1>$description</FONT></TD>
</TR>
<TR>
<TD COLSPAN=2><INPUT TYPE="text" SIZE=80 NAME="f_$field" VALUE="$CONF{$field}"></TD>
</TR>
EOM
		}

print <<EOM;
</TABLE>
</BLOCKQUOTE>
</FORM>
</BODY>
</HTML>
EOM

exit;

##-----------------------------------------------------------------------------
#     Subs
##-----------------------------------------------------------------------------


# ____ login __________________________________________________________________
sub login {
	my($error) = @_;
	my($confglob) = "$CONF{'datadir'}*.conf";
	my(@conffiles) = eval("<$confglob>");
	my($errormsg) = "<CENTER><FONT COLOR=Red>$error</FONT></CENTER>" if($error);
	my($config);

	if(!@conffiles) {
		ScriptError("NO CONFIG","There are no configuration files in your data directory");
		}

	print <<EOM;
<HTML>
<HEAD>
<TITLE>IceDJ Config Login</TITLE>
</HEAD>
<BODY>
$errormsg
<P>Please select a configuration file and enter the appropriate server password:
<P>
<FORM METHOD=POST ACTION="webadmin.pl">
<BLOCKQUOTE>
Select configuration to work with:<BR>
<SELECT NAME="conf">
EOM
	foreach $config (@conffiles) {
		next if($config =~ /example.conf/);
		open(CONF, $config) || ScriptError("OPEN ERR: CONFIG","Request are temporarily offline");
		my(%config);
		while (<CONF>) {
			chomp;
			if(/^#/) { next; } #comment, ignore line
			/(\S+)\s+(.*)$/;
			$config{$1} = $2;
			}
		close(CONF);
		my($conf) = ($config =~ m#([^/]*$)#);
		print <<EOM;
<OPTION VALUE="$conf">$config{'name'}
EOM
		}

	print <<EOM;
<OPTION VALUE="example.conf">New IceDJ streamer
</SELECT>
<BR>Server password:<INPUT TYPE="password" NAME="serverpass">
<BR><INPUT TYPE="reset">  <INPUT TYPE="submit" VALUE="Open">
</BLOCKQUOTE>
</FORM>
</BODY>
</HTML>
EOM

	exit;
	}

# ____ lock ___________________________________________________________________
# locks the data files used for the playlist
# using the lockfile utility provided with procmail for file locking. If you do
# not wish to use it, or have some other file mechanism replace the lockfile
# calls here
sub lock {
#uncomment the below line if you use lockfile() for locking
#	system("/opt/bin/lockfile -2 -r 20 -s 3 -l 30 $LOCKFILE") && dieLog('locking is screwed!');
	}

# ____ unlock _________________________________________________________________
# unlocks the data files used for the playlist
# using the lockfile utility provided with procmail for file locking. If you do
# not wish to use it, or have some other file mechanism replace the lockfile
# calls here
sub unlock {
#uncomment the below lines if you use lockfile() for locking
#	chmod(0666,$LOCKFILE);
#	unlink($LOCKFILE);
	#also want to reset the permissions on our files...
	chmod(0666,$AVAILABLE);
	chmod(0666,$USED);
	}

# ____ ScriptError ____________________________________________________________
# if something goes wrong this returns an error page instead
sub ScriptError {
	my($err,$msg) = @_;
	unlock() if($LOCKED);
#	open(ERR,">>$FAILLOG");
	my(@time) = localtime(time);
	my($i);
	for($i=0;$i<=$#time;$i++)
		{
		if (length($time[$i])<2) {
			$time[$i] = "0$time[$i]";
			}
		}
	my($s,$m,$h,$D,$M,$Y) = @time;
	$M++;
#	print ERR "<$M/$D/$Y\@$h:$m:$s>\t$ENV{'REMOTE_ADDR'}\t$in{'uniqueID'}\t$err\n";
#	close(ERR);
#	open(TEMPLATE, "$CONF{'webroot'}$CONF{'requestpage'}.err");
#	my(@template) = <TEMPLATE>;
#	close(TEMPLATE);
#	my($template) = join('',@template);
#	$template =~ s/<!errmsg>/$msg/gi;
#	print $template;
	print <<EOM;
<HTML>
<HEAD>
<TITLE>Error</TITLE>
</HEAD>
<BODY>
A problem occured, trying to run iceDJ control:
<P><B>$err: $msg</B>
</BODY>
</HTML>
EOM

	exit;
	}
