Category: E-Mail Programs
Author/Contact Info nothingmuch@woobling.org
Description: This wad of code reads text on stdin, thought to contain references to public keys, in the form of URLs, hex numbers (0xdecaf), and optionally emails (i can't test them).

PGP::FindKey was broken for my keyservers, and I can't install Crypt::OpenPGP::KeyServer, so emails are only theoretically available.

The code outputs key blocks on STDOUT.

wrap it in something like

:0h |gpg-key-downloader | gpg --import
or if you don't want to suffer the delay, have it slurp it's input, daemonise (as per perlipc), and pipe to gpg itself.

Updates: removed a proxy env set, and detabbed, formatting, description (sorry, I thought i hit a preview button), added comments

#!/usr/bin/perl

use strict;
use warnings;

my ($keyserver, $getEmails);

BEGIN {
    $keyserver = shift || 'subkeys.pgp.net:11371';  # the keyserver to
+ use
    $getEmails = undef;                             # wether to get em
+ails or not
}

$, = $\ = $/; # ;-)

use LWP::Simple;
use URI::Find;

use if $getEmails => 'Email::Find';
use if $getEmails => 'PGP::FindKey'; # mainly compile time death
BEGIN {
    unless (eval { require Crypt::OpenPGP::KeyServer }){ # getting ugl
+y
        
        ## the following emulates Crypt::OpenPGP::Keyserver, in a not 
+so pretty way.
        
        *Crypt::OpenPGP::KeyServer::new = sub { # create a new object
            my $pkg = shift;
            my %conf = @_;
            bless \$conf{Server}, $pkg; # fill it only with the keyser
+ver setting
        };
        
        *Crypt::OpenPGP::KeyServer::find_keyblock_by_uid = sub { # fet
+ch a key via uid - wraps around PGP::FindKey - not much fun.
            my $self = shift;
            my $str = shift;
            my $id = PGP::FindKey->new(
                keyserver   => $keyserver,
                address     => $str,
            )->result;
            
            $self->find_keyblock_by_keyid(pack("H*",$id)); # "find" th
+ey key ID. PGP::FindKey only gives back one key
        };
        
        *Crypt::OpenPGP::KeyServer::find_keyblock_by_keyid = sub { # f
+etch a key via it's id - constructs a simple URL
            my $self = shift;
            my $id = unpack("H*", shift);
            
            my $url = "http://" . $$self . '/pks/lookup?op=get&search=
+0x' . $id; # the URL we'll be fetching
            
            filter_key_blocks(LWP::Simple::get($url)); # give back an 
+array or string of key blocks, made from the return value of the HTTP
+ get
        };
    }
}

my $kbs = Crypt::OpenPGP::KeyServer->new(
    Server => $keyserver,
);

my @finders = ( # the general interface is the same, so we've grouped 
+them

    URI::Find->new(sub { # to find URLS
        print filter_key_blocks(get(shift)); # get the URL using LWP::
+Simple, and filter key blocks out of it
        return shift;
    }),

    ($getEmails ? Email::Find->new(sub { # to find emails, if at all
        my $emails = shift;
        foreach my $email (@$emails){ print $kbs->find_keyblock_by_uid
+($email) || '' }; # run the list of emails through the key server obj
+ect, and print the results out
        return shift;
    }) : () ),
    
);

sub get_id { # a shortcut to print a key by it's ID
    print $kbs->find_keyblock_by_keyid(pack 'H*', shift) || '';
}

sub filter_key_blocks {
    my $str = shift;
    my @blocks = $str =~ /(-----BEGIN PGP PUBLIC KEY BLOCK-----.*?----
+-END PGP PUBLIC KEY BLOCK-----)/sg; # get all the blocks, nothing mor
+e
    return wantarray ? @blocks : join($/, @blocks); # join if scalar c
+ontext
}

while (<>){
    #study;
    foreach my $uid (/\b(?:0x)((?:[a-fA-F0-9]{8}){1,2})\b/g){ get_id($
+uid) }; # matches ID
    foreach my $finder (@finders) { $finder->find(\$_) }; # run the ca
+nned matches on the input
}