/[pgp-tools]/trunk/caff/caff
ViewVC logotype

Contents of /trunk/caff/caff

Parent Directory Parent Directory | Revision Log Revision Log


Revision 7 - (hide annotations) (download)
Tue Jun 29 12:32:29 2004 UTC (8 years, 10 months ago) by weasel
File size: 22238 byte(s)
Move revision number into version string
1 weasel 5 #!/usr/bin/perl -w
2    
3     # caff -- CA - fire and forget
4 weasel 6 # $Id$
5 weasel 5 #
6     # Copyright (c) 2004 Peter Palfrader <peter@palfrader.org>
7     #
8     # All rights reserved.
9     #
10     # Redistribution and use in source and binary forms, with or without
11     # modification, are permitted provided that the following conditions
12     # are met:
13     # 1. Redistributions of source code must retain the above copyright
14     # notice, this list of conditions and the following disclaimer.
15     # 2. Redistributions in binary form must reproduce the above copyright
16     # notice, this list of conditions and the following disclaimer in the
17     # documentation and/or other materials provided with the distribution.
18     # 3. The name of the author may not be used to endorse or promote products
19     # derived from this software without specific prior written permission.
20     #
21     # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22     # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23     # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24     # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25     # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26     # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27     # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28     # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29     # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30     # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31    
32     use strict;
33     use IO::Handle;
34     use English;
35     use File::Path;
36     use File::Temp qw{tempdir};
37     use MIME::Entity;
38     use Fcntl;
39     use IO::Select;
40     use GnuPG::Interface;
41    
42     my %CONFIG;
43 weasel 6 my $REVISION = '$Rev$';
44 weasel 7 my ($REVISION_NUMER) = $REVISION =~ /(\d+)/;
45     my $VERSION = "0.0.0.$REVISION_NUMER";
46 weasel 5
47     sub load_config() {
48     my $config = $ENV{'HOME'} . '/.caffrc';
49     -f $config or die "No file $config present. See caffrc(5).\n";
50     unless (scalar eval `cat $config`) {
51     die "Couldn't parse $config: $EVAL_ERROR\n" if $EVAL_ERROR;
52     };
53    
54     die ("caffhome is not defined.\n") unless defined $CONFIG{'caffhome'};
55     die ("owner is not defined.\n") unless defined $CONFIG{'owner'};
56     die ("email is not defined.\n") unless defined $CONFIG{'email'};
57     die ("keyid is not defined.\n") unless defined $CONFIG{'keyid'};
58     die ("keyid is not an array ref\n") unless (ref $CONFIG{'keyid'} eq 'ARRAY');
59     for my $keyid (@{$CONFIG{'keyid'}}) {
60     $keyid =~ /^[A-Fa-z0-9]{16}$/ or die ("key $keyid is not a long (16 digit) keyid.\n");
61     };
62     @{$CONFIG{'keyid'}} = map { uc } @{$CONFIG{'keyid'}};
63     $CONFIG{'export-sig-age'}= 24*60*60 unless defined $CONFIG{'export-sig-age'};
64     $CONFIG{'keyserver'} = 'subkeys.pgp.net' unless defined $CONFIG{'keyserver'};
65     $CONFIG{'gpg'} = 'gpg' unless defined $CONFIG{'gpg'};
66     $CONFIG{'gpg-sign'} = $CONFIG{'gpg'} unless defined $CONFIG{'gpg-sign'};
67     $CONFIG{'gpg-delsig'} = $CONFIG{'gpg'} unless defined $CONFIG{'gpg-delsig'};
68     $CONFIG{'secret-keyring'} = $ENV{'HOME'}.'/.gnupg/secring.gpg' unless defined $CONFIG{'secret-keyring'};
69     };
70    
71     sub notice($) {
72     my ($line) = @_;
73     print "[NOTICE] $line\n";
74     };
75     sub info($) {
76     my ($line) = @_;
77     print "[INFO] $line\n";
78     };
79     sub debug($) {
80     my ($line) = @_;
81     #print "[DEBUG] $line\n";
82     };
83     sub trace($) {
84     my ($line) = @_;
85     #print "[trace] $line\n";
86     };
87     sub trace2($) {
88     my ($line) = @_;
89     #print "[trace2] $line\n";
90     };
91    
92     sub make_gpg_fds() {
93     my %fds = (
94     stdin => IO::Handle->new(),
95     stdout => IO::Handle->new(),
96     stderr => IO::Handle->new(),
97     status => IO::Handle->new() );
98     my $handles = GnuPG::Handles->new( %fds );
99     return ($fds{'stdin'}, $fds{'stdout'}, $fds{'stderr'}, $fds{'status'}, $handles);
100     };
101    
102     sub readwrite_gpg($$$$$%) {
103     my ($in, $inputfd, $stdoutfd, $stderrfd, $statusfd, %options) = @_;
104    
105     trace("Entering readwrite_gpg.");
106    
107     my ($first_line, $dummy) = split /\n/, $in;
108     debug("readwrite_gpg sends ".(defined $first_line ? $first_line : "<nothing>"));
109    
110     local $INPUT_RECORD_SEPARATOR = undef;
111     my $sout = IO::Select->new();
112     my $sin = IO::Select->new();
113     my $offset = 0;
114    
115     trace("input is $inputfd; output is $stdoutfd; err is $stderrfd; status is ".(defined $statusfd ? $statusfd : 'undef').".");
116    
117     $inputfd->blocking(0);
118     $stdoutfd->blocking(0);
119     $statusfd->blocking(0) if defined $statusfd;
120     $stderrfd->blocking(0);
121     $sout->add($stdoutfd);
122     $sout->add($stderrfd);
123     $sout->add($statusfd) if defined $statusfd;
124     $sin->add($inputfd);
125    
126     my ($stdout, $stderr, $status) = ("", "", "");
127     my $exitwhenstatusmatches = $options{'exitwhenstatusmatches'};
128     trace("doign stuff until we find $exitwhenstatusmatches") if defined $exitwhenstatusmatches;
129    
130     my ($readyr, $readyw, $written);
131     while ($sout->count() > 0 || (defined($sin) && ($sin->count() > 0))) {
132     if (defined $exitwhenstatusmatches) {
133     if ($status =~ /$exitwhenstatusmatches/m) {
134     trace("readwrite_gpg found match on $exitwhenstatusmatches");
135     last;
136     };
137     };
138    
139     trace("select waiting for ".($sout->count())." fds.");
140     ($readyr, $readyw, undef) = IO::Select::select($sout, $sin, undef, 1);
141     trace("ready: write: ".(defined $readyw ? scalar @$readyw : 0 )."; read: ".(defined $readyr ? scalar @$readyr : 0));
142     for my $wfd (@$readyw) {
143     if (length($in) != $offset) {
144     trace("writing to $wfd.");
145     $written = $wfd->syswrite($in, length($in) - $offset, $offset);
146     $offset += $written;
147     };
148     if ($offset == length($in)) {
149     trace("writing to $wfd done.");
150     unless ($options{'nocloseinput'}) {
151     close $wfd;
152     trace("$wfd closed.");
153     };
154     $sin->remove($wfd);
155     $sin = undef;
156     }
157     }
158    
159     next unless (defined(@$readyr)); # Wait some more.
160    
161     for my $rfd (@$readyr) {
162     if ($rfd->eof) {
163     trace("reading from $rfd done.");
164     $sout->remove($rfd);
165     close($rfd);
166     next;
167     }
168     trace("reading from $rfd.");
169     if ($rfd == $stdoutfd) {
170     $stdout .= <$rfd>;
171     trace2("stdout is now $stdout\n================");
172     next;
173     }
174     if (defined $statusfd && $rfd == $statusfd) {
175     $status .= <$rfd>;
176     trace2("status is now $status\n================");
177     next;
178     }
179     if ($rfd == $stderrfd) {
180     $stderr .= <$rfd>;
181     trace2("stderr is now $stderr\n================");
182     next;
183     }
184     }
185     }
186     trace("readwrite_gpg done.");
187     return ($stdout, $stderr, $status);
188     };
189    
190     sub ask($$) {
191     my ($question, $default) = @_;
192     my $answer;
193     while (1) {
194     print $question,' ',($default ? '[Y/n]' : '[y/N]'), ' ';
195     $answer = <STDIN>;
196     chomp $answer;
197     last if ((defined $answer) && (length $answer <= 1));
198     print "grrrrrr.\n";
199     sleep 1;
200     };
201     my $result = $default;
202     $result = 1 if $answer =~ /y/i;
203     $result = 0 if $answer =~ /n/i;
204     return $result;
205     };
206    
207    
208    
209    
210    
211     my $KEYEDIT_PROMPT = '^\[GNUPG:\] GET_LINE keyedit.prompt';
212     my $KEYEDIT_DELUID_PROMPT = '^\[GNUPG:\] GET_BOOL keyedit.remove.uid.okay';
213     my $KEYEDIT_DELSIG_PROMPT = '^\[GNUPG:\] GET_BOOL keyedit.delsig';
214     my $KEYEDIT_KEYEDIT_OR_DELSIG_PROMPT = '^\[GNUPG:\] (GET_BOOL keyedit.delsig|GET_LINE keyedit.prompt)';
215     my $KEYEDIT_DELSUBKEY_PROMPT = '^\[GNUPG:\] GET_BOOL keyedit.remove.subkey';
216    
217     load_config;
218     my $USER_AGENT = "caff $VERSION - (c) 2004 Peter Palfrader";
219    
220     my $KEYSBASE = $CONFIG{'caffhome'}.'/keys';
221     my $GNUPGHOME = $CONFIG{'caffhome'}.'/gnupghome';
222    
223     -d $KEYSBASE || mkpath($KEYSBASE , 0, 0700) or die ("Cannot create $KEYSBASE: $!\n");
224     -d $GNUPGHOME || mkpath($GNUPGHOME, 0, 0700) or die ("Cannot create $GNUPGHOME: $!\n");
225    
226     my $NOW = time;
227     my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($NOW);
228     my $DATE_STRING = sprintf("%04d-%02d-%02d", $year+1900, $mon+1, $mday);
229    
230    
231     sub usage() {
232 weasel 7 print STDERR "caff $VERSION - (c) 2004 Peter Palfrader\n";
233 weasel 5 print STDERR "Usage: $PROGRAM_NAME [-u <yourkeyid] <keyid> [<keyid> ...]\n";
234     exit 1;
235     };
236    
237     sub export_key($$) {
238     my ($gnupghome, $keyid) = @_;
239    
240     my $gpg = GnuPG::Interface->new();
241     $gpg->call( $CONFIG{'gpg'} );
242     $gpg->options->hash_init(
243     'homedir' => $gnupghome,
244     'armor' => 1 );
245     $gpg->options->meta_interactive( 0 );
246     my ($inputfd, $stdoutfd, $stderrfd, $statusfd, $handles) = make_gpg_fds();
247     my $pid = $gpg->export_keys(handles => $handles, command_args => [ $keyid ]);
248     my ($stdout, $stderr, $status) = readwrite_gpg('', $inputfd, $stdoutfd, $stderrfd, $statusfd);
249     waitpid $pid, 0;
250    
251     return $stdout;
252     };
253    
254     #send_mail($address, $can_encrypt, $longkeyid, $uid, @attached);
255     sub send_mail($$$@) {
256     my ($address, $can_encrypt, $key_id, @keys) = @_;
257    
258     my $message = "Hi,\n\n";
259    
260     $message .= 'please find attached the user id'.(scalar @keys >= 2 ? 's' : '')."\n";
261     for my $key (@keys) {
262     $message .= "\t".$key->{'text'}."\n";
263     };
264     $message .= qq{of your key $key_id signed by me.
265    
266     Note that I did not upload your key to any keyservers. If you want this
267     new signature to be available to others, please upload it yourself.
268     With GnuPG this can be done using
269     gpg --keyserver subkeys.pgp.net --send-key $key_id
270    
271     If you have any questions, don't hesitate to ask.
272    
273     Regards,
274     $CONFIG{'owner'}
275     };
276     my $message_entity = MIME::Entity->build(
277     Type => "text/plain",
278     Charset => "utf-8",
279     Disposition => 'inline',
280     Data => $message);
281    
282     my @key_entities;
283     for my $key (@keys) {
284     $message_entity->attach(
285     Type => "application/pgp-keys",
286     Disposition => 'attachment',
287     Encoding => "7bit",
288     Description => "PGP Key 0x$key_id, uid ".($key->{'text'}).' ('.($key->{'serial'}).')',
289     Data => $key->{'key'},
290     Filename => "0x$key_id.".$key->{'serial'}.".asc");
291     };
292    
293     if ($can_encrypt) {
294     my $message = $message_entity->stringify();
295    
296     my $gpg = GnuPG::Interface->new();
297     $gpg->call( $CONFIG{'gpg'} );
298     $gpg->options->hash_init( 'homedir' => $GNUPGHOME,
299     'extra_args' => '--always-trust',
300     'armor' => 1 );
301     $gpg->options->meta_interactive( 0 );
302     my ($inputfd, $stdoutfd, $stderrfd, $statusfd, $handles) = make_gpg_fds();
303     $gpg->options->push_recipients( $key_id );
304     $gpg->options->push_recipients( $CONFIG{'also-encrypt-to'} ) if defined $CONFIG{'also-encrypt-to'};
305     my $pid = $gpg->encrypt(handles => $handles);
306     my ($stdout, $stderr, $status) = readwrite_gpg($message, $inputfd, $stdoutfd, $stderrfd, $statusfd);
307     waitpid $pid, 0;
308     if ($stdout eq '') {
309     warn ("No data from gpg for list-key $key_id\n");
310     next;
311     };
312     $message = $stdout;
313    
314     $message_entity = MIME::Entity->build(
315     Type => 'multipart/encrypted; protocol="application/pgp-encrypted"');
316    
317     $message_entity->attach(
318     Type => "application/pgp-encrypted",
319     Disposition => 'attachment',
320     Encoding => "7bit",
321     Data => "Version: 1\n");
322    
323     $message_entity->attach(
324     Type => "application/octet-stream",
325     Filename => 'msg.asc',
326     Disposition => 'inline',
327     Encoding => "7bit",
328     Data => $message);
329     };
330    
331     $message_entity->head->add("Subject", "Your signed PGP key 0x$key_id");
332     $message_entity->head->add("To", $address);
333     $message_entity->head->add("From", $CONFIG{'owner'}.' <'.$CONFIG{'email'}.'>');
334     $message_entity->head->add("User-Agent", $USER_AGENT);
335     $message_entity->send();
336     $message_entity->stringify();
337     };
338    
339     my $USER;
340     my @KEYIDS;
341    
342     usage() unless scalar @ARGV >= 1;
343     if ($ARGV[0] eq '-u') {
344     usage() unless scalar @ARGV >= 3;
345     shift @ARGV;
346     $USER = shift @ARGV;
347     unless ($USER =~ /^[A-Za-z0-9]{8,8}([A-Za-z0-9]{8})?$/) {
348     print STDERR "-u $USER is not a keyid.\n";
349     usage();
350     };
351     $USER = uc($USER);
352     };
353     for my $keyid (@ARGV) {
354     unless ($keyid =~ /^[A-Za-z0-9]{8}([A-Za-z0-9]{8})?$/) {
355     print STDERR "$keyid is not a keyid.\n";
356     usage();
357     };
358     push @KEYIDS, uc($keyid);
359     };
360    
361    
362     #############################
363     # receive keys from keyserver
364     #############################
365     my $gpg = GnuPG::Interface->new();
366     $gpg->call( $CONFIG{'gpg'} );
367     $gpg->options->hash_init(
368     'homedir' => $GNUPGHOME,
369     'extra_args' => '--keyserver='.$CONFIG{'keyserver'} );
370     $gpg->options->meta_interactive( 0 );
371     my ($inputfd, $stdoutfd, $stderrfd, $statusfd, $handles) = make_gpg_fds();
372     my $pid = $gpg->recv_keys(handles => $handles, command_args => [ @KEYIDS ]);
373     my ($stdout, $stderr, $status) = readwrite_gpg('', $inputfd, $stdoutfd, $stderrfd, $statusfd);
374     waitpid $pid, 0;
375    
376     my @keyids_ok;
377     my @keyids_failed;
378     # [GNUPG:] IMPORT_OK 0 5B00C96D5D54AEE1206BAF84DE7AAF6E94C09C7F
379     # [GNUPG:] NODATA 1
380     # [GNUPG:] NODATA 1
381     # [GNUPG:] IMPORT_OK 0 25FC1614B8F87B52FF2F99B962AF4031C82E0039
382     for my $line (split /\n/, $status) {
383     if ($line =~ /^\[GNUPG:\] IMPORT_OK/) {
384     push @keyids_ok, shift @KEYIDS;
385     } elsif ($line =~ /^\[GNUPG:\] NODATA/) {
386     push @keyids_failed, shift @KEYIDS;
387     };
388     }
389     die ("Still keys in \@KEYIDS. This should not happen.") if scalar @KEYIDS;
390     notice ("Import failed for: ". (join ' ', @keyids_failed).".") if scalar @keyids_failed;
391    
392     ###########
393     # sign keys
394     ###########
395     info("Sign the following keys according to your policy...");
396     for my $keyid (@keyids_ok) {
397     my @command;
398     push @command, $CONFIG{'gpg-sign'};
399     push @command, '--local-user', $USER if (defined $USER);
400     push @command, "--homedir=$GNUPGHOME";
401     push @command, '--secret-keyring', $CONFIG{'secret-keyring'};
402     push @command, '--sign-key', $keyid;
403     print join(' ', @command),"\n";
404     system (@command);
405     };
406    
407     ##################
408     # export and prune
409     ##################
410     KEYS:
411     for my $keyid (@keyids_ok) {
412     # get key listing
413     #################
414     $gpg = GnuPG::Interface->new();
415     $gpg->call( $CONFIG{'gpg'} );
416     $gpg->options->hash_init( 'homedir' => $GNUPGHOME );
417     $gpg->options->meta_interactive( 0 );
418     ($inputfd, $stdoutfd, $stderrfd, $statusfd, $handles) = make_gpg_fds();
419     $gpg->options->hash_init( 'extra_args' => [ '--with-colons', '--fixed-list-mode' ] );
420     $pid = $gpg->list_public_keys(handles => $handles, command_args => [ $keyid ]);
421     ($stdout, $stderr, $status) = readwrite_gpg('', $inputfd, $stdoutfd, $stderrfd, $statusfd);
422     waitpid $pid, 0;
423     if ($stdout eq '') {
424     warn ("No data from gpg for list-key $keyid\n");
425     next;
426     };
427     my $keyinfo = $stdout;
428     my @publine = grep { /^pub/ } (split /\n/, $stdout);
429     my ($dummy1, $dummy2, $dummy3, $dummy4, $longkeyid, $dummy6, $dummy7, $dummy8, $dummy9, $dummy10, $dummy11, $flags) = split /:/, pop @publine;
430     my $can_encrypt = $flags =~ /E/;
431     unless (defined $longkeyid) {
432     warn ("Didn't find public keyid in edit dialog of key $keyid.\n");
433     next;
434     };
435    
436     # export the key
437     ################
438     my $asciikey = export_key($GNUPGHOME, $keyid);
439     if ($asciikey eq '') {
440     warn ("No data from gpg for export $keyid\n");
441     next;
442     };
443    
444     my @UIDS;
445     my $uid_number = 0;
446     while (1) {
447     my $this_uid_text = '';
448     $uid_number++;
449     info("Doing key $keyid, uid $uid_number");
450    
451     # import into temporary gpghome
452     ###############################
453     my $tempdir = tempdir( "caff-$keyid-XXXXX", DIR => '/tmp/', CLEANUP => 1);
454     my $gpg = GnuPG::Interface->new();
455     $gpg->call( $CONFIG{'gpg'} );
456     $gpg->options->hash_init( 'homedir' => $tempdir );
457     $gpg->options->meta_interactive( 0 );
458     my ($inputfd, $stdoutfd, $stderrfd, $statusfd, $handles) = make_gpg_fds();
459     my $pid = $gpg->import_keys(handles => $handles);
460     my ($stdout, $stderr, $status) = readwrite_gpg($asciikey, $inputfd, $stdoutfd, $stderrfd, $statusfd);
461     waitpid $pid, 0;
462    
463     if ($status !~ /^\[GNUPG:\] IMPORT_OK/m) {
464     warn ("Could not import $keyid into temporary gnupg.\n");
465     next;
466     };
467    
468     # prune it
469     ##########
470     $gpg = GnuPG::Interface->new();
471     $gpg->call( $CONFIG{'gpg-delsig'} );
472     $gpg->options->hash_init(
473     'homedir' => $tempdir,
474     'extra_args' => [ '--with-colons', '--fixed-list-mode', '--command-fd=0', '--no-tty' ] );
475     ($inputfd, $stdoutfd, $stderrfd, $statusfd, $handles) = make_gpg_fds();
476     $pid = $gpg->wrap_call(
477     commands => [ '--edit' ],
478     command_args => [ $keyid ],
479     handles => $handles );
480    
481     debug("Starting edit session");
482     ($stdout, $stderr, $status) = readwrite_gpg('', $inputfd, $stdoutfd, $stderrfd, $statusfd, exitwhenstatusmatches => $KEYEDIT_PROMPT, nocloseinput => 1);
483    
484     # delete other uids
485     ###################
486     my $number_of_subkeys = 0;
487     my $i = 1;
488     my $have_one = 0;
489     my $is_uat = 0;
490     my $delete_some = 0;
491     debug("Parsing stdout output.");
492     for my $line (split /\n/, $stdout) {
493     debug("Checking line $line");
494     my ($type, $dummy2, $dummy3, $dummy4, $dummy5, $dummy6, $dummy7, $dummy8, $dummy9, $uidtext) = split /:/, $line;
495     if ($type eq 'sub') {
496     $number_of_subkeys++;
497     };
498     next unless ($type eq 'uid' || $type eq 'uat');
499     debug("line is interesting.");
500     if ($uid_number != $i) {
501     debug("mark for deletion.");
502     readwrite_gpg("$i\n", $inputfd, $stdoutfd, $stderrfd, $statusfd, exitwhenstatusmatches => $KEYEDIT_PROMPT, nocloseinput => 1);
503     $delete_some = 1;
504     } else {
505     debug("keep it.");
506     $have_one = 1;
507     $this_uid_text = ($type eq 'uid') ? $uidtext : 'attribute';
508     $is_uat = $type eq 'uat';
509     };
510     $i++;
511     };
512     debug("Parsing stdout output done.");
513     if ($is_uat) {
514     notice("Can't handle attribute userid of key $keyid.");
515     next;
516     };
517     unless ($have_one) {
518     info("key $keyid done.");
519     last;
520     };
521     if ($delete_some) {
522     debug("need to delete a few uids.");
523     readwrite_gpg("deluid\n", $inputfd, $stdoutfd, $stderrfd, $statusfd, exitwhenstatusmatches => $KEYEDIT_DELUID_PROMPT, nocloseinput => 1);
524     readwrite_gpg("yes\n", $inputfd, $stdoutfd, $stderrfd, $statusfd, exitwhenstatusmatches => $KEYEDIT_PROMPT, nocloseinput => 1);
525     };
526    
527     # delete subkeys
528     ################
529     if ($number_of_subkeys > 0) {
530     for (my $i=1; $i<=$number_of_subkeys; $i++) {
531     readwrite_gpg("key $i\n", $inputfd, $stdoutfd, $stderrfd, $statusfd, exitwhenstatusmatches => $KEYEDIT_PROMPT, nocloseinput => 1);
532     };
533     readwrite_gpg("delkey\n", $inputfd, $stdoutfd, $stderrfd, $statusfd, exitwhenstatusmatches => $KEYEDIT_DELSUBKEY_PROMPT, nocloseinput => 1);
534     readwrite_gpg("yes\n", $inputfd, $stdoutfd, $stderrfd, $statusfd, exitwhenstatusmatches => $KEYEDIT_PROMPT, nocloseinput => 1);
535     };
536    
537     # delete signatures
538     ###################
539     my $signed_by_me = 0;
540     readwrite_gpg("1\n", $inputfd, $stdoutfd, $stderrfd, $statusfd, exitwhenstatusmatches => $KEYEDIT_PROMPT, nocloseinput => 1);
541     ($stdout, $stderr, $status) =
542     readwrite_gpg("delsig\n", $inputfd, $stdoutfd, $stderrfd, $statusfd, exitwhenstatusmatches => $KEYEDIT_DELSIG_PROMPT, nocloseinput => 1);
543    
544     while($status =~ /$KEYEDIT_DELSIG_PROMPT/m) {
545     # sig:?::17:EA2199412477CAF8:1058095214:::::13x:
546     my @sigline = grep { /^sig/ } (split /\n/, $stdout);
547     my $line = pop @sigline;
548     my $answer = "no";
549     if (defined $line) { # only if we found a sig here - we never remove revocation packets for instance
550     my ($dummy1, $dummy2, $dummy3, $dummy4, $signer, $created, $dummy7, $dummy8, $dummy9) = split /:/, $line;
551     if ($signer eq $longkeyid) {
552     $answer = "no";
553     } elsif (grep { $signer eq $_ } @{$CONFIG{'keyid'}}) {
554     $answer = "no";
555     $signed_by_me = $signed_by_me > $created ? $signed_by_me : $created;
556     } else {
557     $answer = "yes";
558     };
559     };
560     ($stdout, $stderr, $status) =
561     readwrite_gpg($answer."\n", $inputfd, $stdoutfd, $stderrfd, $statusfd, exitwhenstatusmatches => $KEYEDIT_KEYEDIT_OR_DELSIG_PROMPT, nocloseinput => 1);
562     };
563     readwrite_gpg("save\n", $inputfd, $stdoutfd, $stderrfd, $statusfd);
564     waitpid $pid, 0;
565    
566     my $asciikey = export_key($tempdir, $longkeyid);
567     if ($asciikey eq '') {
568     warn ("No data from gpg for export $longkeyid\n");
569     next;
570     };
571    
572     if ($signed_by_me) {
573     if ($NOW - $signed_by_me > $CONFIG{'export-sig-age'} ) {
574     my $write = ask("Signature on $this_uid_text is old. Export?", 0);
575     next unless $write;
576     };
577     my $keydir = "$KEYSBASE/$DATE_STRING";
578     -d $keydir || mkpath($keydir , 0, 0700) or die ("Cannot create $keydir $!\n");
579    
580     my $keyfile = "$keydir/$longkeyid.key.$uid_number.$this_uid_text.asc";
581     open (KEY, ">$keyfile") or die ("Cannot open $keyfile\n");
582     print KEY $asciikey;
583     close KEY;
584    
585     push @UIDS, { text => $this_uid_text, key => $asciikey, serial => $uid_number };
586    
587     info("$longkeyid $uid_number $this_uid_text done.");
588     } else {
589     info("$longkeyid $uid_number $this_uid_text is not signed by me, not writing.");
590     };
591     };
592    
593     if (scalar @UIDS == 0) {
594     info("found no signed uids for $keyid");
595     } else {
596     my @attached ;
597     for my $uid (@UIDS) {
598     unless ($uid->{'text'} =~ /@/) {
599     my $attach = ask("UID $uid->{'text'} is no email address, attach it to every email sent?", 1);
600     push @attached, $uid;
601     };
602     };
603    
604     notice("Key has no encryption capabilities, mail will be sent unencrypted") unless $can_encrypt;
605     for my $uid (@UIDS) {
606     if ($uid->{'text'} =~ /@/) {
607     my $address = $uid->{'text'};
608     $address =~ s/.*<(.*)>.*/$1/;
609     my $send = ask("Send mail to '$address' for $uid->{'text'}?", 1);
610     if ($send) {
611     my $mail = send_mail($address, $can_encrypt, $longkeyid, $uid, @attached);
612    
613     my $keydir = "$KEYSBASE/$DATE_STRING";
614     my $mailfile = "$keydir/$longkeyid.mail.".$uid->{'serial'}.".".$uid->{'text'};
615     open (KEY, ">$mailfile") or die ("Cannot open $mailfile\n");
616     print KEY $mail;
617     close KEY;
618     };
619     };
620     };
621     };
622    
623     };
624    
625    
626    
627    
628     ###############################################################3
629     #### old fork gpg --edit
630     =cut
631     my ($stdin_read, $stdin_write);
632     my ($stdout_read, $stdout_write);
633     my ($stderr_read, $stderr_write);
634     my ($status_read, $status_write);
635     pipe $stdin_read, $stdin_write;
636     pipe $stdout_read, $stdout_write;
637     pipe $stderr_read, $stderr_write;
638     pipe $status_read, $status_write;
639    
640     $pid = fork();
641     unless ($pid) { # child
642     close $stdin_write;
643     close $stdout_read;
644     close $stderr_read;
645     close $status_read;
646    
647     my @call;
648     push @call, $CONFIG{'gpg-delsig'};
649     push @call, "--homedir=$tempdir";
650     push @call, '--with-colons';
651     push @call, '--fixed-list-mode';
652     push @call, '--command-fd=0';
653     push @call, "--status-fd=".fileno($status_write);
654     push @call, "--no-tty";
655     push @call, "--edit";
656     push @call, $keyid;
657    
658     close STDIN;
659     close STDOUT;
660     close STDERR;
661     open (STDIN, "<&".fileno($stdin_read)) or die ("Cannot reopen stdin: $!\n");
662     open (STDOUT, ">&".fileno($stdout_write)) or die ("Cannot reopen stdout: $!\n");
663     open (STDERR, ">&".fileno($stderr_write)) or die ("Cannot reopen stderr: $!\n");
664    
665     fcntl $status_write, F_SETFD, 0;
666    
667     exec (@call);
668     exit;
669     };
670     close $stdin_read;
671     close $stdout_write;
672     close $stderr_write;
673     close $status_write;
674    
675     $inputfd = $stdin_write;
676     $stdoutfd = $stdout_read;
677     $stderrfd = $stderr_read;
678     $statusfd = $status_read;
679     =cut

Properties

Name Value
svn:executable *
svn:keywords Id Rev

  ViewVC Help
Powered by ViewVC 1.1.5