File Coverage

blib/lib/Captcha/reCAPTCHA.pm
Criterion Covered Total %
statement 65 71 91.5
branch 20 24 83.3
condition 7 10 70.0
subroutine 15 16 93.7
pod 4 4 100.0
total 111 125 88.8


line stmt bran cond sub pod time code
1             package Captcha::reCAPTCHA;
2              
3 4     4   188059 use warnings;
  4         10  
  4         129  
4 4     4   176 use strict;
  4         10  
  4         132  
5 4     4   21 use Carp;
  4         11  
  4         347  
6 4     4   9646 use LWP::UserAgent;
  4         315021  
  4         157  
7 4     4   4913 use HTML::Tiny;
  4         21677  
  4         240  
8              
9             our $VERSION = '0.97';
10              
11 4     4   45 use constant API_SERVER => 'http://www.google.com/recaptcha/api';
  4         8  
  4         307  
12 4         186 use constant API_SECURE_SERVER =>
13 4     4   24 'https://www.google.com/recaptcha/api';
  4         14  
14 4     4   18 use constant API_VERIFY_SERVER => 'http://www.google.com';
  4         9  
  4         202  
15 4     4   22 use constant SERVER_ERROR => 'recaptcha-not-reachable';
  4         8  
  4         4353  
16              
17             sub new {
18 13     13 1 14370 my $class = shift;
19 13         45 my $self = bless {}, $class;
20 13         52 $self->_initialize( @_ );
21 12         76 return $self;
22             }
23              
24             sub _initialize {
25 13     13   20 my $self = shift;
26 13   100     77 my $args = shift || {};
27              
28 13 100       274 croak "new must be called with a reference to a hash of parameters"
29             unless 'HASH' eq ref $args;
30             }
31              
32 6   66 6   53 sub _html { shift->{_html} ||= HTML::Tiny->new }
33              
34             sub get_options_setter {
35 5     5 1 9 my $self = shift;
36 5   100     42 my $options = shift || return '';
37              
38 1 50       6 croak "The argument to get_options_setter must be a hashref"
39             unless 'HASH' eq ref $options;
40              
41 1         6 my $h = $self->_html;
42              
43 1         9 return $h->script(
44             { type => 'text/javascript' },
45             "\n//
46             . "var RecaptchaOptions = "
47             . $h->json_encode( $options )
48             . ";\n//]]>\n"
49             ) . "\n";
50             }
51              
52             sub get_html {
53 7     7 1 12 my $self = shift;
54 7         15 my ( $pubkey, $error, $use_ssl, $options ) = @_;
55              
56 7 100       278 croak
57             "To use reCAPTCHA you must get an API key from https://www.google.com/recaptcha/admin/create"
58             unless $pubkey;
59              
60 5         18 my $h = $self->_html;
61 5 100       303 my $server = $use_ssl ? API_SECURE_SERVER : API_SERVER;
62              
63 5         19 my $query = { k => $pubkey };
64 5 100       16 if ( $error ) {
65             # Handle the case where the result hash from check_answer
66             # is passed.
67 2 100       9 if ( 'HASH' eq ref $error ) {
68 1 50       20 return '' if $error->{is_valid};
69 1         3 $error = $error->{error};
70             }
71 2         6 $query->{error} = $error;
72             }
73 5         20 my $qs = $h->query_encode( $query );
74              
75 5         288 return join(
76             '',
77             $self->get_options_setter( $options ),
78             $h->script(
79             {
80             type => 'text/javascript',
81             src => "$server/challenge?$qs",
82             }
83             ),
84             "\n",
85             $h->noscript(
86             [
87             $h->iframe(
88             {
89             src => "$server/noscript?$qs",
90             height => 300,
91             width => 500,
92             frameborder => 0
93             }
94             ),
95             $h->textarea(
96             {
97             name => 'recaptcha_challenge_field',
98             rows => 3,
99             cols => 40
100             }
101             ),
102             $h->input(
103             {
104             type => 'hidden',
105             name => 'recaptcha_response_field',
106             value => 'manual_challenge'
107             }
108             )
109             ]
110             ),
111             "\n"
112             );
113             }
114              
115             sub _post_request {
116 0     0   0 my $self = shift;
117 0         0 my ( $url, $args ) = @_;
118              
119 0         0 my $ua = LWP::UserAgent->new();
120 0         0 $ua->env_proxy();
121 0         0 return $ua->post( $url, $args );
122             }
123              
124             sub check_answer {
125 4     4 1 9 my $self = shift;
126 4         11 my ( $privkey, $remoteip, $challenge, $response ) = @_;
127              
128 4 100       140 croak
129             "To use reCAPTCHA you must get an API key from https://www.google.com/recaptcha/admin/create"
130             unless $privkey;
131              
132 3 100       119 croak "For security reasons, you must pass the remote ip to reCAPTCHA"
133             unless $remoteip;
134              
135 2 50 33     32 return { is_valid => 0, error => 'incorrect-captcha-sol' }
136             unless $challenge && $response;
137              
138 2         22 my $resp = $self->_post_request(
139             API_VERIFY_SERVER . '/recaptcha/api/verify',
140             {
141             privatekey => $privkey,
142             remoteip => $remoteip,
143             challenge => $challenge,
144             response => $response
145             }
146             );
147              
148 2 50       970 if ( $resp->is_success ) {
149 2         34 my ( $answer, $message ) = split( /\n/, $resp->content, 2 );
150 2 100       38 if ( $answer =~ /true/ ) {
151 1         28 return { is_valid => 1 };
152             }
153             else {
154 1         4 chomp $message;
155 1         14 return { is_valid => 0, error => $message };
156             }
157             }
158             else {
159 0           return { is_valid => 0, error => SERVER_ERROR };
160             }
161             }
162              
163             1;
164             __END__