summaryrefslogtreecommitdiffstats
path: root/scripts/gpg-diff
diff options
context:
space:
mode:
Diffstat (limited to '')
-rwxr-xr-xscripts/gpg-diff204
1 files changed, 204 insertions, 0 deletions
diff --git a/scripts/gpg-diff b/scripts/gpg-diff
new file mode 100755
index 0000000..29f45ca
--- /dev/null
+++ b/scripts/gpg-diff
@@ -0,0 +1,204 @@
+#!/usr/bin/perl -w
+
+# Copyright (c) 2007 Anthony Towns
+# GNU GPL; v2 or later
+# Gives an overview of what changed between two keyrings
+
+# Take from jetring-diff and modified to be suitable for git.
+# Copyright (c) 2007 Jonathan McDowell <noodles@earth.li>
+
+use strict;
+use Cwd q{abs_path};
+use File::Temp qw(tempdir);
+use warnings;
+use strict;
+
+if (@ARGV != 2 and @ARGV != 7) {
+ die "usage: gpg-diff old.gpg new.gpg | path old.gpg old-hex old-mode ".
+ "new.gpg new-hex new-mode\n";
+}
+
+# avoid gnupg touching ~/.gnupg
+$ENV{GNUPGHOME}=tempdir("jetring.XXXXXXXXXX", TMPDIR => 1, CLEANUP => 1);
+
+my ($l, $r);
+
+if (@ARGV == 7) {
+ # Print a diff style header
+ print "gpg-diff a/$ARGV[0] b/$ARGV[4]\n";
+ print "--- a/$ARGV[0]\n";
+ print "+++ b/$ARGV[4]\n";
+ print "\n";
+
+ if ($ARGV[4] eq '/dev/null') {
+ print "Key deleted\n";
+ exit 0;
+ }
+
+ $l = parse_keyring($ARGV[1]);
+ $r = parse_keyring($ARGV[4]);
+} else {
+ $l = parse_keyring(shift);
+ $r = parse_keyring(shift);
+}
+
+foreach my $id (sort keys %{$l}) {
+ if (not exists $r->{$id}) {
+ summary("-", @{$l->{$id}});
+ }
+ else {
+ my $diff=0;
+ my @out;
+
+ my %rpackets = map { comparable($_->{'details'}) => $_ }
+ @{$r->{$id}};
+ my %lpackets = map { comparable($_->{'details'}) => 1 }
+ @{$l->{$id}};
+
+ foreach my $packet (@{$l->{$id}}) {
+ if (defined($rpackets{comparable($packet->{'details'})})) {
+ push @out, " ".outformat($packet->{'details'});
+ push @out, comparesigs(\$diff, $packet->{'sigs'},
+ $rpackets{comparable($packet->{'details'})}->{'sigs'});
+ } else {
+ push @out, "-".outformat($packet->{'details'});
+ $diff = 1;
+ }
+ }
+
+ foreach my $packet (@{$r->{$id}}) {
+ if (! $lpackets{comparable($packet->{'details'})}) {
+ push @out, "+".outformat($packet->{'details'});
+ $diff = 1;
+ }
+ }
+
+ print @out if $diff;
+ }
+}
+foreach my $id (sort keys %{$r}) {
+ if (not exists $l->{$id}) {
+ summary("+", @{$r->{$id}});
+ }
+}
+
+sub parse_keyring {
+ my $k=shift;
+
+ $k=abs_path($k); # annoying gpg..
+ my $cache=$k.".cache";
+
+ my $cached=0;
+ my $kmtime=(stat($k))[9];
+ if (-e $cache) {
+ my $cmtime=(stat($cache))[9];
+ if ($kmtime == $cmtime) {
+ open(DUMP, $cache) || die "$cache: $!";
+ $cached=1;
+ }
+ }
+ if (! $cached) {
+ open(DUMP, "gpg --options /dev/null --no-default-keyring ".
+ "--no-auto-check-trustdb --keyring $k --list-sigs ".
+ "--fixed-list-mode --with-colons -q |")
+ or die "couldn't dump keyring $k: $!";
+# Disable caching for the moment
+# if (! open(CACHE, ">$cache")) {
+# print STDERR "warning: cannot write cache $cache\n";
+ $cache=undef;
+# }
+ }
+ my %keys;
+ my $id;
+ my $packet;
+ while (<DUMP>) {
+ if (! $cached && defined $cache) {
+ print CACHE $_;
+ }
+ chomp;
+
+ my @fields=split(":", $_);
+ $fields[5]="-"; # ignore creation date, varies
+ next if $fields[0] eq 'tru';
+ if ($fields[0] eq 'pub') {
+ $id=$fields[4];
+ }
+ if ($fields[0] ne 'sig' && $fields[0] ne 'rev') {
+ if (defined($packet)) {
+ push @{$keys{$id}}, $packet;
+ undef $packet;
+ }
+ $packet->{'details'} = \@fields;
+ } else {
+ if (! defined $id or !defined($packet)) {
+ die "parse error: $_";
+ next;
+ }
+ push @{$packet->{'sigs'}}, \@fields;
+ }
+ }
+ push @{$keys{$id}}, $packet;
+ close DUMP;
+
+ if (defined $cache) {
+ close CACHE;
+ utime($kmtime, $kmtime, $cache) ||
+ print STDERR "warning: failed setting cache time: $!";
+ }
+
+ return \%keys;
+}
+
+sub summary {
+ my $prefix=shift;
+
+ foreach my $record (@_) {
+ if (ref $record eq 'HASH') {
+ summary($prefix, $record->{$_})
+ foreach reverse sort keys %$record;
+ }
+ else {
+ if ($record->[0] eq 'pub' || $record->[0] eq 'uid') {
+ print "$prefix".outformat($record);
+ }
+ }
+ }
+}
+
+sub outformat {
+ return join(":", @{shift()})."\n";
+}
+
+sub comparable {
+ my @record=@{shift()};
+ if ($record[0] eq 'sig') {
+ # Displayed user ids for sigs vary, so compare different
+ # ones the same. The user-id is what matters.
+ $record[9]="";
+ }
+ return join(":", @record);
+}
+
+sub comparesigs {
+ my $diff = shift;
+ my $l = shift;
+ my $r = shift;
+ my %lseen = map { comparable($_) => 1 } @{$l};
+ my %rseen = map { comparable($_) => 1 } @{$r};
+ my @out;
+
+ foreach my $record (@{$l}) {
+ if (! $rseen{comparable($record)}) {
+ push @out, "-".outformat($record);
+ ${$diff} = 1;
+ }
+ }
+ foreach my $record (@{$r}) {
+ if (! $lseen{comparable($record)}) {
+ push @out, "+".outformat($record);
+ ${$diff} = 1;
+ }
+ }
+
+ return @out;
+}