We don't need subkey info on a fingerprint slip, so don't include it by
[pgp-tools.git] / gpg-key2ps / gpg-key2ps
1 #!/usr/bin/perl -w
2 #
3 # gpg-key2ps: convert a PGP/GnuPG key into paper slips.
4 # Copyright (C) 2001-2005 Simon Richter
5 # Copyright (C) 2005-2008 Thijs Kinkhorst
6 # Copyright (C) 2005-2008 Christoph Berg <cb@df7cb.de>
7 # Licenced under the GNU General Public License,
8 # version 2 or later.
9 #
10 # $Id$
11
12 use strict;
13 use Getopt::Long;
14
15 my $version = '$Rev$';
16 $version =~ s/\$Rev:\s*(\d+)\s*\$/$1/;
17 my $showsubkeys = 0;
18 my $revokestyle = "hide";
19 my $columns = 2;
20 my $creationdate = scalar(localtime);
21
22 sub version($) {
23 my $fd = shift;
24 print $fd "gpg-key2ps $version - (c) 2001-2008 Simon Richter, Thijs Kinkhorst, Christoph Berg\n";
25 }
26
27 sub usage($$) {
28 my ($fd, $exitcode) = @_;
29 version ($fd);
30 print $fd <<EOF;
31 Usage: $0 [-p papersize] [-r revoked-style] [-1] [-s] keyid-or-name ...
32 Options:
33 -p --paper-size
34 -s --show-subkeys
35 -r --revoked-style
36 hide - Don't show revoked uids and subkeys (default)
37 grey - Print text in grey
38 note - Add "[revoked]"
39 show - List revoked uids normally
40 strike - Strike through lines
41 -1 Only print one column, for extra wide keys
42 -h --help
43 -v --version
44 EOF
45 exit $exitcode;
46 }
47
48 # fetch command line parameters
49 my $opts;
50 Getopt::Long::config('bundling');
51 if (!GetOptions (
52 '-h' => \$opts->{help},
53 '--help' => \$opts->{help},
54 '-v' => \$opts->{version},
55 '--version' => \$opts->{version},
56 '-s' => \$opts->{showsubkeys},
57 '--show-subkeys' => \$opts->{showsubkeys},
58 '-p=s' => \$opts->{papersize},
59 '--paper-size=s' => \$opts->{papersize},
60 '-r=s' => \$opts->{revokestyle},
61 '--revoked-style=s' => \$opts->{revokestyle},
62 '-1' => \$opts->{1},
63 )) {
64 usage(\*STDERR, 1);
65 }
66
67 if ($opts->{help}) {
68 usage (\*STDOUT, 0);
69 }
70
71 if ($opts->{version}) {
72 version (\*STDOUT);
73 exit 0;
74 }
75
76 if ( $opts->{papersize} ) { $ENV{'PAPERSIZE'} = $opts->{papersize}; }
77 if ( $opts->{showsubkeys} ) { $showsubkeys = 1; }
78 if ( $opts->{revokestyle} ) { $revokestyle = $opts->{revokestyle}; }
79
80 if ( $revokestyle !~ /^(grey|hide|note|show|strike)$/ ) {
81 print STDERR "Unknown revoked-style \"$revokestyle\".\n";
82 usage (\*STDERR, 1);
83 }
84
85 if ( $opts->{1} ) { $columns = 1; }
86
87 usage(\*STDERR, 1) unless scalar @ARGV >= 1;
88
89 # determine the paper size through the paperconf tool
90 my $w; my $h;
91 if ( `which paperconf` && $? == 0 ) {
92 $w=`paperconf -w`;
93 $h=`paperconf -h`;
94 chomp($w);
95 chomp($h);
96 } else {
97 # Default to A4.
98 print STDERR "Warning: libpaper-utils is not installed, defaulting to A4.\n";
99 $w=596;
100 $h=842;
101 }
102
103 # open a gpg process we'll be reading from below
104 map { s/'/'\\''/g; } @ARGV; # quote single quotes
105 # --list-key due to #382794
106 open(GPG, "gpg --list-key --with-fingerprint --with-colons '". (join "' '", @ARGV) ."' |");
107
108 sub start_postscript {
109 # start the PostScript output
110 print <<EOF;
111 %!PS-Adobe-3.0
112 %%BoundingBox: 0 0 $w $h
113 %%Title:
114 %%Creator: gpg-key2ps $version
115 %%CreationDate: $creationdate
116 %%Pages: 1
117 %%EndComments
118
119 %%Page: 1 1
120
121 /w $w def
122 /h $h def
123
124 /Times-Roman findfont 9 scalefont setfont
125
126 /newline {
127 /y y 10 sub def
128 } def
129
130 /hline {
131 30 y 3 add moveto
132 w $columns div 30 sub y 3 add lineto stroke
133 newline
134 } def
135
136 /needhline {
137 /condhline { hline } def
138 } def
139
140 /noneedhline {
141 /condhline { } def
142 } def
143
144 /showAlgorithm {
145 << 1 (R) 2 (r) 3 (s) 16 (g) 20 (G) 17 (D) >> exch get
146 show
147 } def
148
149 /pub {
150 condhline
151 50 y moveto (pub) show
152 70 y moveto show showAlgorithm (/) show show
153 150 y moveto show
154 200 y moveto show
155 newline
156 needhline
157 } def
158
159 /fpr {
160 70 y moveto (Key fingerprint = ) show show
161 newline
162 } def
163
164 /uid {
165 50 y moveto (uid) show
166 200 y moveto show
167 newline
168 } def
169
170 /sbk {
171 50 y moveto (sub) show
172 70 y moveto show showAlgorithm (/) show show
173 150 y moveto show
174 newline
175 } def
176
177 EOF
178
179 # output the desired display for revoked uids
180 if ( $revokestyle eq "grey" ) {
181 print <<EOF;
182 /revuid {
183 .5 setgray
184 uid
185 0 setgray
186 } def
187 /revsbk {
188 .5 setgray
189 sbk
190 0 setgray
191 } def
192 EOF
193 } elsif ( $revokestyle eq "note" ) {
194 print <<EOF;
195 /revuid {
196 50 y moveto (uid) show
197 200 y moveto show ( [revoked]) show
198 newline
199 } def
200 /revsbk {
201 sbk
202 ( [revoked]) show
203 } def
204 EOF
205 } elsif ( $revokestyle eq "show" ) {
206 print <<EOF;
207 /revuid { uid } def
208 /revsbk { sbk } def
209 EOF
210 } elsif ( $revokestyle eq "strike" ) {
211 print <<EOF;
212 /revuid {
213 uid
214 45 y 9 add moveto h 2 div 45 sub y 18 add lineto stroke
215 } def
216 /revsbk {
217 sbk
218 45 y 9 add moveto h 2 div 45 sub y 18 add lineto stroke
219 } def
220 EOF
221 }
222
223 print <<EOF;
224
225 /key {
226 noneedhline
227 EOF
228 } # sub start_postscript
229
230 # walk the output of gpg line by line
231 # $numlines has the total number of lines so we'll know how many to put on page
232 my $numlines = 0;
233 my $started = 0;
234 while(<GPG>) {
235 # we don't use these
236 if ( /^(tru|uat):/ ) { next; }
237 # every primary uid causes an extra line because of the separator
238 if ( /^pub:/ ) {
239 start_postscript() unless $started;
240 $started = 1;
241 $numlines++;
242 }
243 # primary uid
244 s/^pub:[^:]*:([^:]*):([0-9]*):.{8,8}(.{8,8}):([^:]*):[^:]*:[^:]*:[^:]*:([^:]*):[^:]*:[^:]*:.*/ ($5) ($4) ($3) $2 ($1) pub/;
245 # fingerprint, format it nicely with spaces
246 if ( /^fpr:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:([^:]*):.*/ ) {
247 my $fpr = $1;
248 # v4 key
249 $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/;
250 # v3 key
251 $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;
252 $_ = " ($fpr) fpr\n";
253 }
254 # user ids
255 s/\\x([0-9a-f][0-9a-f])/ chr(hex($1)) /gie;
256 $_ = `echo "$_" | iconv -ct latin1`;
257 s/^uid:[^:r]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:([^:]*):.*/ ($1) uid/;
258 # revoked user id
259 if (s/^uid:r[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:([^:]*):.*/ ($1) revuid/) {
260 next if $revokestyle eq "hide";
261 }
262 # subkey
263 if (s/^sub:[^r:]*:([^:]*):([0-9]*):.{8,8}(.{8,8}):([^:]*):[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:.*/ ($4) ($3) $2 ($1) sbk/) {
264 next if ! $showsubkeys;
265 }
266 if (s/^sub:r[^:]*:([^:]*):([0-9]*):.{8,8}(.{8,8}):([^:]*):[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:.*/ ($4) ($3) $2 ($1) revsbk/) {
267 next if (!$showsubkeys) || $revokestyle eq "hide";
268 }
269 $numlines++;
270 # print this line
271 print;
272 }
273 close(GPG);
274
275 unless ($started) {
276 print STDERR "No public key found.\n";
277 exit 1;
278 }
279
280 # output the remaining postscript
281 print <<EOF;
282 } def
283
284 /numlines $numlines def
285 /num w 16 sub 10 div numlines div def
286
287 /column {
288 /y w 20 sub def
289 1 1 num {
290 gsave
291 0 0 h $columns div w rectclip
292 /upper y 11 add def
293 key
294 newline
295 /lower y 11 add def
296 0 upper h $columns div upper h $columns div lower 0 lower 0 upper moveto lineto lineto lineto lineto stroke
297 grestore
298 } for
299 } def
300
301 w 0 translate
302 90 rotate
303 column
304 EOF
305
306 if ( $columns == 2 ) {
307 print <<EOF;
308 h $columns div 0 translate
309 column
310
311 EOF
312 }
313
314 print <<EOF;
315 showpage
316
317 %%Trailer
318 %%EOF
319 EOF
320
321 # done!
322 exit 0;
323
324
325 __END__
326
327 =head1 NAME
328
329 B<gpg-key2ps> - generates a PS file from a GnuPG keyring
330
331 =head1 SYNOPSIS
332
333 B<gpg-key2ps> [B<-r> I<revoked-style>] [B<-p> I<papersize>] [B<-1>] [B<-s>] I<keyid-or-name> [ I<...> ]
334
335 =head1 DESCRIPTION
336
337 gpg-key2ps generates a PostScript file with your OpenPGP key fingerprint (repeated
338 as often as it fits) useful for keysigning parties. The only argument is the same
339 as you would pass to GPG's list-keys command, either a key-id or a (partial) name.
340 The PS data is written to stdout.
341
342 =head1 OPTIONS
343
344 =over
345
346 =item B<-p> B<--paper-size> I<paper-size>
347
348 Select the output paper size. Default is to look into /etc/papersize or A4 if
349 libpaper isn't installed.
350
351 =item B<-s> B<--show-subkeys>
352
353 Enable subkey information to be printed on the slips. Subkey information is
354 normally not relevant to the key signing process.
355
356 =item B<-r> B<--revoked-style> I<revoked-style>
357
358 Select how to mark revoked UIDs and subkeys. Five styles are available:
359 B<hide> don't show at all (default),
360 B<show> show normally,
361 B<grey> display in 50% grey,
362 B<note> add "[revoked]", and
363 B<strike> strike through.
364
365 =item B<-1>
366
367 Print only one column of paper slips instead of the default two. Useful for keys
368 with long UIDs that otherwise won't fit.
369
370 =item I<keyid>
371
372 Keyids to print. Multiple can be separated by spaces.
373
374 =item B<-h> B<--help>
375
376 Print usage and exit.
377
378 =item B<-v> B<--version>
379
380 Print version and exit.
381
382 =back
383
384 =head1 SEE ALSO
385
386 =over
387
388 =item gpg(1)
389
390 GNU Privacy Guard.
391
392 =item http://pgp-tools.alioth.debian.org/
393
394 The homepage of B<gpg-key2ps> and the other tools bundled in B<signing-party>.
395
396 =item http://www.debian.org/events/materials/business-cards/
397
398 B<gpg-key2ps> prints plain fingerprint slips. If you are looking for something
399 more stylish, look at these latex templates for business cards that also
400 include fingerprints.
401
402 =back
403
404 =head1 AUTHORS AND COPYRIGHT
405
406 =over
407
408 =item (c) 2001-2005 Simon Richter <sjr@debian.org>
409
410 =item (c) 2005-2008 Thijs Kinkhorst <thijs@kinkhorst.com>
411
412 =item (c) 2005-2008 Christoph Berg <cb@df7cb.de>
413
414 =back