#!/usr/bin/env perl
#>*******************************************************
# THIS FILE WAS AUTO-GENERATED BY ATLASSIAN STASH.
# IT CONTAINS NO USER-SERVICEABLE PARTS.
#>*******************************************************

# Perl script should be placed in ${stash.home}/bin/git-hooks. This script opens a socket connection to Stash and
# facilitates RPC for hook callbacks.
#
# When Stash calls out to the SCM, it sets the following environment variables:
# - STASH_HOOK_ADDRESS: which IP address Stash is listening on (specific address for these callbacks)
# - STASH_HOOK_PORT: which port Stash is listening on (specific port for these callbacks)
# - STASH_HOOK_REQUEST_ID: an identifier that Stash can use to determine which request the callback applies to

use IO::Socket;
use Encode;

# Utility functions to match the Java counterparts on DataInputStream and DataOutputStream

sub writeUnsignedShort {
    my ($out, $short) = @_;
    my $packed = pack('n', $short);
    print $out $packed;
}

sub writeUTF {
   my ($out, $chunk) = @_;
   my $packed = Encode::encode_utf8($chunk);
   writeUnsignedShort($out, length($packed));
   print $out $packed;
}

sub readUnsignedShort {
    my ($in) = @_;
    read($in, my $packed, 2);
    return unpack('n', $packed);
}

sub readUTF {
    my ($in) = @_;
    my $length = readUnsignedShort($in);
    read($in, my $packed, $length);
    my $unpacked = Encode::decode_utf8($packed);
    return $unpacked;
}

# Open a socket back to the Stash server. This will be used to send and receive information about
# the hook via a channel based protocol
my $socket = new IO::Socket::INET (
			PeerAddr => $ENV{'STASH_HOOK_ADDRESS'},
			PeerPort => $ENV{'STASH_HOOK_PORT'}, 
			Proto => 'tcp');
die "Could not create socket to ping Stash: $!\n" unless $socket;

# Write inputs over socket.

# Ensure raw (binary) output
binmode($socket, ':raw');

# first write the REQUEST ID
print $socket 'X';
writeUTF($socket, $ENV{'STASH_HOOK_REQUEST_ID'});

# then write the hook type
print $socket 'T';
writeUTF($socket, $ARGV[0]);
shift @ARGV;

# then write the args
foreach (@ARGV) {
    # don't be lame, give $_ a name
    local $arg = $_;
    print $socket 'A';
    writeUTF($socket, $arg);
}

# then copy STDIN to the socket, ensuring UTF8 encoding is used
my ($stdInChunk, $n);
binmode(STDIN, ":utf8");
while (($n = read(STDIN, $stdInChunk, 8192)) != 0) {
    print $socket 'I';
    writeUTF($socket, $stdInChunk);
}

# Signify that this is the end of the clients communication
print $socket 'E';

# Read channels from socket

# Ensure UTF8 on stdout/err
binmode(STDOUT, ":utf8");
binmode(STDERR, ":utf8");

my $exitCode = 0;
while (True) {
    read($socket, my $c, 1);
    if ($c eq "O") {
        # STDOUT channel. Read the next chunk and write it to STDOUT
        print STDOUT readUTF($socket);
        next;
    } elsif ($c eq "E") {
        # STDERR channel. Read the next chunk and write it to STDERR
        print STDERR readUTF($socket);
        next;
    } elsif ($c eq "X") {
        # Exit code channel. Read short and use it to exit
        $exitCode = readUnsignedShort($socket);
        last;
    } else {
        # The server isn't giving back valid data. Bail
        print STDOUT "Communication breakdown with Stash.\n";
        $exitCode = 1;
        last;
    }
}

# close the socket
close($socket);

# finally exit with the correct status code
exit $exitCode
