#=================================================================
# imp_gpg.pm - Imprints support for gnuGPG
#
# A set of routines to handle calls to the gnuPG package.
#=================================================================

package imp_gpg;

use strict;
use vars qw($GPG_BINARY @GPG_ARGS @ISA @EXPORT);

@ISA = qw(Exporter);
@EXPORT = qw(impgpg_set_binary
	     impgpg_binary_exists
	     impgpg_set_args
	     impgpg_parse_detsig
	     impgpg_build_sigfile 
	     impgpg_print_sigfile
	     impgpg_verify_signature
	     impgpg_verify_signature_using_sigfile
	     impgpg_install_pubkey);

$GPG_BINARY = "/usr/bin/gpg";
@GPG_ARGS = ();

#-----------------------------------------------------------------
# Set the name of the gpg binary. The $bin parameter should
# include the full path and file name.
#-----------------------------------------------------------------
sub impgpg_set_binary {
    my ($bin) = @_;

    $GPG_BINARY = $bin;

    print STDERR "Binary set to [$bin]\n";
}

#-----------------------------------------------------------------
# Return 1 if the binary ($GPG_BINARY) exists and is executable,
# else return 0;
#-----------------------------------------------------------------
sub impgpg_binary_exists {
    my $rc;

    if (-x $GPG_BINARY) {
	$rc = 1;
    }
    else {
	$rc = 0;
    }
}

#-----------------------------------------------------------------
# Set the standard arguments used by subsequent impgpg functions.
# These are arguments that appear on the gpg command line.
#-----------------------------------------------------------------
sub impgpg_set_args {
    @GPG_ARGS = @_;
}

#-----------------------------------------------------------------
# Parse an ASCII-armored detached sig file and return a return 
# code, the version string, signature, and CRC. Returns a non-zero
# return code (first returned value) on failure.
#-----------------------------------------------------------------
sub impgpg_parse_detsig {
    my ($sigfile) = @_;

    my $version;
    my $sig,
    my $crc;
    my $rc = 0;
    my $state = "";

    open (INF, "< $sigfile");
    # Run through a simple state machine to break file into
    # pieces.
    while (<INF>) {
	chop;

	# Start state
	if ($state eq "") {
	    if (index ($_, "BEGIN PGP SIGNATURE", 0) >= 0) {
		$state = "VERSION";
	    }
	    else {
		last;
	    }
	}
	elsif ($state eq "VERSION") {
	    if (substr ($_, 0, 9) eq "Version: ") {
		$state = "BLANKLINE";
		$version = substr ($_, 9, 255);
	    }
	}
	elsif ($state eq "BLANKLINE") {
	    if ($_ eq "") {
		$state = "READSIG";
	    }
	}
	elsif ($state eq "READSIG") {
	    $sig .= $_;
	    if (substr ($_, length($_) - 1, 1) eq "=") {
		$state = "READCRC";
	    }
	}
	elsif ($state eq "READCRC") {
	    $crc = $_;
	    $state = "READEND";
	}
	elsif ($state eq "READEND") {
	    if (index ($_, "END PGP SIGNATURE", 0) >= 0) {
		$state = "DONE";
	    }
	}
    }
    close INF;

    if ($state ne "DONE") {
	$rc = 1;
    }
    
    return ($rc, $version, $sig, $crc);
}

#-----------------------------------------------------------------
# Given the pieces of an ASCII-armored gnuGPG signature, construct
# and return the text of a signature file.
#-----------------------------------------------------------------
sub impgpg_build_sigfile {
    my ($ver, $sig, $crc) = @_;

    my $data = "-----BEGIN PGP SIGNATURE-----\n" 
	      . "Version: $ver\n"
	      . "Comment: For info see http://www.gnupg.org\n"
	      . "\n";
	    
    while (length ($sig) > 64) {
	my $chunk = substr ($sig, 0, 64);
	$sig = substr ($sig, 64, 1024);
	$data .= "$chunk\n";
    }

    if (length ($sig) > 0) {
	$data .= "$sig\n";
    }

    $data .= "$crc\n"
	     . "-----END PGP SIGNATURE-----\n";

    return $data;
}

#-----------------------------------------------------------------
# Given a file name and the pieces of an ASCII-armored gnuPGP
# signature, construct the detached signature text and write to
# the specified file.
#-----------------------------------------------------------------
sub impgpg_print_sigfile {
    my ($outfile, $ver, $sig, $crc) = @_;

    my $data = impgpg_build_sigfile ($ver, $sig, $crc);

    unless (open (OUTF, "> $outfile")) {
	return 1;
    }
    print OUTF $data;
    unless (close (OUTF)) {
	return 1;
    }

    return 0; # success
}

#-----------------------------------------------------------------
# Verify the gnuGPG signature of a file.
#-----------------------------------------------------------------
sub impgpg_verify_signature {
    my ($infile, $ver, $sig, $crc) = @_;

    # "Reconstitute" a facsimile of the original
    # detached signature file from its pieces.

    my $data = impgpg_build_sigfile ($ver, $sig, $crc);
    
    # verify the file

    my $rc = system ("echo -en \"$data\" " 
	       	     . "| $GPG_BINARY @GPG_ARGS --verify - $infile 2> /dev/null");

    return $rc;
}

#-----------------------------------------------------------------
# Verify the file $infile, using the signature file $sigfile.
#-----------------------------------------------------------------
sub impgpg_verify_signature_using_sigfile {
    my ($infile, $sigfile) = @_;

    my $rc;

    $rc = system ($GPG_BINARY, @GPG_ARGS, 
		  "--verify", $sigfile, 
		  $infile);
    return $rc;
}

#-----------------------------------------------------------------
# Install public key.
#-----------------------------------------------------------------
sub impgpg_install_pubkey {
    my ($key_filename) = @_;
    my $rc;

    $rc = system ($GPG_BINARY, @GPG_ARGS, 
		  "--import", $key_filename);

    return $rc;
}

1;
