File Coverage

blib/lib/Net/Pavatar.pm
Criterion Covered Total %
statement 16 18 88.8
branch n/a
condition n/a
subroutine 6 6 100.0
pod n/a
total 22 24 91.6


line stmt bran cond sub pod time code
1             package Net::Pavatar;
2              
3 1     1   21289 use warnings;
  1         2  
  1         32  
4 1     1   5 use strict;
  1         1  
  1         30  
5 1     1   5457 use LWPx::ParanoidAgent;
  1         382575  
  1         990  
6 1     1   16 use Carp;
  1         1  
  1         474  
7 1     1   11 use URI;
  1         2  
  1         37  
8 1     1   1005 use GD;
  0            
  0            
9             use Regexp::Common qw /URI/;
10              
11             =head1 NAME
12              
13             Net::Pavatar - Pavatar client
14              
15             =head1 VERSION
16              
17             Version 1.01
18              
19             =cut
20              
21             our $VERSION = '1.01';
22              
23             =head1 SYNOPSIS
24              
25             use Net::Pavatar;
26              
27             my ($hash, $file_type) = Net::Pavatar->fetch( 'http://someblog.com/', { size => [32, 48] } );
28              
29             if ($file_type) {
30             open FILE, ">avatar.$file_type";
31             print FILE $hash->{'48'};
32             close FILE;
33             }
34              
35             =cut
36              
37             sub _browser_get {
38             my $url = shift;
39             my $browser = shift;
40              
41             my ($i, $resp) = (0, undef);
42             do {
43             if ($i++) { sleep(1); }
44             $resp = $browser->get($url);
45             } until ($i >= 3 or $resp->code <= 499);
46             return wantarray ? ($resp, $resp->is_success) : $resp;
47             }
48              
49              
50             =head1 DESCRIPTION
51              
52             Fetches a pavatar image from a given URL and gives it to you in the sizes you specify. Uses LWPx::ParanoidAgent to protect your servers from attacks.
53              
54             This module fully conforms to Pavatar spec 0.3.0 (L), which is the latest one on Apr 25th, 2007.
55              
56             =head1 METHODS
57              
58             =cut
59              
60             sub _discover {
61             my $class = shift;
62             my $url = shift;
63             my $params = shift || {};
64              
65             my $ua = $params->{'ua'} || LWPx::ParanoidAgent->new( timeout => 15, parse_head => 0 );
66             my ($resp, $ok) = &_browser_get($url, $ua);
67             if (! $ok) { return }
68             my $base = $resp->base();
69              
70             # STEP 3.a of spec
71             my ($answer) = $resp->header('X-Pavatar');
72             if (defined $answer) {
73             if ($answer eq 'none' or $answer !~ /$RE{'URI'}{'HTTP'}/) { return }
74             return $answer;
75             }
76              
77             # STEP 3.b of spec
78             my $page = $resp->content;
79             if ($resp->content_type =~ /\b(x?html|xml)\b/) {
80             ($answer) = $page =~ //gi;
81             if (defined $answer) {
82             if ($answer eq 'none' or $answer !~ /$RE{'URI'}{'HTTP'}/) { return }
83             return $answer;
84             }
85             }
86              
87             # STEP 3.c of spec
88             my $uri = URI->new($url);
89             #my $uri = $resp->request->uri;
90             if ($uri->scheme ne 'http') { return; }
91             $uri = 'http://'.$uri->host_port.($uri->path || '/');
92             my $pavuri = URI->new_abs('pavatar.png', $uri);
93              
94             my $max_size = $ua->max_size;
95             $ua->max_size(51200);
96              
97             ($resp, $ok) = &_browser_get( $pavuri->as_string, $ua );
98             if ($ok) { $ua->max_size($max_size); return wantarray ? ($pavuri, $resp) : $pavuri; }
99              
100             my $did_pavuri = $pavuri->as_string;
101             $pavuri->path('/pavatar.png');
102              
103             if ($pavuri->as_string ne $did_pavuri) {
104             ($resp, $ok) = &_browser_get( $pavuri, $ua );
105             if ($ok) { $ua->max_size($max_size); return wantarray ? ($pavuri, $resp) : $pavuri; }
106             }
107              
108             $ua->max_size($max_size);
109              
110             return;
111             }
112              
113              
114              
115             =head2 my ($hashref, $type) = Net::Pavatar->fetch( $url, \%opts )
116              
117             Returns a hashref and a string, as a 2-list. The hash contains the image sizes as keys, and the image data for each size as values. The string contains the image type and can either be 'jpeg', 'png' or 'gif'. If a pavatar does not exist, or is not valid for any reason, returns null.
118              
119             The \%opts hashref is optional, and accepts the following keys:
120              
121             C : the sizes that you want the pavatar image returned in - defaults to 80
122              
123             C : the total time that UserAgent is allowed to retrieve each page or image - defaults to 15
124              
125             e.g. C<< Net::Pavatar->fetch( $url, { size => [32, 48], timeout => 25 } ) >>
126              
127             =cut
128              
129             sub fetch {
130             my $class = shift;
131             my $url = shift;
132             my $params = shift || {};
133              
134             my $ua = $params->{'ua'} || LWPx::ParanoidAgent->new( timeout => 15, parse_head => 0 );
135             ($url, my $resp) = $class->_discover($url, { ua => $ua });
136             if (! $url) { return; }
137              
138             my $max_size = $ua->max_size;
139             $ua->max_size(51200);
140             my $ok;
141             if (! $resp) {
142             ($resp, $ok) = &_browser_get($url, $ua);
143             } else {
144             $ok = 1;
145             }
146             $ua->max_size($max_size);
147             if (! $ok) { return; }
148              
149             my $type = $resp->content_type;
150             ($type) = $type =~ /^image\/(.+)$/g;
151              
152             my $img;
153             if ($type eq 'jpeg') {
154             $img = GD::Image->newFromJpegData($resp->content, 1);
155             } elsif ($type eq 'gif') {
156             $img = GD::Image->newFromGifData($resp->content, 1);
157             } elsif ($type eq 'png') {
158             $img = GD::Image->newFromPngData($resp->content, 1);
159             } else {
160             return;
161             }
162             if (! $img) { return; }
163              
164             my ($width, $height) = $img->getBounds();
165             if ($width != 80 or $height != 80) { return; }
166             my @sizes;
167             my $size = $params->{'size'};
168             if (! defined $size) {
169             @sizes = (80);
170             } elsif (ref $size eq 'ARRAY') {
171             @sizes = grep { /^\d+$/ } @$size;
172             } elsif (! ref $size) {
173             @sizes = int($size);
174             } else {
175             confess "Error: sizes parameter needs to be a number or an arrayref";
176             }
177              
178             my $return = { };
179             foreach my $size (@sizes) {
180             if ($size == 80) {
181             $return->{'80'} = $resp->content();
182             } elsif ($size > 0 and $size < 80) {
183             my $newimage = GD::Image->new($size, $size, 1);
184             $newimage->copyResampled($img, 0, 0, 0, 0, $size, $size, 80, 80);
185             my $data = $newimage->$type();
186             $return->{$size} = $data;
187             } else {
188             confess "Error: problem with size = '$size' (needs to be an integer between 1 and 80 inclusive)";
189             }
190             }
191             if (! keys %$return) { return; }
192              
193             return ($return, $type);
194             }
195              
196             =head1 AUTHOR
197              
198             Alexander Karelas, C<< >>
199              
200             =head1 BUGS
201              
202             Please report any bugs or feature requests to
203             C, or through the web interface at
204             L.
205             I will be notified, and then you'll automatically be notified of progress on
206             your bug as I make changes.
207              
208             =head1 SUPPORT
209              
210             You can find documentation for this module with the perldoc command.
211              
212             perldoc Net::Pavatar
213              
214             You can also look for information at:
215              
216             =over 4
217              
218             =item * AnnoCPAN: Annotated CPAN documentation
219              
220             L
221              
222             =item * CPAN Ratings
223              
224             L
225              
226             =item * RT: CPAN's request tracker
227              
228             L
229              
230             =item * Search CPAN
231              
232             L
233              
234             =item * Module's RSS feed
235              
236             L
237              
238             =back
239              
240             =head1 ACKNOWLEDGEMENTS
241              
242             =head1 COPYRIGHT & LICENSE
243              
244             Copyright 2007 Alexander Karelas, all rights reserved.
245              
246             This program is free software; you can redistribute it and/or modify it
247             under the same terms as Perl itself.
248              
249             =cut
250              
251             1; # End of Net::Pavatar