#!/usr/bin/perl

# Distmp3, a program for distributing the encoding of music among several computers.
# Copyright (C) 2000  Martin Josefsson
#
# 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.
#
# 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.
#
#
# Author: Martin Josefsson <gandalf@wlug.westbo.se>
#

use IO::Socket;
use IO::Handle;
use Net::hostent;

$|=1;

#----------------------------
$version = "0.1.6";
$debug = 1;
#$debug_file = "debug.file";

$wav_fifo = "/tmp/fifo.wav";
$mp3_fifo = "/tmp/fifo.mp3";
$local_port = 4600;
$program = "/bin/bash -c \"lame $wav_fifo $mp3_fifo\"";

$RTR = "hejhopp_rtr";
$RTS = "hejhopp_rts";
$ACK = "hejhopp_ack";

$SRV_ID = "hejhopp_server_id";
$CLI_ID = "hejhopp_client_id";

$DATA_SIZE = 16384;
#----------------------------

$SIG{INT} = sub { 
		if ( $debug || $debug_file ) {
			printd("Caught INT signal, going down in flames...\n");
		}
		if ( $debug_file ) {
			close DEBUG;
		}
		print "Exited gracefully...\n"; 
		shutdown $local,2;
		close WAV_FIFO;
		close MP3_FIFO;
		exit(1); 
		};

$SIG{TERM} = sub { 
		if ( $debug || $debug_file ) {
			printd("Caught TERM signal, going up in smoke...\n");
		}
		if ( $debug_file ) {
			close DEBUG;
		}
		print "Exited gracefully...\n"; 
		shutdown $local,2;
		close WAV_FIFO;
		close MP3_FIFO;
		exit(1); 
		};

sub open_local_connection
{
printd('Opening local Socket');
$local = IO::Socket::INET->new(Proto     => "tcp",
		  	       LocalPort => $local_port,
			       Listen    => SOMAXCONN,
			       Reuse     => 1)
	or die "Can't bind port $local_port on localhost: $!";
$local->autoflush(1);
printd("Port $local_port on localhost ready for connections");
}

sub check_fifo
{
unless (-p $mp3_fifo) {
	printd('creating mp3_fifo $mp3_fifo');
	unlink $mp3_fifo;
	system('mknod', '-m 0600', $mp3_fifo, 'p') && die "can't mknod $mp3_fifo: $!";
}
unless (-p $wav_fifo) {
	printd('creating wav_fifo $wav_fifo');
	unlink $wav_fifo;
	system('mknod', '-m 0600', $wav_fifo, 'p') && die "can't mknod $wav_fifo: $!";
}
}

sub run_program
{
if ( $debug ) {
	$extra_param = "&";
}
else
{
	$extra_param = "2&>/dev/null &";
}
system qq!$program $extra_param! || die "Couldn't run program $program";
printd("Executed program $program");
}

sub open_debug_file
{
open (DEBUG, ">>$debug_file") or die "Couldn't open debug file $debug_file";
}

sub close_debug_file
{
close DEBUG;
}

sub open_wav_fifo
{
printd('opening wav-fifo');
open (WAV_FIFO, "> $wav_fifo") || die "Can't open fifo $wav_fifo for writing: $!";
WAV_FIFO->autoflush(1);
}

sub open_mp3_fifo
{
printd('opening mp3-fifo');
open (MP3_FIFO, "< $mp3_fifo") || die "Can't open fifo $mp3_fifo for reading: $!";
MP3_FIFO->autoflush(1);
}

sub close_wav_fifo
{
printd('closing wav-fifo');
close WAV_FIFO;
}

sub close_mp3_fifo
{
printd('closing mp3-fifo');
close MP3_FIFO;
}

sub send_data
{
$data = $_[0];
print $remote_client $data;
}

sub recieve_data
{
read $remote_client,$rdata,$DATA_SIZE;
return $rdata;
} 

sub read_from_mp3
{
read MP3_FIFO,$wdata,$DATA_SIZE;
return $wdata;
}

sub write_to_wav
{
$mdata = $_[0];
print WAV_FIFO $mdata;
}

sub handshake
{
printd('initiate handshake');

printd('waiting for CLI_ID');
read $remote_client,$answer,length($CLI_ID);
printd('got CLI_ID');
if ( $answer ne $CLI_ID ) { $cli_error = 1; }
if ( $cli_error eq 1 ) {
	printd("Client isn't who he should be");
	$cli_error = 0;
}
else
{
	$cli_error = 0;
	printd('Ok, Client id is what it should be');

	send_data($SRV_ID);
	printd('Sent SRV_ID');

	printd('Waiting for ACK');
	read $remote_client,$answer,length($ACK);
	printd('got ACK');
	if ( $answer ne $ACK ) { $ack_error = 1; }
	if ( $ack_error eq 1 ) {
		printd("Ack isn't Ack");
		$ack_error = 0;
	}
	else
	{
		$ack_error = 0;
		printd('ACK ok');
		return "1";
	}
}
}

sub printd
{
if ( $debug ) {
	$message = $_[0];
	print "$message\n";
}
if ( $debug_file ) {
	$message = $_[0];
	print DEBUG "$message\n";
}
}



#Huvudprogram

if ( $debug_file ) {
	open_debug_file;
}
printd("Distmp3 daemon version $version");

check_fifo;

open_local_connection;

while ($remote_client = $local->accept()) {
	$remote_client->autoflush(1);
	if ( $hostinfo = gethostbyaddr($remote_client->peeraddr) ) { 
		$remote_host = $hostinfo->name; 
	}
	else
	{
		$remote_host = $remote_client->peerhost;
	}

	printd("Connect from $remote_host");

	if ( handshake ) { 

		run_program;

		printd('Forking like hell');

		unless ($pid = fork) {
			printd('forking child for retrieving data from fifo and sending it to the client');
			send_data($RTS);
			printd('Sent RTS');
			open_mp3_fifo;
			while ( $data = read_from_mp3 ) {
				send_data($data);
			}
			close_mp3_fifo;
			shutdown $remote_client,1;
			exit(0);
		}

		unless ($pid2 = fork) {
			printd('forking child for retrieving data from the client and sending it to the fifo');
			send_data($RTR);
			printd('Sent RTR');
			open_wav_fifo;
			while ( $data2 = recieve_data ) {
				write_to_wav($data2);
			}
			close_wav_fifo;
			exit(0);
		}	

		waitpid($pid2,0);
		waitpid($pid,0);
	}
printd("Connection from $remote_host terminated");
}
