File Coverage

blib/lib/WWW/HKP.pm
Criterion Covered Total %
statement 51 91 56.0
branch 27 50 54.0
condition 17 48 35.4
subroutine 9 16 56.2
pod 4 4 100.0
total 108 209 51.6


line stmt bran cond sub pod time code
1 2     2   17516 use strict;
  2         4  
  2         85  
2 2     2   11 use warnings;
  2         4  
  2         66  
3             package WWW::HKP;
4             # ABSTRACT: Interface to HTTP Keyserver Protocol (HKP)
5              
6 2     2   8 use Carp;
  2         3  
  2         124  
7 2     2   49 use 5.010;
  2         6  
  2         66  
8 2     2   15556 use LWP::UserAgent 6.05;
  2         90450  
  2         77  
9 2     2   16 use URI 1.60;
  2         35  
  2         57  
10 2     2   10 use URI::Escape 3.31;
  2         37  
  2         2080  
11              
12             our $VERSION = '0.04'; # VERSION
13              
14              
15             sub new($;%) {
16 2     2 1 811 my ($class, %options) = @_;
17            
18 2         16 my $uri = URI->new('http:');
19 2   50     13067 $uri->host($options{host} || 'localhost');
20 2   50     469 $uri->port($options{port} || 11371);
21            
22 2         100 my $ua = LWP::UserAgent->new;
23 2         5654 $ua->agent(__PACKAGE__.'/'.$VERSION);
24            
25 2         121 my $self = {
26             ua => $ua,
27             uri => $uri,
28             };
29            
30 2   33     31 return bless $self => (ref $class || $class);
31             }
32              
33 0     0   0 sub _ua($) { shift->{ua} }
34 0     0   0 sub _uri($) { shift->{uri} }
35              
36             sub _get($$) {
37 0     0   0 my ($self, %query) = @_;
38 0         0 $self->{error} = undef;
39 0         0 $self->_uri->path('/pks/lookup');
40 0         0 $self->_uri->query_form(%query);
41 0         0 my $response = $self->_ua->get($self->_uri);
42 0 0 0     0 if (defined $response and ref $response and $response->isa('HTTP::Response') and $response->is_success) {
      0        
      0        
43 0         0 return $response->decoded_content;
44             } else {
45 0         0 $self->{error} = $response->status_line;
46 0         0 return undef;
47             }
48             }
49              
50             sub _post($%) {
51 0     0   0 my ($self, %query) = @_;
52 0         0 $self->{error} = undef;
53 0         0 $self->_uri->path('/pks/lookup');
54 0         0 my $response = $self->_ua->post($self->_uri, \%query);
55 0 0 0     0 if (defined $response and ref $response and $response->isa('HTTP::Response') and $response->is_success) {
      0        
      0        
56 0         0 return $response->decoded_content;
57             } else {
58 0         0 $self->{error} = $response->status_line;
59 0         0 return undef;
60             }
61              
62             }
63              
64             sub _parse_mr($$$) {
65 3     3   3128 my ($self, $lines, $filter_ok) = @_;
66 3         5 my $keys = {};
67 3         3 my $key;
68 3         4 my ($keyc, $keyn) = (0, 0);
69 3         27 foreach my $line (split /\r?\n/ => $lines) {
70 10 100       64 if ($line =~ /^info:(\d+):(\d+)$/) {
    100          
    50          
71 3 50       12 croak "unsupported hkp version: v$1" unless $1 == 1;
72 3         7 $keyc = $2;
73             } elsif ($line =~ /^pub:([0-9a-f]{8,16}):(\d*):(\d*):(\d*):(\d*):([der]*)$/i) {
74 3         5 $key = $1;
75 3         4 $keyn++;
76 3         10 my ($algo, $keylen, $created, $expires, $flags, $ok) = ($2, $3, $4, $5, $6, undef);
77 3 100 66     48 $ok = ((($created and $created > time) or ($expires and $expires < time) or (length $flags)) ? 0 : 1);
78 3 50 66     12 if ($filter_ok and !$ok) {
79 0         0 $key = undef;
80 0         0 next;
81             }
82 3 100 50     34 $keys->{$key} = {
    100 50        
    100          
83             algo => $algo,
84             keylen => $keylen,
85             created => $created || undef,
86             expires => $expires || undef,
87             revoked => ($flags =~ /r/ ? 1 : 0),
88             expired => ($flags =~ /e/ ? 1 : 0),
89             deleted => ($flags =~ /d/ ? 1 : 0),
90             ok => $ok,
91             uids => []
92             };
93             } elsif ($line =~ /^uid:([^:]*):(\d*):(\d*):([der]*)$/i) {
94 4 50       9 next unless defined $key;
95 4         11 my ($uid, $created, $expires, $flags, $ok) = ($1, $2, $3, $4, undef);
96 4 100 100     36 $ok = ((($created and $created > time) or ($expires and $expires < time) or (length $flags)) ? 0 : 1);
97 4 100 100     21 next if $filter_ok and !$ok;
98 2 100 50     2 push @{ $keys->{$key}->{uids} } => {
  2 100 50     13  
    100          
99             uid => uri_unescape($uid),
100             created => $created || undef,
101             expires => $expires || undef,
102             revoked => ($flags =~ /r/ ? 1 : 0),
103             expired => ($flags =~ /e/ ? 1 : 0),
104             deleted => ($flags =~ /d/ ? 1 : 0),
105             ok => $ok
106             };
107             } else {
108 0         0 carp "unknown line: $line";
109             }
110             }
111 3 50       40 carp "server said there where $keyc keys, but $keyn keys parsed" unless $keyc == $keyn;
112 3         14 return $keys;
113             }
114              
115              
116             sub query($$$;%) {
117 0     0 1   my ($self, $type, $search, %options) = @_;
118              
119              
120 0 0         if ($type eq 'index') {
    0          
121 0           my @options = qw(mr);
122 0 0         push @options => 'exact' if $options{exact};
123 0           my $message = $self->_get(op => 'index', options => join(',' => @options), search => $search);
124 0 0         return undef unless defined $message;
125 0 0         return $self->_parse_mr($message, $options{filter_ok} ? 1 : 0);
126             }
127              
128              
129             elsif ($type eq 'get') {
130 0 0         if ($search !~ /^0x/) {
131 0           $search = '0x'.$search;
132             }
133 0           my $message = $self->_get(op => 'get', options => 'exact', search => $search);
134 0 0         return undef unless defined $message;
135 0           return $message;
136             }
137              
138              
139             else {
140 0           confess "unknown query type '$type'";
141             }
142             }
143              
144              
145             sub submit($@) {
146 0     0 1   my ($self, @keys) = @_;
147 0           my $status = $self->_post(map {( keytext => $_ )} @keys);
  0            
148 0   0       return (defined $status and $status ? 1 : 0);
149             }
150              
151              
152 0     0 1   sub error($) { shift->{error} }
153              
154             1;
155              
156             __END__