3 # caff -- CA - fire and forget
6 # Copyright (c) 2004 Peter Palfrader <peter@palfrader.org>
10 # Redistribution and use in source and binary forms, with or without
11 # modification, are permitted provided that the following conditions
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.
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.
36 use File
::Temp
qw{tempdir
};
43 my $REVISION = '$Rev$';
44 my ($REVISION_NUMER) = $REVISION =~ /(\d+)/;
45 my $VERSION = "0.0.0.$REVISION_NUMER";
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;
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");
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'};
73 print "[NOTICE] $line\n";
77 print "[INFO] $line\n";
81 #print "[DEBUG] $line\n";
85 #print "[trace] $line\n";
89 #print "[trace2] $line\n";
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);
102 sub readwrite_gpg
($$$$$%) {
103 my ($in, $inputfd, $stdoutfd, $stderrfd, $statusfd, %options) = @_;
105 trace
("Entering readwrite_gpg.");
107 my ($first_line, $dummy) = split /\n/, $in;
108 debug
("readwrite_gpg sends ".(defined $first_line ?
$first_line : "<nothing>"));
110 local $INPUT_RECORD_SEPARATOR = undef;
111 my $sout = IO
::Select
->new();
112 my $sin = IO
::Select
->new();
115 trace
("input is $inputfd; output is $stdoutfd; err is $stderrfd; status is ".(defined $statusfd ?
$statusfd : 'undef').".");
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;
126 my ($stdout, $stderr, $status) = ("", "", "");
127 my $exitwhenstatusmatches = $options{'exitwhenstatusmatches'};
128 trace
("doign stuff until we find $exitwhenstatusmatches") if defined $exitwhenstatusmatches;
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");
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);
148 if ($offset == length($in)) {
149 trace
("writing to $wfd done.");
150 unless ($options{'nocloseinput'}) {
152 trace
("$wfd closed.");
159 next unless (defined(@
$readyr)); # Wait some more.
161 for my $rfd (@
$readyr) {
163 trace
("reading from $rfd done.");
168 trace
("reading from $rfd.");
169 if ($rfd == $stdoutfd) {
171 trace2
("stdout is now $stdout\n================");
174 if (defined $statusfd && $rfd == $statusfd) {
176 trace2
("status is now $status\n================");
179 if ($rfd == $stderrfd) {
181 trace2
("stderr is now $stderr\n================");
186 trace
("readwrite_gpg done.");
187 return ($stdout, $stderr, $status);
191 my ($question, $default) = @_;
194 print $question,' ',($default ?
'[Y/n]' : '[y/N]'), ' ';
197 last if ((defined $answer) && (length $answer <= 1));
201 my $result = $default;
202 $result = 1 if $answer =~ /y/i;
203 $result = 0 if $answer =~ /n/i;
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';
218 my $USER_AGENT = "caff $VERSION - (c) 2004 Peter Palfrader";
220 my $KEYSBASE = $CONFIG{'caffhome'}.'/keys';
221 my $GNUPGHOME = $CONFIG{'caffhome'}.'/gnupghome';
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");
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);
232 print STDERR
"caff $VERSION - (c) 2004 Peter Palfrader\n";
233 print STDERR
"Usage: $PROGRAM_NAME [-u <yourkeyid] <keyid> [<keyid> ...]\n";
238 my ($gnupghome, $keyid) = @_;
240 my $gpg = GnuPG
::Interface
->new();
241 $gpg->call( $CONFIG{'gpg'} );
242 $gpg->options->hash_init(
243 'homedir' => $gnupghome,
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);
254 #send_mail($address, $can_encrypt, $longkeyid, $uid, @attached);
255 sub send_mail
($$$@
) {
256 my ($address, $can_encrypt, $key_id, @keys) = @_;
258 my $message = "Hi,\n\n";
260 $message .= 'please find attached the user id'.(scalar @keys >= 2 ?
's' : '')."\n";
261 for my $key (@keys) {
262 $message .= "\t".$key->{'text'}."\n";
264 $message .= qq{of your key
$key_id signed by me
.
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
271 If you have any questions
, don
't hesitate to ask.
276 my $message_entity = MIME::Entity->build(
277 Type => "text/plain",
279 Disposition => 'inline
',
283 for my $key (@keys) {
284 $message_entity->attach(
285 Type => "application/pgp-keys",
286 Disposition => 'attachment
',
288 Description => "PGP Key 0x$key_id, uid ".($key->{'text
'}).' ('.($key->{'serial
'}).')',
289 Data => $key->{'key
'},
290 Filename => "0x$key_id.".$key->{'serial
'}.".asc");
294 my $message = $message_entity->stringify();
296 my $gpg = GnuPG::Interface->new();
297 $gpg->call( $CONFIG{'gpg
'} );
298 $gpg->options->hash_init( 'homedir
' => $GNUPGHOME,
299 'extra_args
' => '--always
-trust
',
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);
309 warn ("No data from gpg for list-key $key_id\n");
314 $message_entity = MIME::Entity->build(
315 Type => 'multipart
/encrypted; protocol="application/pgp-encrypted
"');
317 $message_entity->attach(
318 Type => "application
/pgp
-encrypted
",
319 Disposition => 'attachment',
321 Data => "Version
: 1\n");
323 $message_entity->attach(
324 Type => "application
/octet
-stream
",
325 Filename => 'msg.asc',
326 Disposition => 'inline',
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();
342 usage() unless scalar @ARGV >= 1;
343 if ($ARGV[0] eq '-u') {
344 usage() unless scalar @ARGV >= 3;
347 unless ($USER =~ /^[A-Za-z0-9]{8,8}([A-Za-z0-9]{8})?$/) {
348 print STDERR "-u
$USER is
not a keyid
.\n";
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";
358 push @KEYIDS, uc($keyid);
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);
378 # [GNUPG:] IMPORT_OK 0 5B00C96D5D54AEE1206BAF84DE7AAF6E94C09C7F
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;
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;
395 info("Sign the following
keys according to your policy
...");
396 for my $keyid (@keyids_ok) {
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";
411 for my $keyid (@keyids_ok) {
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);
424 warn ("No data from gpg
for list
-key
$keyid\n");
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");
438 my $asciikey = export_key($GNUPGHOME, $keyid);
439 if ($asciikey eq '') {
440 warn ("No data from gpg for export $keyid\n");
447 my $this_uid_text = '';
449 info("Doing key $keyid, uid $uid_number");
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);
463 if ($status !~ /^\[GNUPG:\] IMPORT_OK/m) {
464 warn ("Could not import $keyid into temporary gnupg.\n");
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 );
481 debug("Starting edit session");
482 ($stdout, $stderr, $status) = readwrite_gpg('', $inputfd, $stdoutfd, $stderrfd, $statusfd, exitwhenstatusmatches => $KEYEDIT_PROMPT, nocloseinput => 1);
486 my $number_of_subkeys = 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++;
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);
507 $this_uid_text = ($type eq 'uid
') ? $uidtext : 'attribute
';
508 $is_uat = $type eq 'uat
';
512 debug("Parsing stdout output done.");
514 notice("Can't handle attribute userid of key
$keyid.");
518 info("key
$keyid done
.");
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);
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);
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);
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);
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;
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) {
553 } elsif (grep { $signer eq $_ } @{$CONFIG{'keyid'}}) {
555 $signed_by_me = $signed_by_me > $created ? $signed_by_me : $created;
560 ($stdout, $stderr, $status) =
561 readwrite_gpg($answer."\n", $inputfd, $stdoutfd, $stderrfd, $statusfd, exitwhenstatusmatches => $KEYEDIT_KEYEDIT_OR_DELSIG_PROMPT, nocloseinput => 1);
563 readwrite_gpg("save
\n", $inputfd, $stdoutfd, $stderrfd, $statusfd);
566 my $asciikey = export_key($tempdir, $longkeyid);
567 if ($asciikey eq '') {
568 warn ("No data from gpg
for export
$longkeyid\n");
573 if ($NOW - $signed_by_me > $CONFIG{'export-sig-age'} ) {
574 my $write = ask("Signature on
$this_uid_text is old
. Export?
", 0);
577 my $keydir = "$KEYSBASE/$DATE_STRING";
578 -d $keydir || mkpath($keydir , 0, 0700) or die ("Cannot create
$keydir $!\n");
580 my $keyfile = "$keydir/$longkeyid.key
.$uid_number.$this_uid_text.asc
";
581 open (KEY, ">$keyfile") or die ("Cannot
open $keyfile\n");
585 push @UIDS, { text => $this_uid_text, key => $asciikey, serial => $uid_number };
587 info("$longkeyid $uid_number $this_uid_text done
.");
589 info("$longkeyid $uid_number $this_uid_text is
not signed by me
, not writing
.");
593 if (scalar @UIDS == 0) {
594 info("found
no signed uids
for $keyid");
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;
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);
611 my $mail = send_mail($address, $can_encrypt, $longkeyid, $uid, @attached);
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");
628 ###############################################################3
629 #### old fork gpg --edit
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;
641 unless ($pid) { # child
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
";
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");
665 fcntl $status_write, F_SETFD, 0;
675 $inputfd = $stdin_write;
676 $stdoutfd = $stdout_read;
677 $stderrfd = $stderr_read;
678 $statusfd = $status_read;