#!/usr/bin/perl
#$Id: LATEX-server,v 1.4 2012/10/31 08:05:43 sofc Exp $
use strict;
use warnings;
use IO::Socket;
use IO::Interface::Simple;
use File::Copy;
use Impedance::Header;
use Sys::Hostname;
use LATEX;
my $VERSION = 0.3;
my $PROGRAM = $0;
my $debug = 0;

if (scalar(@ARGV) > 0)
{
    for(my $x = 0; $x < scalar(@ARGV);$x++)
    {
	if ($ARGV[$x] =~ /--version/)
	{
	    print "$PROGRAM is: $VERSION\n";
	    exit 0;
	}
	if ($ARGV[$x] =~ /--debug/)
	{
	    $debug = 1;
	}
    } 
}
our $host  =  'localhost';
our $port    =  '4050';
our $user      = "nobody";
print "Using host $host and port $port\n" if ($debug);
# Let children perish
#
$SIG{"CHLD"} = "DEFAULT";

# Get userid for $user. Abort if userid is zero (superuser) or
# non-existent.
#
unless ( our $uid = ( getpwnam($user) )[2] ) 
{
    die "Attempt to run server as non-existent or superuser\n";
}

# Initialize C structure#
our $server_addr   = ( gethostbyname($host) )[4];
our $server_struct = pack( "S n a4 x8", AF_INET, $port, $server_addr );
print "Server address: $server_addr\n" if ($debug);

# Set up socket#
our $proto = ( getprotobyname("tcp") )[2];
socket( SOCK, PF_INET, SOCK_STREAM, $proto )
    || die "Failed to initialize socket: $!\n";

# Bind to address/port and set up pending queue #
setsockopt( SOCK, SOL_SOCKET, SO_REUSEADDR, 1 )  || die "setsockopt() failed: $!\n";
bind( SOCK, $server_struct ) || die "bind() failed: $!\n";
listen( SOCK, SOMAXCONN ) || die "listen() failed: $!\n";

our %cmdlst = ();
$cmdlst{'debug'} = \&debug;
$cmdlst{'exit'} = \&shutdown;
$cmdlst{'unlink'} = \&unlink_file;
$cmdlst{'compile'} = \&compile;

# Deal with requests #
our $remote_host;
for ( ; ; ) {

    # Grab next pending request
    #
    $remote_host = accept( NEWSOCK, SOCK );
    die "accept() error: $!\n" unless ($remote_host);
    ### Process request ###
    &request();
    close(NEWSOCK);
}

# Close master socket
close(SOCK);
exit;

sub request
{
    # Resolve hostname of remote machine before calling chroot()
    our $raw_addr = ( unpack( "S n a4 x8", $remote_host ) )[2];
    our $dot_addr = join ( ".", unpack( "C4", $raw_addr ) );
    our $name = ( gethostbyaddr( $raw_addr, AF_INET ) )[0];
    # Read client request and get $path
    our @args = ();
    our $cmd = undef;
    my $ltime = localtime;
    print "$ltime\t$dot_addr\t$name\t" if ($debug);
    my $str = <NEWSOCK>;
    chomp $str;
    ($cmd,@args) = split(/\t+/,$str);
    if ($cmdlst{$cmd})
    {
        my $ok = $cmdlst{$cmd}->(@args);
        print "'$cmd'\tResult: $ok\n" if ($debug);
        print NEWSOCK $ok;
    }
    else
    {
        print "Unknown command: '$cmd'\n";
        print NEWSOCK -1;
    }
}


sub compile($)
{
    my $res = '';
    my $file = shift;
    chdir '/tmp/';
    my ($pdf,$err) = &LATEX::compile_doc($file);
    if ($err)
    {
        $res .= "Error: $err\n";
        unlink $pdf if ($pdf && -e $pdf);
        unlink $file if (-e $file);
        return $res;
    }
    unlink $file if (-e $file);
    chmod 0666,$pdf;
    $pdf =~ /\/tmp\/([\w\.]+)/;
    $pdf = $1;
    eval {
	### Fix for SELinux ###                                                                                                       
	my $cmd = 'chcon -v --type=httpd_sys_rw_content_t /tmp/'.$pdf;
        system($cmd);
    };
    move('/tmp/'.$pdf,$Impedance::Header::htmldir.'/png/'.$pdf) or die "Could not move $pdf $!\n";
    return $pdf;
}

sub unlink_file($)
{
    my $file = shift;
    return "File $file not found" unless (-e $file);
    unlink $file;
    return "File $file unlinked!";
}

sub debug
{
    $debug = !$debug;
    return "Debug: $debug";
}

sub shutdown
{
    print "\n\nShutting down server!\n";
    print NEWSOCK "Shutting down server!";
    close(SOCK);
    stop_remote();
    exit;
}

