5 # See the pod documentation at the end of this file for author,
6 # copyright, and licence information.
9 # libintl-perl (Locale::Recode)
10 # OR libtext-iconv-perl (Text::Iconv),
11 # OR the "recode" binary
16 # * use the user's normal keyring to find signatures
17 # * support for multiple user keys
18 # * better charset conversion
20 # see the Debian changelog for further changes.
22 my $VERSION = qq$Rev$;
36 gpgsigs $VERSION- http://pgp-tools.alioth.debian.org/
37 (c) 2004 Uli Martens <uli\@youam.net>
38 (c) 2004, 2005 Peter Palfrader <peter\@palfrader.org>
39 (c) 2004, 2005, 2006, 2007 Christoph Berg <cb\@df7cb.de>
45 my ($fd, $error) = @_;
50 Usage: $PROGRAM_NAME [-r] [-t <charset>] <keyid> <keytxt> [<outfile>]
52 keyid is a long or short keyid (e.g. DE7AAF6E94C09C7F or 94C09C7F)
53 separate multiple keyids with ','
54 -r call gpg --recv-keys before proceeding
55 -f <charset> convert <keytxt> from charset
56 -t <charset> convert UIDs to charset in output
62 my ($fromcharset, $charset, $recv_keys, $refresh, $latex);
63 Getopt
::Long
::config
('bundling');
65 '-f=s' => \
$fromcharset,
70 help
=> sub { usage
(*STDOUT
, 0); },
71 version
=> sub { version
(*STDOUT
); exit 0;},
72 ) or usage
(*STDERR
, 1);
76 $fromcharset ||= "ISO-8859-1";
77 $charset ||= $ENV{LC_ALL
} || $ENV{LC_CTYPE
} || $ENV{LANG
} || "ISO-8859-1";
78 $charset = "ISO-8859-1" unless $charset =~ /[\.-]/;
84 my ($text, $from, $to) = @_;
86 if (eval "require Locale::Recode") {
87 my $rt = Locale
::Recode
->new (from
=> $from, to
=> $to);
92 } elsif (eval "require Text::Iconv") {
93 my $it = Text
::Iconv
->new($from, $to);
95 my $result = $it->convert($text);
96 warn ("Could not convert '$text'\n") unless defined $result;
97 return (defined $result) ?
$result : $text
99 my $pid = open3
(\
*WTRFH
, \
*RDRFH
, \
*ERRFH
, 'recode', "utf8..$charset");
103 my $result = <RDRFH
>;
107 warn ("'recode' failed, is it installed?\n") unless defined $result;
108 return (defined $result) ?
$result : $text
114 my @mykeys = split /,/, uc(shift @ARGV);
115 my $keytxt = (shift @ARGV) || usage
(*STDERR
, 1);
116 my $outfile = (shift @ARGV) || '-';
118 map { s/^0x//i; } @mykeys;
119 my %uids = map { $_ => [] } @mykeys;
121 if (!@mykeys || scalar @ARGV) {
124 foreach my $falsekey (grep { $_ !~ /^([0-9A-F]{16,16}|[0-9A-F]{8,8})$/ } @mykeys) {
125 print STDERR
"Invalid keyid $falsekey given\n";
129 -r
$keytxt or die ("$keytxt does not exist\n");
132 # get list of keys in file
134 open (TXT
, $keytxt) or die ("Cannot open $keytxt\n");
136 if ( m/^pub +(?:\d+)[DR]\/([0-9A
-F
]{8}) [0-9]{4}-[0-9]{2}-[0-9]{2} *(.*)/ ) {
143 # get all known signatures
145 print STDERR
"Requesting keys from keyserver\n";
146 system "gpg --recv-keys @keys";
149 print STDERR
"Running --list-sigs, this will take a while ";
150 open SIGS
, "gpg --fixed-list-mode --with-colons --list-sigs @mykeys @keys 2>/dev/null |"
151 or die "can't get gpg listing";
153 my ($key, $uid, $sigs);
155 if ( m/^pub:(?:.*?:){3,3}([0-9A-F]{16,16}):/ ) {
160 if ( m/^uid:(.):(?:.*?:){7,7}(.*):/s ) {
163 $uid =~ s/\\x([0-9a-f][0-9a-f])/ chr(hex($1)) /gie;
164 $uid = myrecode
($uid, "UTF-8", $charset);
166 my ($shortkey) = substr $key, -8;
167 # Remember non-revoked uids
168 next if $uidstatus eq "r";
169 push @
{$uids{$shortkey}}, $uid;
172 if ( m/^uat:(.)::::[^:]+::([0-9A-F]+)::\d+ (\d+)/ ) { # uat:-::::2006-08-03::27BAEAF742BD253C2F3F03B043DC1536880193C4::1 7993:
174 # $2 is hash of attribute data
175 my $size = $3 - 19; # FIXME: find a nicer way to find out picture size
176 $uid = "[jpeg image of size $size]";
177 next if $uidstatus eq "r";
179 system "gpg --photo-viewer 'convert - %k.eps' --list-options show-photos --list-key $key > /dev/null";
181 my ($shortkey) = substr $key, -8;
182 push @
{$uids{$shortkey}}, $uid;
185 if ( m/^sig:(?:.*?:){3,3}([0-9A-F]{8})([0-9A-F]{8}):(?:.*?:){5,5}(.*?):/ ) {
187 if ($class eq '10x') {
189 } elsif ($class eq '11x') {
191 } elsif ($class eq '12x') {
193 } elsif ($class eq '13x') {
198 # Handle the case where one UID was signed multiple times
199 # with different signature classes.
200 my $before = $sigs->{$key}->{$uid}->{$1.$2};
201 if (defined $before) {
202 if ($before eq 'S' || $before eq 's') {
203 $sigs->{$key}->{$uid}->{$1.$2} = $class;
204 } elsif ($class eq 'S' || $class eq 's') {
205 # intentionally left blank
206 } elsif ($before < $class) {
207 $sigs->{$key}->{$uid}->{$1.$2} = $class;
210 $sigs->{$key}->{$uid}->{$1.$2} .= $class;
212 $sigs->{$key}->{$uid}->{$2} = $sigs->{$key}->{$uid}->{$1.$2};
215 next if ( m/^(rev|rvk|sub|tru):/ ); # revoke/revoker/subkey/trust
216 warn "unknown value: '$_', key: ".(defined $key ?
$key :'none')."\n";
221 for my $k ( keys %{$sigs} ) {
222 if ( $k =~ m/^[0-9A-F]{8}([0-9A-F]{8})$/ ) {
223 $sigs->{$1} = $sigs->{$k};
229 open MD
, "gpg --with-colons --print-md md5 $keytxt|" or warn "can't get gpg md5\n";
232 open MD
, "gpg --with-colons --print-md sha1 $keytxt|" or warn "can't get gpg sha1\n";
235 open MD
, "gpg --with-colons --print-md sha256 $keytxt|" or warn "can't get gpg sha256\n";
239 my @MD5 = split /:/, $MD5;
240 my @SHA1 = split /:/, $SHA1;
241 my @SHA256 = split /:/, $SHA256;
244 $SHA256 = $SHA256[2];
246 $MD5 =~ s/(.{16})/$1 /;
247 $SHA1 =~ s/(.{20})/$1 /;
248 $SHA256 =~ s/(.{32})/$1 /;
249 $MD5 =~ s/([0-9A-Z]{2})/$1 /ig;
250 $SHA1 =~ s/([0-9A-Z]{4})/$1 /ig;
251 $SHA256 =~ s/([0-9A-Z]{4})/$1 /ig;
256 my $metatxt = quotemeta($keytxt);
257 $MD5 =~ s/^$metatxt:\s*//;
258 $SHA1 =~ s/^$metatxt:\s*//;
259 $SHA256 =~ s/^$metatxt:\s*//;
265 my ($key, $uid) = @_;
266 if (! defined $sigs->{$key}->{$uid}) {
267 warn "uid '$uid' not found on key $key\n";
268 #for (keys %{ $sigs->{$key} }) {
269 # print STDERR "only have $_\n";
271 return '(' . (' ' x
@mykeys) . ')';
274 foreach my $mykey (@mykeys) {
275 $r .= defined $sigs->{$key}->{$uid}->{$mykey} ?
$sigs->{$key}->{$uid}->{$mykey} : ' ';
284 print STDERR
"Annotating $keytxt, writing into $outfile\n";
285 open (TXT
, $keytxt) or die ("Cannot open $keytxt\n");
286 open (WRITE
, '>'.$outfile) or die ("Cannot open $outfile for writing\n");
290 \documentclass{article}
291 \usepackage[margin=2cm]{geometry}
293 \usepackage{graphicx}
301 $_ = myrecode
($_, $fromcharset, $charset);
302 if (/^MD5 Checksum:/ && defined $MD5) {
303 s/[_[:xdigit:]][_ [:xdigit:]]+_/$MD5/;
305 if (/^SHA1 Checksum:/ && defined $SHA1) {
306 s/[_[:xdigit:]][_ [:xdigit:]]+_/$SHA1/;
308 if (/^SHA256 Checksum:/ && defined $SHA256) {
309 s/[_[:xdigit:]][_ [:xdigit:]]+_/$SHA256/;
311 if ( m/^pub +(?:\d+)[DR]\/([0-9A
-F
]{8}) [0-9]{4}-[0-9]{2}-[0-9]{2} *(.*)/ ) {
314 #if ($uid) { # in gpg 1.2, the first uid is here
315 # print WRITE print_tag($key, $uid) . " $_";
322 if ( m/^ *Key fingerprint/ ) {
325 foreach my $mykey (@mykeys) {
326 foreach my $myuid (@
{$uids{$mykey}}) {
327 $inc .= defined $sigs->{$mykey}->{$myuid}->{$key} ?
$sigs->{$mykey}->{$myuid}->{$key} : ' ';
330 print WRITE
"[$inc] incoming signatures\n" if $inc =~ /\S/;
331 if ($refresh or $latex) {
332 foreach $uid (@
{$uids{$key}}) {
333 print WRITE print_tag
($key, $uid) . " $uid\n";
334 if ($latex and ($uid =~ /^\[jpeg image/)) {
335 print WRITE
"\\begin{flushright}\n";
336 print WRITE
"\\includegraphics[height=3cm]{$key.eps}\n";
337 print WRITE
"\\end{flushright}\n";
344 if ( m/^uid +(.*)$/ ) {
346 next if $refresh or $latex;
347 unless (defined $key) {
348 warn "key is undefined - input text is possibly malformed near line $line\n";
351 die "bad tag from $key | $uid" unless defined (print_tag
($key, $uid));
352 print WRITE print_tag
($key, $uid) . " $_";
358 print WRITE
"Legend:\n";
360 foreach my $i (0 .. @mykeys - 1) {
361 print WRITE
'(' . ' 'x
$i . 'S' . ' 'x
(@mykeys-$i-1) . ") signed with $mykeys[$i] $uids{$mykeys[$i]}->[0]\n";
362 $num_myuids += @
{$uids{$mykeys[$i]}};
365 foreach my $mykey (@mykeys) {
366 foreach my $myuid (@
{$uids{$mykey}}) {
367 my $inc = defined $sigs->{$mykey}->{$myuid}->{$key} ?
$sigs->{$mykey}->{$myuid}->{$key} : ' ';
368 print WRITE
"[" . ' 'x
$i . 'S' . ' 'x
($num_myuids-$i-1) . "] has signed $mykey $myuid\n";
387 B<gpgsigs> - annotate list of GnuPG keys with already done signatures
391 B<gpgsigs> [-r] [-f I<charset>] [-t I<charset>] I<keyid>I<[>B<,>I<keyidI<[>B<,>I<...>I<]>>I<]> F<keytxt> [F<outfile>]
395 B<gpgsigs> was written to assist the user in signing keys during a keysigning
396 party. It takes as input a file containing keys in C<gpg --list-keys> format
397 and prepends every line with a tag indicating if the user has already signed
398 that uid. When the file contains C<MD5 Checksum:> or C<SHA1 Checksum:> lines
399 and placeholders (C<__ __>), the checksum is inserted.
407 Call I<gpg --recv-keys> before creating the output.
411 Convert F<keytxt> from I<charset>. The default is ISO-8859-1.
415 Convert UIDs to I<charset>. The default is derived from LC_ALL, LC_CTYPE, and
416 LANG, and if all these are unset, the default is ISO-8859-1.
420 Use this keyid (8 or 16 byte) for annotation. Multiple keyids can be separated
425 Read input from F<keytxt>.
429 Write output to F<outfile>. Default is stdout.
435 The following key signing parties are using B<gpgsigs>:
437 http://www.palfrader.org/ksp-lt2k4.html
439 http://www.palfrader.org/ksp-lt2k5.html
443 B<GnuPG> is known to change its output format quite often. This version has
444 been tested with gpg 1.2.5 and gpg 1.4.1. YMMV.
450 http://pgp-tools.alioth.debian.org/
452 =head1 AUTHORS AND COPYRIGHT
454 (c) 2004 Uli Martens <uli@youam.net>
456 (c) 2004, 2005 Peter Palfrader <peter@palfrader.org>
458 (c) 2004, 2005, 2006, 2007 Christoph Berg <cb@df7cb.de>
464 Redistribution and use in source and binary forms, with or without
465 modification, are permitted provided that the following conditions
468 1. Redistributions of source code must retain the above copyright
469 notice, this list of conditions and the following disclaimer.
471 2. Redistributions in binary form must reproduce the above copyright
472 notice, this list of conditions and the following disclaimer in the
473 documentation and/or other materials provided with the distribution.
475 3. The name of the author may not be used to endorse or promote products
476 derived from this software without specific prior written permission.
478 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
479 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
480 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
481 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
482 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
483 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
484 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
485 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
486 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
487 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.