* Import keyanalyze into signing-party. Thanks to Matthew Wilcox for the
[pgp-tools.git] / gpg-key2ps / gpg-key2ps
index 789d6d057395fd6fcbb867184432f941f0d5ecb5..da3231c7dd8f224ac84629af26de5a3250f94018 100755 (executable)
-#! /bin/sh
-
-set -e
-
-VERSION="0.3.1"
-USAGE="Usage: $0 [-p papersize] [-r revoked-style] keyid-or-name"
-
-
-if [ -z "$*" ]; then
-       echo $USAGE
-       exit 1
-fi
-
-KEYIDS=
-REVOKESTYLE=hide
-
-while [ -n "$1" ]; do
-       case "$1" in
-       -p)
-               PAPERSIZE=$2
-               export PAPERSIZE
-               shift 2
-               ;;
-       -p*)
-               PAPERSIZE=`echo $1 | sed -e 's/^-p//'`
-               export PAPERSIZE
-               shift 1
-               ;;
-       -r)
-               REVOKESTYLE=$2
-               shift 2
-               ;;
-       -r*)
-               REVOKESTYLE=`echo $1 | sed -e 's/^-r//'`
-               shift 1
-               ;;
-       -*)
-               echo $USAGE
-               exit 1
-               ;;
-       *)
-               KEYIDS=$1
-               shift 1
-               ;;
-       esac
-done
-
-case "$REVOKESTYLE" in
-grey|hide|note|show|strike)
-       ;;
-*)
-       echo >&2 "Unknown style \"$REVOKESTYLE\". Please use one of"
-       echo >&2 "  grey   - Print text in grey"
-       echo >&2 "  hide   - Don't show revoked uids"
-       echo >&2 "  note   - Add \"(revoked)\""
-       echo >&2 "  show   - List revoked uids normally"
-       echo >&2 "  strike - Strike through lines"
-       exit 1
-       ;;
-esac
-
-TMPFILE=`mktemp -t gpg-key2ps.XXXXXX`
-
-if ! gpg --fingerprint --with-colons $KEYIDS 2>/dev/null >$TMPFILE; then
-       echo >&2 "Key not found. Try 'gpg --list-keys'"
-       rm $TMPFILE
-       exit 1
-fi
-
-NUMLINES=$((`wc -l <$TMPFILE` + `grep '^pub:' $TMPFILE | wc -l` - 1))
-
-if test -x /usr/bin/paperconf; then
-       w=`paperconf -w`
-       h=`paperconf -h`
-else
+#!/usr/bin/perl -w
+#
+# gpg-key2ps: convert a PGP/GnuPG key into paper slips.
+# Copyright (C) 2001-2005  Simon Richter
+# Copyright (C) 2005-2007  Thijs Kinkhorst
+# Copyright (C) 2005-2008  Christoph Berg <cb@df7cb.de>
+# Licenced under the GNU General Public License,
+# version 2 or later.
+#
+# $Id$
+
+use strict;
+use Getopt::Long;
+
+my $version = '$Rev$';
+$version =~ s/\$Rev:\s*(\d+)\s*\$/$1/;
+my $revokestyle = "hide";
+my $columns = 2;
+my $creationdate = scalar(localtime);
+
+sub version($) {
+       my $fd = shift;
+       print $fd "gpg-key2ps $version - (c) 2001-2008 Simon Richter, Thijs Kinkhorst, Christoph Berg\n";
+}
+
+sub usage($$) {
+       my ($fd, $exitcode) = @_;
+       version ($fd);
+       print $fd <<EOF;
+Usage: $0 [-p papersize] [-r revoked-style] [-1] keyid-or-name ...
+Options:
+ -p --paper-size
+ -r --revoked-style
+        hide   - Don't show revoked uids and subkeys (default)
+        grey   - Print text in grey
+        note   - Add "[revoked]"
+        show   - List revoked uids normally
+        strike - Strike through lines
+ -1 Only print one column, for extra wide keys
+ -h --help
+ -v --version
+EOF
+       exit $exitcode;
+}
+
+# fetch command line parameters
+my $opts;
+Getopt::Long::config('bundling');
+if (!GetOptions (
+       '-h'                => \$opts->{help},
+       '--help'            => \$opts->{help},
+       '-v'                => \$opts->{version},
+       '--version'         => \$opts->{version},
+       '-p=s'              => \$opts->{papersize},
+       '--paper-size=s'    => \$opts->{papersize},
+       '-r=s'              => \$opts->{revokestyle},
+       '--revoked-style=s' => \$opts->{revokestyle},
+       '-1'                => \$opts->{1},
+)) {
+       usage(\*STDERR, 1);
+}
+
+if ($opts->{help}) {
+       usage (\*STDOUT, 0);
+}
+
+if ($opts->{version}) {
+       version (\*STDOUT);
+       exit 0;
+}
+
+if ( $opts->{revokestyle} ) { $revokestyle = $opts->{revokestyle}; }
+if ( $opts->{papersize} ) { $ENV{'PAPERSIZE'} = $opts->{papersize}; }
+
+if ( $revokestyle !~ /^(grey|hide|note|show|strike)$/ ) {
+       print STDERR "Unknown revoked-style \"$revokestyle\".\n";
+       usage (\*STDERR, 1);
+}
+
+if ( $opts->{1} ) { $columns = 1; }
+
+usage(\*STDERR, 1) unless scalar @ARGV >= 1;
+
+# determine the paper size through the paperconf tool
+my $w; my $h;
+if ( `which paperconf` && $? == 0 ) {
+       $w=`paperconf -w`;
+       $h=`paperconf -h`;
+       chomp($w);
+       chomp($h);
+} else {
        # Default to A4.
-       w=596
-       h=842
-fi
-
-cat <<EOF
+       print STDERR "Warning: libpaper-utils is not installed, defaulting to A4.\n";
+       $w=596;
+       $h=842;
+}
+
+# open a gpg process we'll be reading from below
+map { s/'/'\\''/g; } @ARGV; # quote single quotes
+# --list-key due to #382794
+open(GPG, "gpg --list-key --with-fingerprint --with-colons '". (join "' '", @ARGV) ."' |");
+
+sub start_postscript {
+# start the PostScript output
+print <<EOF;
 %!PS-Adobe-3.0
 %%BoundingBox: 0 0 $w $h
 %%Title: 
-%%Creator: gpg-key2ps $VERSION
-EOF
-echo "%%CreationDate: `date`"
-cat <<EOF
+%%Creator: gpg-key2ps $version
+%%CreationDate: $creationdate
 %%Pages: 1
 %%EndComments
 
@@ -102,7 +124,7 @@ cat <<EOF
 
 /hline {
        30 y 3 add moveto
-       w 2 div 30 sub y 3 add lineto stroke
+       w $columns div 30 sub y 3 add lineto stroke
        newline
 } def
 
@@ -114,10 +136,15 @@ cat <<EOF
        /condhline { } def
 } def
 
