File Coverage

blib/lib/WWW/HKP.pm
Criterion Covered Total %
statement 51 94 54.2
branch 27 46 58.7
condition 17 48 35.4
subroutine 9 16 56.2
pod 4 4 100.0
total 108 208 51.9


line stmt bran cond sub pod time code
1             package WWW::HKP;
2              
3 2     2   29195 use warnings;
  2         4  
  2         114  
4 2     2   13 use strict;
  2         4  
  2         96  
5 2     2   12 use Carp;
  2         8  
  2         208  
6 2     2   56 use 5.010;
  2         7  
  2         188  
7              
8 2     2   12290 use LWP::UserAgent 6.05;
  2         171063  
  2         81  
9 2     2   27 use URI 1.60;
  2         37  
  2         53  
10 2     2   11 use URI::Escape 3.31;
  2         38  
  2         3205  
11              
12             =head1 NAME
13              
14             WWW::HKP - Interface to HTTP Keyserver Protocol (HKP)
15              
16             =head1 VERSION
17              
18             Version 0.02
19              
20             =cut
21              
22             our $VERSION = '0.02';
23              
24              
25             =head1 SYNOPSIS
26              
27             use WWW::HKP;
28            
29             my $hkp = WWW::HKP->new();
30            
31             $hkp->query(index => 'foo@bar.baz');
32             $hkp->query(get => 'DEADBEEF');
33              
34             =head1 DESCRIPTION
35              
36             This module implements the IETF draft of the OpenPGP HTTP Keyserver Protocol.
37              
38             More Informationen about HKP is available at L.
39              
40             =head1 FUNCTIONS
41              
42             =head2 new([%options])
43              
44             The C constructor method instanciates a new C object. The following example shows available options and its default values.
45              
46             my $hkp = WWW::HKP->new(
47             host => 'localhost',
48             port => 11371
49             );
50              
51             In most cases you just need to set the I parameter:
52              
53             my $hkp = new WWW::HKP host => 'pool.sks-keyservers.net';
54              
55             =cut
56              
57             sub new($;%) {
58 2     2 1 731 my ($class, %options) = @_;
59            
60 2         40 my $uri = URI->new('http:');
61 2   50     25581 $uri->host($options{host} || 'localhost');
62 2   50     594 $uri->port($options{port} || 11371);
63            
64 2         139 my $ua = LWP::UserAgent->new;
65 2         8023 $ua->agent(__PACKAGE__.'/'.$VERSION);
66            
67 2         163 my $self = {
68             ua => $ua,
69             uri => $uri,
70             };
71            
72 2   33     47 return bless $self => (ref $class || $class);
73             }
74              
75 0     0   0 sub _ua($) { shift->{ua} }
76 0     0   0 sub _uri($) { shift->{uri} }
77              
78             sub _get($$) {
79 0     0   0 my ($self, %query) = @_;
80 0         0 $self->{error} = undef;
81 0         0 $self->_uri->path('/pks/lookup');
82 0         0 $self->_uri->query_form(%query);
83 0         0 my $response = $self->_ua->get($self->_uri);
84 0 0 0     0 if (defined $response and ref $response and $response->isa('HTTP::Response') and $response->is_success) {
      0        
      0        
85 0         0 return $response->decoded_content;
86             } else {
87 0         0 $self->{error} = $response->status_line;
88 0         0 return undef;
89             }
90             }
91              
92             sub _post($%) {
93 0     0   0 my ($self, %query) = @_;
94 0         0 $self->{error} = undef;
95 0         0 $self->_uri->path('/pks/lookup');
96 0         0 my $response = $self->_ua->post($self->_uri, \%query);
97 0 0 0     0 if (defined $response and ref $response and $response->isa('HTTP::Response') and $response->is_success) {
      0        
      0        
98 0         0 return $response->decoded_content;
99             } else {
100 0         0 $self->{error} = $response->status_line;
101 0         0 return undef;
102             }
103              
104             }
105              
106             sub _parse_mr($$$) {
107 3     3   2501 my ($self, $lines, $filter_ok) = @_;
108 3         5 my $keys = {};
109 3         5 my $key;
110 3         5 my ($keyc, $keyn) = (0, 0);
111 3         24 foreach my $line (split /\r?\n/ => $lines) {
112 10 100       71 if ($line =~ /^info:(\d+):(\d+)$/) {
    100          
    50          
113 3 50       12 croak "unsupported hkp version: v$1" unless $1 == 1;
114 3         8 $keyc = $2;
115             } elsif ($line =~ /^pub:([0-9a-f]{8,16}):(\d*):(\d*):(\d*):(\d*):([der]*)$/i) {
116 3         4 $key = $1;
117 3         4 $keyn++;
118 3         13 my ($algo, $keylen, $created, $expires, $flags, $ok) = ($2, $3, $4, $5, $6, undef);
119 3 100 66     57 $ok = ((($created and $created > time) or ($expires and $expires < time) or (length $flags)) ? 0 : 1);
120 3 50 66     12 if ($filter_ok and !$ok) {
121 0         0 $key = undef;
122 0         0 next;
123             }
124 3 100 50     38 $keys->{$key} = {
    100 50        
    100          
125             algo => $algo,
126             keylen => $keylen,
127             created => $created || undef,
128             expires => $expires || undef,
129             revoked => ($flags =~ /r/ ? 1 : 0),
130             expired => ($flags =~ /e/ ? 1 : 0),
131             deleted => ($flags =~ /d/ ? 1 : 0),
132             ok => $ok,
133             uids => []
134             };
135             } elsif ($line =~ /^uid:([^:]*):(\d*):(\d*):([der]*)$/i) {
136 4 50       8 next unless defined $key;
137 4         12 my ($uid, $created, $expires, $flags, $ok) = ($1, $2, $3, $4, undef);
138 4 100 100     44 $ok = ((($created and $created > time) or ($expires and $expires < time) or (length $flags)) ? 0 : 1);
139 4 100 100     22 next if $filter_ok and !$ok;
140 2 100 50     3 push @{ $keys->{$key}->{uids} } => {
  2 100 50     11  
    100          
141             uid => uri_unescape($uid),
142             created => $created || undef,
143             expires => $expires || undef,
144             revoked => ($flags =~ /r/ ? 1 : 0),
145             expired => ($flags =~ /e/ ? 1 : 0),
146             deleted => ($flags =~ /d/ ? 1 : 0),
147             ok => $ok
148             };
149             } else {
150 0         0 carp "unknown line: $line";
151             }
152             }
153 3 50       48 carp "server said there where $keyc keys, but $keyn keys parsed" unless $keyc == $keyn;
154 3         15 return $keys;
155             }
156              
157             =head2 query($type => $search [, %options ])
158              
159             The C method implements both query operations of HKP: I and I
160              
161             =cut
162              
163             sub query($$$;%) {
164 0     0 1   my ($self, $type, $search, %options) = @_;
165 0           given ($type) {
166              
167             =head3 I operation
168              
169             $hkp->query(index => 'foo@bar.baz');
170              
171             The first parameter must be I, the secondend parameter an email-address or key-id.
172              
173             If any keys where found, a hashref is returned. Otherwise returns undef, an error message can be fetched with C<< $hkp->error() >>.
174              
175             The returned hashref may look like this:
176              
177             {
178             'DEADBEEF' => {
179             'algo' => '1',
180             'keylen' => '2048',
181             'created' => '1253025510',
182             'expires' => '1399901151',
183             'deleted' => 0,
184             'expired' => 0,
185             'revoked' => 0,
186             'ok' => 1,
187             'uids' => [
188             {
189             'uid' => 'Lorem Ipsum (This is an example) '
190             'created' => '1253025510',
191             'expires' => '1399901151',
192             'deleted' => 0,
193             'expired' => 0,
194             'revoked' => 0,
195             'ok' => 1
196             }
197             ]
198             }
199             }
200              
201             The keys of the hashref are key-ids. The meaning of the hashkeys in the seconded level:
202              
203             =over
204              
205             =item I
206              
207             The algorithm of the key. The values are described in RFC 2440
208              
209             =item I
210              
211             The key length in bytes
212              
213             =item I
214              
215             Creation date of the key, in seconds since 1970-01-01 UTC.
216              
217             =item I
218              
219             Expiration date of the key
220              
221             =item I, I, I
222              
223             Indication details, whether the key is deleted, expired or revoked. If the flag is that, the value is C<1>, otherwise C<0>.
224              
225             =item I
226              
227             The creation date and expiration date is checked against C. If it doesn't match or any of the flags obove are set, I will be C<0>, otherwise C<1>.
228              
229             =item I
230              
231             A arrayref of user-ids.
232              
233             =over
234              
235             =item I
236              
237             The user-id in common format. It can be parsed by L for example.
238              
239             =item I, I, I, I, I, I
240              
241             This fields have the same meaning as described above. The information is taken from the self-signature, if any. I and I may be C if not available (e.g. empty string).
242              
243             =back
244              
245             =back
246              
247             =head4 Available options
248              
249             =over
250              
251             =item I
252              
253             Set the I parameter to C<1> (or any expression that evaluates to true), if you want an exact match of your search expression.
254              
255             =item I
256              
257             Set the I parameter to C<1> (or any expression that evaluates to true), if you want only valid results. All keys or user IDs having I-parameter of C<0> are ignored.
258              
259             $hkp->query(index => 'foo@bar.baz', filter_ok => 1);
260              
261             =back
262              
263             =cut
264              
265 0           when ('index') {
266 0           my @options = qw(mr);
267 0 0         push @options => 'exact' if $options{exact};
268 0           my $message = $self->_get(op => 'index', options => join(',' => @options), search => $search);
269 0 0         return undef unless defined $message;
270 0 0         return $self->_parse_mr($message, $options{filter_ok} ? 1 : 0);
271             }
272              
273             =head3 I operation
274              
275             $hkp->query(get => 'DEADBEEF');
276              
277             The operation returns the public key of specified key-id or undef, if not found. Any error messages can be fetched with C<< $hkp->error() >>.
278              
279             =cut
280              
281 0           when ('get') {
282 0 0         if ($search !~ /^0x/) {
283 0           $search = '0x'.$search;
284             }
285 0           my $message = $self->_get(op => 'get', options => 'exact', search => $search);
286 0 0         return undef unless defined $message;
287 0           return $message;
288             }
289              
290             =head3 unimplemented operations
291              
292             A HKP server may implement various other operations. Unimplemented operation cause the module to die with a stack trace.
293              
294             =cut
295              
296 0           default {
297 0           confess "unknown query type '$type'";
298             }
299             }
300             }
301              
302             =head2 submit
303              
304             Submit one or more ASCII-armored version of public keys to the server.
305              
306             $pubkey = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n...";
307            
308             $hkp->submit($pubkey);
309            
310             @pubkeys = ($pubkey1, $pubkey2, ...);
311              
312             $hkp->submit(@pubkeys);
313              
314             In case of success, C<1> is returned. Otherweise C<0> and an error message can be fetched from C<$hkp->error>.
315              
316             =cut
317              
318             sub submit($@) {
319 0     0 1   my ($self, @keys) = @_;
320 0           my $status = $self->_post(map {( keytext => $_ )} @keys);
  0            
321 0   0       return (defined $status and $status ? 1 : 0);
322             }
323              
324             =head2 error
325              
326             Returns last error message, if any.
327              
328             $hkp->error; # "404 Not found", for example.
329              
330             =cut
331              
332 0     0 1   sub error($) { shift->{error} }
333              
334             =head1 AUTHOR
335              
336             David Zurborg, C<< >>
337              
338             =head1 BUGS
339              
340             Please report any bugs or feature requests trough my project management tool at L. I will be notified, and then you'll
341             automatically be notified of progress on your bug as I make changes.
342              
343             =head1 SUPPORT
344              
345             You can find documentation for this module with the perldoc command.
346              
347             perldoc WWW::HKP
348              
349             You can also look for information at:
350              
351             =over 4
352              
353             =item * Redmine: Homepage of this module
354              
355             L
356              
357             =item * RT: CPAN's request tracker
358              
359             L
360              
361             =item * AnnoCPAN: Annotated CPAN documentation
362              
363             L
364              
365             =item * CPAN Ratings
366              
367             L
368              
369             =item * Search CPAN
370              
371             L
372              
373             =back
374              
375             =head1 COPYRIGHT & LICENSE
376              
377             Copyright 2014 David Zurborg, all rights reserved.
378              
379             This program is not really free software; you can redistribute it and/or modify it under certain circumstances. See file F for details.
380              
381             =cut
382              
383             1; # End of WWW::HKP