#!/usr/bin/perl
#
# Quick hack to make SHA256 files and create gpg detached signatures
# for all packages and SHA256 files.

use warnings;
use File::Find;
use Digest::MD5;
use Digest::SHA;
use POSIX qw(setsid);

umask(022);

my @signfiles = ();
my %filelist;
my $startdir = $ARGV[0];
die "usage: $0 start_dir\n" unless defined($startdir);

$| = 1; # unbuffered stdout

# Read passphrase early--we don't know if we need to use it yet
# XXX - should verify passphrase is correct (try signing temp file?)
system("stty -echo");
print "Enter passphrase: ";
chomp(my $pass = <STDIN>);
system("stty echo");
print "\n";

print "Checking existing signatures";
find(\&wanted, $startdir);
print "\n";

# Sign any files without a signature (detached or embedded)
foreach (@signfiles) {
    my $pid = open(CHILD, "|-");
    die "unable to fork\n" unless defined($pid);
    if ($pid == 0) {
	# Child
	if (/\.rpm$/) {
	    # Disassociate from tty so we can pipe the passphrase
	    setsid();
	    exec("rpm", "--addsign", $_);
	} else {
	    exec("gpg", "--yes", "--passphrase-fd", "0", "-b", $_);
	}
	exit 1;
    }
    # Parent: write passphrase
    print CHILD $pass;
    close(CHILD);
}

print "Computing md5 and sha256 digests\n";
foreach my $file (keys %filelist) {
    write_md5($file, @{$filelist{$file}});
    write_sha($file, @{$filelist{$file}});
}

$pass =~ s/./x/g; # probably not effective

exit(0); # XXX - return an error if warranted

sub wanted {
    my $dir = $_;
    if (-d && opendir(DIR, $dir)) {
	my @files = ();
	while (defined(my $file = readdir(DIR))) {
	    next unless $file =~ /\.(gz|deb|rpm|pkg)$/;
	    push(@files, $file);
	}
	if (int(@files)) {
	    print ".";
	    $filelist{$File::Find::name} = \@files;
	    foreach (@files) {
		# Only need to sign if no valid existing signature
		my $file = "$dir/$_";
		my $sign_it = 1;
		if (/\.rpm$/) {
		    my $output = `rpm --checksig $file`;
		    if ($output =~ / gpg .*OK$/) {
			$sign_it = 0;
		    }
		} elsif (-f "$file.sig") {
		    system("gpg -quiet --no-tty --verify $file.sig $file >/dev/null 2>&1");
		    $sign_it = $? >> 8;
		}
		push(@signfiles, $File::Find::name . "/$_") if $sign_it;
	    }
	}
	closedir(DIR);
    }
}

sub write_md5 {
    my ($dir, @files) = @_;

    # Calculate MD5 digest of all the files in the dir
    my $md5 = Digest::MD5->new;
    my %digests;
    foreach my $file (@files) {
	if (open(FILE, '<', "$dir/$file")) {
	    $md5->addfile(*FILE);
	    $digests{$file} = $md5->hexdigest;
	    close(FILE);
	    $md5->reset;
	} else {
	    warn "$dir/$file: $!\n";
	}
    }
    if (scalar(keys %digests)) {
	my $modified = 1;
	if (open(MD5, '<', "$dir/MD5")) {
	    my %old_digests;
	    while (<MD5>) {
		chomp;
		my ($digest, $file) = split;
		$old_digests{$file} = $digest;
	    }
	    close(MD5);
	    # Check existing digest file for modifications
	    if (scalar(keys %old_digests) == scalar(keys %digests)) {
		$modified = 0;
		foreach my $file (sort keys %digests) {
		    if ($old_digests{$file} ne $digests{$file}) {
			$modified = 1;
			last;
		    }
		}
	    }
	}
	if ($modified) {
	    if (open(MD5, '>', "$dir/MD5")) {
		# Digest file missing or changed, write a new one
		foreach my $file (sort keys %digests) {
		    print MD5 "$digests{$file} $file\n";
		}
		close(MD5);
	    } else {
		warn "$dir/MD5: $!\n";
	    }
	    open(GPG, "|gpg --yes --passphrase-fd 0 -b $dir/MD5");
	    print GPG $pass;
	    close(GPG);
	}
    }
}

sub write_sha {
    my ($dir, @files) = @_;

    # Calculate SHA256 digest of all the files in the dir
    my $sha = Digest::SHA->new(256);
    my %digests;
    foreach my $file (@files) {
	if (open(FILE, '<', "$dir/$file")) {
	    $sha->addfile(*FILE);
	    $digests{$file} = $sha->hexdigest;
	    close(FILE);
	    $sha->reset;
	} else {
	    warn "$dir/$file: $!\n";
	}
    }
    if (scalar(keys %digests)) {
	my $modified = 1;
	if (open(SHA256, '<', "$dir/SHA256")) {
	    my %old_digests;
	    while (<SHA256>) {
		chomp;
		my ($digest, $file) = split;
		$old_digests{$file} = $digest;
	    }
	    close(SHA256);
	    # Check existing digest file for modifications
	    if (scalar(keys %old_digests) == scalar(keys %digests)) {
		$modified = 0;
		foreach my $file (sort keys %digests) {
		    if ($old_digests{$file} ne $digests{$file}) {
			$modified = 1;
			last;
		    }
		}
	    }
	}
	if ($modified) {
	    if (open(SHA256, '>', "$dir/SHA256")) {
		# Digest file missing or changed, write a new one
		foreach my $file (sort keys %digests) {
		    print SHA256 "$digests{$file} $file\n";
		}
		close(SHA256);
	    } else {
		warn "$dir/SHA256: $!\n";
	    }
	    open(GPG, "|gpg --yes --passphrase-fd 0 -b $dir/SHA256");
	    print GPG $pass;
	    close(GPG);
	}
    }
}