+/showAlgorithm {
+  << 1 (R) 2 (r) 3 (s) 16 (g) 20 (G) 17 (D) >> exch get
+  show
+} def
+
 /pub {
        condhline
        50 y moveto (pub) show
-       70 y moveto show show (/) show show
+       70 y moveto show showAlgorithm (/) show show
        150 y moveto show
        200 y moveto show
        newline
@@ -135,80 +162,131 @@ cat <<EOF
        newline
 } def
 
-EOF
-
-case "$REVOKESTYLE" in
-grey)
-       echo "/revuid {"
-       echo "  .5 setgray"
-       echo "  uid"
-       echo "  0 setgray"
-       echo "} def"
-       ;;
-hide)
-       echo "/revuid {} def"
-       ;;
-note)
-       echo "/revuid {"
-       echo "  50 y moveto (uid) show"
-       echo "  200 y moveto show ([revoked]) show"
-       echo "  newline"
-       echo "} def"
-       ;;
-show)  
-       echo "/revuid { uid } def"
-       ;;
-strike)
-       echo "/revuid {"
-       echo "  uid"
-       echo "  45 y 9 add moveto h 2 div 45 sub y 18 add lineto stroke"
-       echo "} def"
-       ;;
-esac
-
-cat <<EOF
-
 /sbk {
        50 y moveto (sub) show
-       70 y moveto show show (/) show show
+       70 y moveto show showAlgorithm (/) show show
        150 y moveto show
        newline
 } def
 
