#!/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;

umask(022);

die "usage: $0 start_dir\n" unless @ARGV;

$| = 1; # unbuffered stdout

# gpg (via pinentry) will read the passphrase from the terminal
$ENV{'GPG_TTY'} = `tty`;
if ($ENV{'GPG_TTY'} =~ /not a tty/) {
    die "$0: a tty is required\n";
}

# Also used by wanted()
my @signfiles;
my %filelist;

foreach my $startdir (@ARGV) {
    undef @signfiles;
    undef %filelist;

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

    # Sign any files without a signature (detached or embedded)
    foreach (@signfiles) {
	if (/\.rpm$/) {
	    $cmd = "rpm --addsign $_";
	} elsif (/\.deb$/) {
	    $cmd = "debsigs --sign=origin --gpgopts=--batch $_";
	} else {
	    $cmd = "gpg --yes --batch -b $_";
	}
	system($cmd);
    }

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

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 (/\.deb$/) {
		    # Old way: just check for presence of signature
		    #my @out = `debsigs --list --gpgopts=--batch,--no-tty,--pinentry-mode=loopback $file`;
		    #$sign_it = 0 if grep(": signed by ", @out);
		    # Note: requires local changes to debsigs
		    system("debsigs --verify --gpgopts=--batch,--no-tty,--pinentry-mode=loopback $file >/dev/null 2>&1");
		    $sign_it = $? >> 8;
		} elsif (-f "$file.sig") {
		    system("gpg --quiet --batch --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";
	    }
	    system("gpg --yes --batch -b $dir/MD5");
	}
    }
}

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";
	    }
	    system("gpg --yes --batch -b $dir/SHA256");
	}
    }
}
