=over
-=item B<caff> [-mMR] [-u I<yourkeyid>] I<keyid> [I<keyid> ..]
+=item B<caff> [-eEmMRS] [-u I<yourkeyid>] I<keyid> [I<keyid> ..]
=back
=over
-=item B<-m>, B<--mail>
+=item B<-e>, B<--export>, B<-E>, B<--no-export>
-Send mail after signing. Default is to ask the user for each uid.
+Export/do not export old signatures. Default is to ask the user for each old
+signature.
-=item B<-M>, B<--no-mail>
+=item B<-m>, B<--mail>, B<-M>, B<--no-mail>
-Do not send mail after signing. Default is to ask the user for each uid.
+Send/do not send mail after signing. Default is to ask the user for each uid.
=item B<-R>, B<--no-download>
Do not retrieve the key to be signed from a keyserver.
+=item B<-S>, B<--no-sign>
+
+Do not sign the keys.
+
=item B<-u> I<yourkeyid>, B<--local-user> I<yourkeyid>
Select the key that is used for signing, in case you have more than one key.
An additional keyid to encrypt messages to. Default: none.
+=item B<gpg-sign-args> [string]
+
+Additional arguments to pass to gpg. Default: none.
+
=head2 Keyserver settings
=item B<keyserver> [string]
=item B<mail-template> [string]
-Email template which is used as the body text for the email sent out.
+Email template which is used as the body text for the email sent out
instead of the default text if specified. The following perl variables
can be used in the template:
return ($stdout, $stderr, $status);
};
-sub ask($$) {
- my ($question, $default) = @_;
+sub ask($$;$$) {
+ my ($question, $default, $forceyes, $forceno) = @_;
+ return $default if $forceyes and $forceno;
+ return 1 if $forceyes;
+ return 0 if $forceno;
my $answer;
while (1) {
print $question,' ',($default ? '[Y/n]' : '[y/N]'), ' ';
sub usage($$) {
my ($fd, $exitcode) = @_;
version($fd);
- print $fd "Usage: $PROGRAM_NAME [-mMR] [-u <yourkeyid>] <keyid> [<keyid> ...]\n";
+ print $fd "Usage: $PROGRAM_NAME [-eEmMRS] [-u <yourkeyid>] <keyid> [<keyid> ...]\n";
print $fd "Consult the manual page for more information.\n";
exit $exitcode;
};
'-V' => \$params->{'version'},
'-u=s' => \$params->{'local-user'},
'--local-user=s' => \$params->{'local-user'},
+ '-e' => \$params->{'export'},
+ '--export' => \$params->{'export'},
+ '-E' => \$params->{'no-export'},
+ '--no-export' => \$params->{'no-export'},
'-m' => \$params->{'mail'},
'--mail' => \$params->{'mail'},
'-M' => \$params->{'no-mail'},
'--no-mail' => \$params->{'no-mail'},
'-R' => \$params->{'no-download'},
'--no-download' => \$params->{'no-download'},
+ '-S' => \$params->{'no-sign'},
+ '--no-sign' => \$params->{'no-sign'},
)) {
usage(\*STDERR, 1);
};
if ($params->{'local-user'}) {
$USER = $params->{'local-user'};
$USER =~ s/^0x//i;
- unless ($USER =~ /^[A-Za-z0-9]{8,8}([A-Za-z0-9]{8})?$/) {
+ unless ($USER =~ /^([A-Z0-9]{8}|[A-Z0-9]{16}|[A-Z0-9]{32}|[A-Z0-9]{40})$/i) {
print STDERR "-u $USER is not a keyid.\n";
usage(\*STDERR, 1);
};
for my $keyid (@ARGV) {
$keyid =~ s/^0x//i;
- unless ($keyid =~ /^[A-Za-z0-9]{8}([A-Za-z0-9]{8}|[A-Za-z0-9]{32})?$/) {
+ unless ($keyid =~ /^([A-Z0-9]{8}|[A-Z0-9]{16}|[A-Z0-9]{32}|[A-Z0-9]{40})$/i) {
print STDERR "$keyid is not a keyid.\n";
usage(\*STDERR, 1);
};
$CONFIG{'no-download'} = $params->{'no-download'} if defined $params->{'no-download'};
$CONFIG{'no-mail'} = $params->{'no-mail'} if defined $params->{'no-mail'};
$CONFIG{'mail'} = $params->{'mail'} if defined $params->{'mail'};
+$CONFIG{'no-sign'} = $params->{'no-sign'} if defined $params->{'no-sign'};
#################
# receive keys from keyserver
#############################
my @keyids_ok;
-my @keyids_failed;
if ($CONFIG{'no-download'}) {
@keyids_ok = @KEYIDS;
} else {
+ info ("fetching keys, this will take a while...");
+ if (grep { /^[A-Z0-9]{32}$/ } @KEYIDS) {
+ info ("found v3 key fingerprints in argument list - note that HKP keyservers do not support retrieving v3 keys by fingerprint");
+ }
+
my $gpg = GnuPG::Interface->new();
$gpg->call( $CONFIG{'gpg'} );
$gpg->options->hash_init(
'extra_args' => '--keyserver='.$CONFIG{'keyserver'} );
$gpg->options->meta_interactive( 0 );
my ($inputfd, $stdoutfd, $stderrfd, $statusfd, $handles) = make_gpg_fds();
-
- my @local_keyids = @KEYIDS;
- for my $keyid (@local_keyids) {
- info ("fetching $keyid...");
- my $pid = $gpg->recv_keys(handles => $handles, command_args => [ $keyid ]);
- my ($stdout, $stderr, $status) = readwrite_gpg('', $inputfd, $stdoutfd, $stderrfd, $statusfd);
- waitpid $pid, 0;
+ my $pid = $gpg->recv_keys(handles => $handles, command_args => [ @KEYIDS ]);
+ my ($stdout, $stderr, $status) = readwrite_gpg('', $inputfd, $stdoutfd, $stderrfd, $statusfd);
+ waitpid $pid, 0;
# [GNUPG:] IMPORT_OK 0 5B00C96D5D54AEE1206BAF84DE7AAF6E94C09C7F
# [GNUPG:] NODATA 1
# [GNUPG:] NODATA 1
# [GNUPG:] IMPORT_OK 0 25FC1614B8F87B52FF2F99B962AF4031C82E0039
- my $handled = 0;
- for my $line (split /\n/, $status) {
- if ($line =~ /^\[GNUPG:\] IMPORT_OK \d+ ([0-9A-F]{40})/) {
- my $imported_key = $1;
- if ($keyid ne $imported_key &&
- $keyid ne substr($imported_key, -16) &&
- $keyid ne substr($imported_key, -8)) {
- warn("Imported unexpected key. expected: $keyid; got: $imported_key.\n");
- next;
- };
- push @keyids_ok, $keyid;
- shift @KEYIDS;
- $handled = 1;
- last;
- } elsif ($line =~ /^\[GNUPG:\] NODATA/) {
- push @keyids_failed, $keyid;
- shift @KEYIDS;
- $handled = 1;
- last;
+ my %local_keyids = map { $_ => 1 } @KEYIDS;
+ for my $line (split /\n/, $status) {
+ if ($line =~ /^\[GNUPG:\] IMPORT_OK \d+ ([0-9A-F]{8})([0-9A-F]{16})([0-9A-F]{8})([0-9A-F]{0,8})/) {
+ my $imported_key;
+ $imported_key = $1.$2.$3 if $local_keyids{$1.$2.$3}; # v3 key
+ $imported_key = $1.$2.$3.$4 if $local_keyids{$1.$2.$3.$4};
+ $imported_key = $3.$4 if $local_keyids{ $3.$4};
+ $imported_key = $4 if $local_keyids{ $4};
+ unless ($imported_key) {
+ notice ("Imported unexpected key; got: $1$2$3$4. (This is normal for v3 keys.)\n");
+ next;
};
- };
- unless ($handled) {
- notice ("Huh, what's up with $keyid?");
- push @keyids_failed, $keyid;
- shift @KEYIDS;
- };
+ debug ("Imported $imported_key");
+ delete $local_keyids{$imported_key};
+ unshift @keyids_ok, $imported_key;
+ } elsif ($line =~ /^\[GNUPG:\] (NODATA|IMPORT_RES|IMPORTED) /) {
+ } else {
+ notice ("got unknown reply from gpg: $line");
+ }
};
- die ("Still keys in \@KEYIDS. This should not happen.") if scalar @KEYIDS;
- notice ("Import failed for: ". (join ' ', @keyids_failed).".") if scalar @keyids_failed;
+ if (scalar %local_keyids) {
+ notice ("Import failed for: ". (join ' ', keys %local_keyids).".");
+ exit 1 unless ask ("Some keys could not be imported - continue anyway?", 0);
+ }
};
+unless (@keyids_ok) {
+ notice ("No keys to sign found");
+ exit 0;
+}
+
###########
# sign keys
###########
push @command, '--local-user', $USER if (defined $USER);
push @command, "--homedir=$GNUPGHOME";
push @command, '--secret-keyring', $CONFIG{'secret-keyring'};
+ push @command, split ' ', $CONFIG{'gpg-sign-args'} || "";
push @command, '--edit', $keyid;
push @command, 'sign';
+ push @command, 'save';
print join(' ', @command),"\n";
system (@command);
};
if ($signed_by_me) {
if ($NOW - $signed_by_me > $CONFIG{'export-sig-age'} ) {
- my $write = ask("Signature on $this_uid_text is old. Export?", 0);
+ my $write = ask("Signature on $this_uid_text is old. Export?", 0, $params->{export}, $params->{'no-export'});
next unless $write;
};
my $keydir = "$KEYSBASE/$DATE_STRING";
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)) {
+ if (ask("Send mail to '$address' for $uid->{'text'}?", 1, $CONFIG{'mail'})) {
my $mail = send_mail($address, $can_encrypt, $longkeyid, $uid, @attached);
my $keydir = "$KEYSBASE/$DATE_STRING";