Add a changelog
[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 /reencode {
125 exch findfont
126 dup length dict
127 begin {
128 1 index /FID eq
129 { pop pop }
130 { def } ifelse
131 } forall
132 /Encoding ISOLatin1Encoding def
133 currentdict
134 end
135 definefont
136 } bind def
137
138 /Times-Roman /trlatin1 reencode 9 scalefont setfont
139
140 /newline {
141 /y y 10 sub def
142 } def
143
144 /hline {
145 30 y 3 add moveto
146 w $columns div 30 sub y 3 add lineto stroke
147 newline
148 } def
149
150 /needhline {
151 /condhline { hline } def
152 } def
153
154 /noneedhline {
155 /condhline { } def
156 } def
157
158 /showAlgorithm {
159 << 1 (R) 2 (r) 3 (s) 16 (g) 20 (G) 17 (D) >> exch get
160 show
161 } def
162
163 /pub {
164 condhline
165 50 y moveto (pub) show
166 70 y moveto show showAlgorithm (/) show show
167 150 y moveto show
168 200 y moveto show
169 newline
170 needhline
171 } def
172
173 /fpr {
174 70 y moveto (Key fingerprint = ) show show
175 newline
176 } def
177
178 /uid {
179 50 y moveto (uid) show
180 200 y moveto show
181 newline
182 } def
183
184 /sbk {
185 50 y moveto (sub) show
186 70 y moveto show showAlgorithm (/) show show
187 150 y moveto show
188 newline
189 } def
190
191 EOF
192
193 # output the desired display for revoked uids
194 if ( $revokestyle eq "grey" ) {
195 print <<EOF;
196 /revuid {
197 .5 setgray
198 uid
199 0 setgray
200 } def
201 /revsbk {
202 .5 setgray
203 sbk
204 0 setgray
205 } def
206 EOF
207 } elsif ( $revokestyle eq "note" ) {
208 print <<EOF;
209 /revuid {
210 50 y moveto (uid) show
211 200 y moveto show ( [revoked]) show
212 newline
213 } def
214 /revsbk {
215 sbk
216 ( [revoked]) show
217 } def
218 EOF
219 } elsif ( $revokestyle eq "show" ) {
220 print <<EOF;
221 /revuid { uid } def
222 /revsbk { sbk } def
223 EOF
224 } elsif ( $revokestyle eq "strike" ) {
225 print <<EOF;
226 /revuid {
227 uid
228 45 y 9 add moveto h 2 div 45 sub y 18 add lineto stroke
229 } def
230 /revsbk {
231 sbk
232 45 y 9 add moveto h 2 div 45 sub y 18 add lineto stroke
233 } def
234 EOF
235 }
236
237 print <<EOF;
238
239 /key {
240 noneedhline
241 EOF
242 } # sub start_postscript
243
244 # walk the output of gpg line by line
245 # $numlines has the total number of lines so we'll know how many to put on page
246 my $numlines = 0;
247 my $started = 0;
248 while(<GPG>) {
249 # we don't use these
250 if ( /^(tru|uat):/ ) { next; }
251 # every primary uid causes an extra line because of the separator
252 if ( /^pub:/ ) {
253 start_postscript() unless $started;
254 $started = 1;
255 $numlines++;
256 }
257 # primary uid
258 s/^pub:[^:]*:([^:]*):([0-9]*):.{8,8}(.{8,8}):([^:]*):[^:]*:[^:]*:[^:]*:([^:]*):[^:]*:[^:]*:.*/ ($5) ($4) ($3) $2 ($1) pub/;
259 # fingerprint, format it nicely with spaces
260 if ( /^fpr:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:([^:]*):.*/ ) {
261 my $fpr = $1;
262 # v4 key
263 $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/;
264 # v3 key
265 $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;
266 $_ = " ($fpr) fpr\n";
267 }
268 # user ids
269 s/\\x([0-9a-f][0-9a-f])/ chr(hex($1)) /gie;
270 $_ = `echo "$_" | iconv -c -f utf-8 -t latin1`;
271 s/^uid:[^:r]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:([^:]*):.*/ ($1) uid/;
272 # revoked user id
273 if (s/^uid:r[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:([^:]*):.*/ ($1) revuid/) {
274 next if $revokestyle eq "hide";
275 }
276 # subkey
277 if (s/^sub:[^r:]*:([^:]*):([0-9]*):.{8,8}(.{8,8}):([^:]*):[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:.*/ ($4) ($3) $2 ($1) sbk/) {
278 next if ! $showsubkeys;
279 }
280 if (s/^sub:r[^:]*:([^:]*):([0-9]*):.{8,8}(.{8,8}):([^:]*):[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:.*/ ($4) ($3) $2 ($1) revsbk/) {
281 next if (!$showsubkeys) || $revokestyle eq "hide";
282 }
283 $numlines++;
284 # print this line
285 print;
286 }
287 close(GPG);
288
289 unless ($started) {
290 print STDERR "No public key found.\n";
291 exit 1;
292 }
293
294 # output the remaining postscript
295 print <<EOF;
296 } def
297
298 /numlines $numlines def
299 /num w 16 sub 10 div numlines div def
300
301 /column {
302 /y w 20 sub def
303 1 1 num {
304 gsave
305 0 0 h $columns div w rectclip
306 /upper y 11 add def
307 key
308 newline
309 /lower y 11 add def
310 0 upper h $columns div upper h $columns div lower 0 lower 0 upper moveto lineto lineto lineto lineto stroke
311 grestore
312 } for
313 } def
314
315 w 0 translate
316 90 rotate
317 column
318 EOF
319
320 if ( $columns == 2 ) {
321 print <<EOF;
322 h $columns div 0 translate
323 column
324
325 EOF
326 }
327
328 print <<EOF;
329 showpage
330
331 %%Trailer
332 %%EOF
333 EOF
334
335 # done!
336 exit 0;
337
338
339 __END__
340
341 =head1 NAME
342
343 B<gpg-key2ps> - generates a PS file from a GnuPG keyring
344
345 =head1 SYNOPSIS
346
347 B<gpg-key2ps> [B<-r> I<revoked-style>] [B<-p> I<papersize>] [B<-1>] [B<-s>] I<keyid-or-name> [ I<...> ]
348
349 =head1 DESCRIPTION
350
351 gpg-key2ps generates a PostScript file with your OpenPGP key fingerprint (repeated
352 as often as it fits) useful for keysigning parties. The only argument is the same
353 as you would pass to GPG's list-keys command, either a key-id or a (partial) name.
354 The PS data is written to stdout.
355
356 =head1 OPTIONS
357
358 =over
359
360 =item B<-p> B<--paper-size> I<paper-size>
361
362 Select the output paper size. Default is to look into /etc/papersize or A4 if
363 libpaper isn't installed.
364
365 =item B<-s> B<--show-subkeys>
366
367 Enable subkey information to be printed on the slips. Subkey information is
368 normally not relevant to the key signing process.
369
370 =item B<-r> B<--revoked-style> I<revoked-style>
371
372 Select how to mark revoked UIDs and subkeys. Five styles are available:
373 B<hide> don't show at all (default),
374 B<show> show normally,
375 B<grey> display in 50% grey,
376 B<note> add "[revoked]", and
377 B<strike> strike through.
378
379 =item B<-1>
380
381 Print only one column of paper slips instead of the default two. Useful for keys
382 with long UIDs that otherwise won't fit.
383
384 =item I<keyid>
385
386 Keyids to print. Multiple can be separated by spaces.
387
388 =item B<-h> B<--help>
389
390 Print usage and exit.
391
392 =item B<-v> B<--version>
393
394 Print version and exit.
395
396 =back
397
398 =head1 SEE ALSO
399
400 =over
401
402 =item gpg(1)
403
404 GNU Privacy Guard.
405
406 =item http://pgp-tools.alioth.debian.org/
407
408 The homepage of B<gpg-key2ps> and the other tools bundled in B<signing-party>.
409
410 =item http://www.debian.org/events/materials/business-cards/
411
412 B<gpg-key2ps> prints plain fingerprint slips. If you are looking for something
413 more stylish, look at these latex templates for business cards that also
414 include fingerprints.
415
416 =back
417
418 =head1 AUTHORS AND COPYRIGHT
419
420 =over
421
422 =item (c) 2001-2005 Simon Richter <sjr@debian.org>
423
424 =item (c) 2005-2008 Thijs Kinkhorst <thijs@kinkhorst.com>
425
426 =item (c) 2005-2008 Christoph Berg <cb@df7cb.de>
427
428 =back