+EOF
+
+# output the desired display for revoked uids
+if ( $revokestyle eq "grey" ) {
+       print <<EOF;
+/revuid {
+       .5 setgray
+       uid
+       0 setgray
+} def
+/revsbk {
+       .5 setgray
+       sbk
+       0 setgray
+} def
+EOF
+} elsif ( $revokestyle eq "note" ) {
+       print <<EOF;
+/revuid {
+       50 y moveto (uid) show
+       200 y moveto show ( [revoked]) show
+       newline
+} def
+/revsbk {
+       sbk
+       ( [revoked]) show
+} def
+EOF
+} elsif ( $revokestyle eq "show" ) {
+       print <<EOF;
+/revuid { uid } def
+/revsbk { sbk } def
+EOF
+} elsif ( $revokestyle eq "strike" ) {
+       print <<EOF;
+/revuid {
+       uid
+       45 y 9 add moveto h 2 div 45 sub y 18 add lineto stroke
+} def
+/revsbk {
+       sbk
+       45 y 9 add moveto h 2 div 45 sub y 18 add lineto stroke
+} def
+EOF
+}
+
+print <<EOF;
+
 /key {
        noneedhline
 EOF
-sed -e '/^tru:/d' \
-    -e '/^uat:/d' \
-    -e 's/^pub:[^:]*:\([^:]*\):1:.\{8,8\}\(.\{8,8\}\):\([^:]*\):[^:]*:[^:]*:[^:]*:\([^:]*\):[^:]*:[^:]*:.*/    (\4) (\3) (\2) (R) (\1) pub/' \
-    -e 's/^pub:[^:]*:\([^:]*\):17:.\{8,8\}\(.\{8,8\}\):\([^:]*\):[^:]*:[^:]*:[^:]*:\([^:]*\):[^:]*:[^:]*:.*/   (\4) (\3) (\2) (D) (\1) pub/' \
-    -e 's/^fpr:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:\([^:]*\):.*/   (\1) fpr/' \
-    -e 's/(\(.\{16,16\}\)\(.\{16,16\}\)) fpr/(@split@\1@  @split@\2@) fpr/' \
-    -e 's/@split@\(.\{2,2\}\)\(.\{2,2\}\)\(.\{2,2\}\)\(.\{2,2\}\)\(.\{2,2\}\)\(.\{2,2\}\)\(.\{2,2\}\)\(.\{2,2\}\)@/\1 \2 \3 \4 \5 \6 \7 \8/g' \
-    -e 's/(\(.\{20,20\}\)\(.\{20,20\}\)) fpr/(@split@\1@  @split@\2@) fpr/' \
-    -e 's/@split@\(.\{4,4\}\)\(.\{4,4\}\)\(.\{4,4\}\)\(.\{4,4\}\)\(.\{4,4\}\)@/\1 \2 \3 \4 \5/g' \
-    -e 's/^uid:[^:r]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:\([^:]*\):.*/  (\1) uid/' \
-    -e 's/^uid:r[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:\([^:]*\):.*/  (\1) revuid/' \
-    -e 's/^sub:[^:]*:\([^:]*\):16:.\{8,8\}\(.\{8,8\}\):\([^:]*\):[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:.*/       (\3) (\2) (g) (\1) sbk/' \
-    <$TMPFILE
-cat <<EOF
+} # sub start_postscript
+
+# walk the output of gpg line by line
+# $numlines has the total number of lines so we'll know how many to put on page
+my $numlines = 0;
+my $started = 0;
+while(<GPG>) {
+       # we don't use these
+       if ( /^(tru|uat):/ ) { next; }
+       # every primary uid causes an extra line because of the separator
+       if ( /^pub:/ ) {
+               start_postscript() unless $started;
+               $started = 1;
+               $numlines++;
+       }
+       # primary uid
+       s/^pub:[^:]*:([^:]*):([0-9]*):.{8,8}(.{8,8}):([^:]*):[^:]*:[^:]*:[^:]*:([^:]*):[^:]*:[^:]*:.*/  ($5) ($4) ($3) $2 ($1) pub/;
+       # fingerprint, format it nicely with spaces
+       if ( /^fpr:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:([^:]*):.*/ ) {
+               my $fpr = $1;
+               # v4 key
+               $fpr =~ s/(\w{4})(\w{4})(\w{4})(\w{4})(\w{4})(\w{4})(\w{4})(\w{4})(\w{4})(\w{4})/$1 $2 $3 $4 $5  $6 $7 $8 $9 $10/;
+               # v3 key
+               $fpr =~ s/(\w{2})(\w{2})(\w{2})(\w{2})(\w{2})(\w{2})(\w{2})(\w{2})(\w{2})(\w{2})(\w{2})(\w{2})(\w{2})(\w{2})(\w{2})(\w{2})/$1 $2 $3 $4 $5 $6 $7 $8  $9 $10 $11 $12 $13 $14 $15 $16/g;
+               $_ = "  ($fpr) fpr\n";
+       }
+       # user ids
+       s/\\x([0-9a-f][0-9a-f])/ chr(hex($1)) /gie;
+       $_ = `echo "$_" | iconv -ct latin1`;
+       s/^uid:[^:r]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:([^:]*):.*/     ($1) uid/;
+       # revoked user id
+       if (s/^uid:r[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:([^:]*):.*/ ($1) revuid/) {
+               next if $revokestyle eq "hide";
+       }
+       # subkey
+       s/^sub:[^r:]*:([^:]*):([0-9]*):.{8,8}(.{8,8}):([^:]*):[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:.*/   ($4) ($3) $2 ($1) sbk/;
+       if (s/^sub:r[^:]*:([^:]*):([0-9]*):.{8,8}(.{8,8}):([^:]*):[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:.*/       ($4) ($3) $2 ($1) revsbk/) {
+               next if $revokestyle eq "hide";
+       }
+       $numlines++;
+       # print this line
+       print;
+}
+close(GPG);
+
+unless ($started) {
+       print STDERR "No public key found.\n";
+       exit 1;
+}
+
+# output the remaining postscript
+print <<EOF;
 } def
 
-EOF
-echo "/numlines $(($NUMLINES + 1)) def"
-cat <<EOF
+/numlines $numlines def
 /num w 16 sub 10 div numlines div def
 
 /column {
        /y w 20 sub def
        1 1 num {
                gsave
-               0 0 h 2 div w rectclip
+               0 0 h $columns div w rectclip
                /upper y 11 add def
                key
                newline
                /lower y 11 add def
-               0 upper h 2 div upper h 2 div lower 0 lower 0 upper moveto lineto lineto lineto lineto stroke
+               0 upper h $columns div upper h $columns div lower 0 lower 0 upper moveto lineto lineto lineto lineto stroke
                grestore
        } for
 } def
@@ -216,13 +294,104 @@ cat <<EOF
 w 0 translate
 90 rotate
 column
-h 2 div 0 translate
+EOF
+
+if ( $columns == 2 ) {
+       print <<EOF;
+h $columns div 0 translate
 column
 
+EOF
+}
+
+print <<EOF;
 showpage
 
 %%Trailer
 %%EOF
 EOF
 
-rm $TMPFILE
+# done!
+exit 0;
+
+
+__END__
+
+=head1 NAME
+
+B<gpg-key2ps> - generates a PS file from a GnuPG keyring
+
+=head1 SYNOPSIS
+
+B<gpg-key2ps> [B<-r> I<revoked-style>] [B<-p> I<papersize>] I<keyid-or-name> [ I<...> ]
+
+=head1 DESCRIPTION
+
+gpg-key2ps generates a PostScript file with your OpenPGP key fingerprint (repeated
+as often as it fits) useful for keysigning parties. The only argument is the same
+as you would pass to GPG's list-keys command, either a key-id or a (partial) name.
+The PS data is written to stdout.
+
+=head1 OPTIONS
+
+=over
+
+=item B<-p> B<--paper-size> I<paper-size>
+
+Select the output paper size. Default is to look into /etc/papersize or A4 if
+libpaper isn't installed.
+
+=item B<-r> B<--revoked-style> I<revoked-style>
+
+Select how to mark revoked UIDs and subkeys. Five styles are available:
+ B<hide> don't show at all (default),
+ B<show> show normally,
+ B<grey> display in 50% grey,
+ B<note> add "[revoked]", and
+ B<strike> strike through.
+
+=item I<keyid>
+
+Keyids to print. Multiple can be separated by spaces.
+
+=item B<-h> B<--help>
+
+Print usage and exit.
+
+=item B<-v> B<--version>
+
+Print version and exit.
+
+=back
+
+=head1 SEE ALSO
+
+=over
+
+=item gpg(1)
+
+GNU Privacy Guard.
+
+=item http://pgp-tools.alioth.debian.org/
+
+The homepage of B<gpg-key2ps> and the other tools bundled in B<signing-party>.
+
+=item http://www.debian.org/events/materials/business-cards/
+
+B<gpg-key2ps> prints plain fingerprint slips. If you are looking for something
+more stylish, look at these latex templates for business cards that also
+include fingerprints.
+
+=back
+
+=head1 AUTHORS AND COPYRIGHT
+
+=over
+
+=item (c) 2001-2005 Simon Richter <sjr@debian.org>
+
+=item (c) 2005-2007 Thijs Kinkhorst <thijs@kinkhorst.com>
+
+=item (c) 2005-2008 Christoph Berg <cb@df7cb.de>
+
+=back