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

use Cwd;
use IO::Socket;
use Encode;
use File::Basename;

# 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(defined($chunk) ? $chunk : "");
    writeUnsignedShort($out, length($packed));
    print $out $packed;
}

sub readBinary {
    my ($in) = @_;
    my $length = readUnsignedShort($in);
    read($in, my $packed, $length);
    return $packed;
}

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

# Open a socket back to the server. This will be used to send and receive information about the callback via a channel
# based protocol.
my $socket = new IO::Socket::INET (
    PeerAddr => $ENV{'BB_SIGNING_CALLBACK_ADDRESS'},
    PeerPort => $ENV{'BB_SIGNING_CALLBACK_PORT'},
    Proto => 'tcp');
die "Could not create the socket to trigger the Bitbucket callback for signing Git objects: $!\n" unless $socket;

# Write inputs over socket.

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

# Write any additional args.
foreach my $arg (@ARGV) {
    print $socket 'A';
    writeUTF($socket, $arg);
}

# Copy STDIN to the socket, ensuring UTF8 encoding is used. STDIN is consumed in 16380 _UTF-8 character_ chunks. The
# max packet size is 65535 _bytes_, so 16380 is used to ensure no STDIN chunk exceeds what can be written in a single
# packet (x4 for max UTF-8 binary encoding).
my ($stdInChunk, $n);
binmode(STDIN, ":utf8");
while (($n = read(STDIN, $stdInChunk, 16380)) != 0) {
    print $socket 'I';
    writeUTF($socket, $stdInChunk);
}

# Signify that this is the end of the client's communication.
print $socket 'E';

# Read channels from socket.

# Ensure raw (binary) on STDOUT/ERR. (We may write encoded UTF-8 to them, but that should produce the correct results.)
binmode(STDOUT, ":raw");
binmode(STDERR, ":raw");

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 readBinary($socket);
        next;
    } elsif ($c eq "E") {
        # STDERR channel. Read the next chunk and write it to STDERR.
        print STDERR readBinary($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 STDERR "Unexpected data detected when signing the Git object.\n";
        $exitCode = 1;
        last;
    }
}

# Close the socket.
close($socket);

# Finally exit with the correct status code.
exit $exitCode