=back
+=item B<bcc> [string]
+
+Address to send blind carbon copies to when sending mail.
+Default: none.
+
=back
=head1 AUTHOR
exit $exitcode;
};
+######
+# export key $keyid from $gnupghome
+######
sub export_key($$) {
my ($gnupghome, $keyid) = @_;
return $stdout;
};
+######
+# import a key from the scalar $asciikey into a gpg homedirectory in $tempdir
+######
+sub import_key($$) {
+ my ($gnupghome, $asciikey) = @_;
+
+ my $gpg = GnuPG::Interface->new();
+ $gpg->call( $CONFIG{'gpg'} );
+ $gpg->options->hash_init( 'homedir' => $gnupghome );
+ $gpg->options->meta_interactive( 0 );
+ my ($inputfd, $stdoutfd, $stderrfd, $statusfd, $handles) = make_gpg_fds();
+ my $pid = $gpg->import_keys(handles => $handles);
+ my ($stdout, $stderr, $status) = readwrite_gpg($asciikey, $inputfd, $stdoutfd, $stderrfd, $statusfd);
+ waitpid $pid, 0;
+
+ if ($status !~ /^\[GNUPG:\] IMPORT_OK/m) {
+ return undef;
+ };
+ return 1;
+};
+
+
+######
+# Send an email to $address. If $can_encrypt is true then the mail
+# will be PGP/MIME encrypted to $longkeyid.
+#
+# $longkeyid, $uid, and @attached will be used in the email and the template.
+######
#send_mail($address, $can_encrypt, $longkeyid, $uid, @attached);
sub send_mail($$$@) {
my ($address, $can_encrypt, $key_id, @keys) = @_;
$message_entity->head->add("Subject", "Your signed PGP key 0x$key_id");
$message_entity->head->add("To", $address);
$message_entity->head->add("From", '"'.$CONFIG{'owner'}.'" <'.$CONFIG{'email'}.'>');
+ $message_entity->head->add("Bcc", $CONFIG{'bcc'}) if defined $CONFIG{'bcc'};
$message_entity->head->add("User-Agent", $USER_AGENT);
$message_entity->send();
$message_entity->stringify();
};
+######
+# clean up a UID so that it can be used on the FS.
+######
sub sanitize_uid($) {
my ($uid) = @_;
return $good_uid;
};
+sub delete_signatures($$$$$$) {
+ my ($inputfd, $stdoutfd, $stderrfd, $statusfd, $longkeyid, $keyids) =@_;
+
+ my $signed_by_me = 0;
+
+ my ($stdout, $stderr, $status) =
+ readwrite_gpg("delsig\n", $inputfd, $stdoutfd, $stderrfd, $statusfd, exitwhenstatusmatches => $KEYEDIT_DELSIG_PROMPT, nocloseinput => 1);
+
+ while($status =~ /$KEYEDIT_DELSIG_PROMPT/m) {
+ # sig:?::17:EA2199412477CAF8:1058095214:::::13x:
+ my @sigline = grep { /^sig/ } (split /\n/, $stdout);
+ $stdout =~ s/\n/\\n/g;
+ notice("[sigremoval] why are there ".(scalar @sigline)." siglines in that part of the dialog!? got: $stdout") if scalar @sigline >= 2; # XXX
+ my $line = pop @sigline;
+ my $answer = "no";
+ if (defined $line) { # only if we found a sig here - we never remove revocation packets for instance
+ debug("[sigremoval] doing line $line.");
+ my (undef, undef, undef, undef, $signer, $created, undef, undef, undef) = split /:/, $line;
+ if ($signer eq $longkeyid) {
+ debug("[sigremoval] selfsig ($signer).");
+ $answer = "no";
+ } elsif (grep { $signer eq $_ } @{$keyids}) {
+ debug("[sigremoval] signed by us ($signer).");
+ $answer = "no";
+ $signed_by_me = $signed_by_me > $created ? $signed_by_me : $created;
+ } else {
+ debug("[sigremoval] not interested in that sig ($signer).");
+ $answer = "yes";
+ };
+ } else {
+ debug("[sigremoval] no sig line here, only got: ".$stdout);
+ };
+ ($stdout, $stderr, $status) =
+ readwrite_gpg($answer."\n", $inputfd, $stdoutfd, $stderrfd, $statusfd, exitwhenstatusmatches => $KEYEDIT_KEYEDIT_OR_DELSIG_PROMPT, nocloseinput => 1);
+ };
+
+ return $signed_by_me;
+};
+
+
+
my $USER;
my @KEYIDS;
my $params;
my $this_uid_text = '';
$uid_number++;
debug("Doing key $keyid, uid $uid_number");
+ my $tempdir = tempdir( "caff-$keyid-XXXXX", DIR => '/tmp/', CLEANUP => 1);
# import into temporary gpghome
###############################
- my $tempdir = tempdir( "caff-$keyid-XXXXX", DIR => '/tmp/', CLEANUP => 1);
- my $gpg = GnuPG::Interface->new();
- $gpg->call( $CONFIG{'gpg'} );
- $gpg->options->hash_init( 'homedir' => $tempdir );
- $gpg->options->meta_interactive( 0 );
- my ($inputfd, $stdoutfd, $stderrfd, $statusfd, $handles) = make_gpg_fds();
- my $pid = $gpg->import_keys(handles => $handles);
- my ($stdout, $stderr, $status) = readwrite_gpg($asciikey, $inputfd, $stdoutfd, $stderrfd, $statusfd);
- waitpid $pid, 0;
-
- if ($status !~ /^\[GNUPG:\] IMPORT_OK/m) {
+ my $result = import_key($tempdir, $asciikey);
+ unless ($result) {
warn ("Could not import $keyid into temporary gnupg.\n");
next;
};
if ($uid_number != $i) {
debug("mark for deletion.");
readwrite_gpg("$i\n", $inputfd, $stdoutfd, $stderrfd, $statusfd, exitwhenstatusmatches => $KEYEDIT_PROMPT, nocloseinput => 1);
- $delete_some = 1;
+ $delete_some++;
} else {
debug("keep it.");
$have_one = 1;
- $this_uid_text = ($type eq 'uid') ? $uidtext : 'attribute';
+ $this_uid_text = ($type eq 'uid') ? $uidtext : '[attribute]';
$is_uat = $type eq 'uat';
};
$i++;
};
debug("Parsing stdout output done.");
- if ($is_uat) {
- notice("Can't handle attribute userid of key $keyid.");
- next;
- };
unless ($have_one) {
debug("Uid ".($uid_number-1)." was the last, there is no $uid_number.");
info("key $keyid done.");
last;
};
+
+ my $prune_some_sigs_on_uid;
+ my $prune_all_sigs_on_uid;
+ if ($is_uat) {
+ debug("handling attribute userid of key $keyid.");
+ if ($uid_number == 1) {
+ debug(" attribute userid is #1, unmarking #2 for deletion.");
+ readwrite_gpg("2\n", $inputfd, $stdoutfd, $stderrfd, $statusfd, exitwhenstatusmatches => $KEYEDIT_PROMPT, nocloseinput => 1);
+ $delete_some--;
+ $prune_some_sigs_on_uid = 1;
+ $prune_all_sigs_on_uid = 2;
+ } else {
+ debug("attribute userid is not #1, unmarking #1 for deletion.");
+ readwrite_gpg("1\n", $inputfd, $stdoutfd, $stderrfd, $statusfd, exitwhenstatusmatches => $KEYEDIT_PROMPT, nocloseinput => 1);
+ $delete_some--;
+ $prune_some_sigs_on_uid = 2;
+ $prune_all_sigs_on_uid = 1;
+ };
+ } else {
+ $prune_some_sigs_on_uid = 1;
+ };
+
if ($delete_some) {
- debug("need to delete a few uids.");
+ debug("need to delete $delete_some uids.");
readwrite_gpg("deluid\n", $inputfd, $stdoutfd, $stderrfd, $statusfd, exitwhenstatusmatches => $KEYEDIT_DELUID_PROMPT, nocloseinput => 1);
readwrite_gpg("yes\n", $inputfd, $stdoutfd, $stderrfd, $statusfd, exitwhenstatusmatches => $KEYEDIT_PROMPT, nocloseinput => 1);
};
# delete signatures
###################
- my $signed_by_me = 0;
- readwrite_gpg("1\n", $inputfd, $stdoutfd, $stderrfd, $statusfd, exitwhenstatusmatches => $KEYEDIT_PROMPT, nocloseinput => 1);
- ($stdout, $stderr, $status) =
- readwrite_gpg("delsig\n", $inputfd, $stdoutfd, $stderrfd, $statusfd, exitwhenstatusmatches => $KEYEDIT_DELSIG_PROMPT, nocloseinput => 1);
-
- while($status =~ /$KEYEDIT_DELSIG_PROMPT/m) {
- # sig:?::17:EA2199412477CAF8:1058095214:::::13x:
- my @sigline = grep { /^sig/ } (split /\n/, $stdout);
- $stdout =~ s/\n/\\n/g;
- notice("[sigremoval] why are there ".(scalar @sigline)." siglines in that part of the dialog!? got: $stdout") if scalar @sigline >= 2; # XXX
- my $line = pop @sigline;
- my $answer = "no";
- if (defined $line) { # only if we found a sig here - we never remove revocation packets for instance
- debug("[sigremoval] doing line $line.");
- my (undef, undef, undef, undef, $signer, $created, undef, undef, undef) = split /:/, $line;
- if ($signer eq $longkeyid) {
- debug("[sigremoval] selfsig ($signer).");
- $answer = "no";
- } elsif (grep { $signer eq $_ } @{$CONFIG{'keyid'}}) {
- debug("[sigremoval] signed by us ($signer).");
- $answer = "no";
- $signed_by_me = $signed_by_me > $created ? $signed_by_me : $created;
- } else {
- debug("[sigremoval] not interested in that sig ($signer).");
- $answer = "yes";
- };
- } else {
- debug("[sigremoval] no sig line here, only got: ".$stdout);
- };
- ($stdout, $stderr, $status) =
- readwrite_gpg($answer."\n", $inputfd, $stdoutfd, $stderrfd, $statusfd, exitwhenstatusmatches => $KEYEDIT_KEYEDIT_OR_DELSIG_PROMPT, nocloseinput => 1);
+ readwrite_gpg("$prune_some_sigs_on_uid\n", $inputfd, $stdoutfd, $stderrfd, $statusfd, exitwhenstatusmatches => $KEYEDIT_PROMPT, nocloseinput => 1); # mark uid for delsig
+ my $signed_by_me = delete_signatures($inputfd, $stdoutfd, $stderrfd, $statusfd, $longkeyid, $CONFIG{'keyid'});
+ readwrite_gpg("$prune_some_sigs_on_uid\n", $inputfd, $stdoutfd, $stderrfd, $statusfd, exitwhenstatusmatches => $KEYEDIT_PROMPT, nocloseinput => 1); # unmark uid from delsig
+ if (defined $prune_all_sigs_on_uid) {
+ readwrite_gpg("$prune_all_sigs_on_uid\n", $inputfd, $stdoutfd, $stderrfd, $statusfd, exitwhenstatusmatches => $KEYEDIT_PROMPT, nocloseinput => 1); # mark uid for delsig
+ delete_signatures($inputfd, $stdoutfd, $stderrfd, $statusfd, $longkeyid, []);
+ readwrite_gpg("$prune_all_sigs_on_uid\n", $inputfd, $stdoutfd, $stderrfd, $statusfd, exitwhenstatusmatches => $KEYEDIT_PROMPT, nocloseinput => 1); # unmark uid from delsig
};
+
+
readwrite_gpg("save\n", $inputfd, $stdoutfd, $stderrfd, $statusfd);
waitpid $pid, 0;
print KEY $asciikey;
close KEY;
- push @UIDS, { text => $this_uid_text, key => $asciikey, serial => $uid_number };
+ push @UIDS, { text => $this_uid_text, key => $asciikey, serial => $uid_number, "is_uat" => $is_uat };
info("$longkeyid $uid_number $this_uid_text done.");
} else {
my @attached;
for my $uid (@UIDS) {
trace("UID: $uid->{'text'}\n");
- unless ($uid->{'text'} =~ /@/) {
+ if ($uid->{'is_uat'}) {
+ my $attach = ask("UID $uid->{'text'} is an attribute UID, attach it to every email sent?", 1);
+ push @attached, $uid if $attach;
+ } elsif ($uid->{'text'} !~ /@/) {
my $attach = ask("UID $uid->{'text'} is no email address, attach it to every email sent?", 1);
push @attached, $uid if $attach;
};
notice("Key has no encryption capabilities, mail will be sent unencrypted") unless $can_encrypt;
for my $uid (@UIDS) {
- if ($uid->{'text'} =~ /@/) {
+ if (!$uid->{'is_uat'} && ($uid->{'text'} =~ /@/)) {
my $address = $uid->{'text'};
$address =~ s/.*<(.*)>.*/$1/;
if ($CONFIG{'mail'} or ask("Send mail to '$address' for $uid->{'text'}?", 1)) {
};
};
-
-
-
-
-###############################################################3
-#### old fork gpg --edit
-=cut
- my ($stdin_read, $stdin_write);
- my ($stdout_read, $stdout_write);
- my ($stderr_read, $stderr_write);
- my ($status_read, $status_write);
- pipe $stdin_read, $stdin_write;
- pipe $stdout_read, $stdout_write;
- pipe $stderr_read, $stderr_write;
- pipe $status_read, $status_write;
-
- $pid = fork();
- unless ($pid) { # child
- close $stdin_write;
- close $stdout_read;
- close $stderr_read;
- close $status_read;
-
- my @call;
- push @call, $CONFIG{'gpg-delsig'};
- push @call, "--homedir=$tempdir";
- push @call, '--with-colons';
- push @call, '--fixed-list-mode';
- push @call, '--command-fd=0';
- push @call, "--status-fd=".fileno($status_write);
- push @call, "--no-tty";
- push @call, "--edit";
- push @call, $keyid;
-
- close STDIN;
- close STDOUT;
- close STDERR;
- open (STDIN, "<&".fileno($stdin_read)) or die ("Cannot reopen stdin: $!\n");
- open (STDOUT, ">&".fileno($stdout_write)) or die ("Cannot reopen stdout: $!\n");
- open (STDERR, ">&".fileno($stderr_write)) or die ("Cannot reopen stderr: $!\n");
-
- fcntl $status_write, F_SETFD, 0;
-
- exec (@call);
- exit;
- };
- close $stdin_read;
- close $stdout_write;
- close $stderr_write;
- close $status_write;
-
- $inputfd = $stdin_write;
- $stdoutfd = $stdout_read;
- $stderrfd = $stderr_read;
- $statusfd = $status_read;
-=cut