File Coverage

blib/lib/Captcha/reCAPTCHA.pm
Criterion Covered Total %
statement 95 102 93.1
branch 35 42 83.3
condition 10 13 76.9
subroutine 20 21 95.2
pod 7 7 100.0
total 167 185 90.2


line stmt bran cond sub pod time code
1             package Captcha::reCAPTCHA;
2              
3 6     6   111123 use warnings;
  6         9  
  6         177  
4 6     6   22 use strict;
  6         7  
  6         98  
5 6     6   17 use Carp;
  6         11  
  6         391  
6 6     6   3405 use LWP::UserAgent;
  6         175313  
  6         176  
7 6     6   2939 use HTML::Tiny;
  6         11795  
  6         236  
8              
9             our $VERSION = '0.99';
10              
11 6     6   29 use constant API_SERVER => 'http://www.google.com/recaptcha/api';
  6         8  
  6         356  
12 6         239 use constant API_SECURE_SERVER =>
13 6     6   22 'https://www.google.com/recaptcha/api';
  6         7  
14 6     6   23 use constant API_VERIFY_SERVER => 'http://www.google.com';
  6         8  
  6         231  
15 6     6   22 use constant API_VERIFY_SERVER_V2 => 'https://www.google.com/recaptcha/api/siteverify';
  6         5  
  6         231  
16 6     6   56 use constant SERVER_ERROR => 'recaptcha-not-reachable';
  6         8  
  6         231  
17 6     6   22 use constant API_V2_SERVER => 'https://www.google.com/recaptcha/api.js';
  6         7  
  6         5151  
18              
19             =head1 NAME
20              
21             Captcha::reCAPTCHA - A Perl implementation of the reCAPTCHA API
22              
23             =head1 VERSION
24              
25             This document describes Captcha::reCAPTCHA version 0.99
26              
27             =head1 NOTICE
28              
29             Please note this module now allows the use of v2
30             there are no changes to version 1.
31             Version 2 has seperate methds you can call
32              
33             =cut
34              
35             =head1 SYNOPSIS
36              
37             Note this release contains methods that use
38              
39             use Captcha::reCAPTCHA;
40              
41             my $c = Captcha::reCAPTCHA->new;
42              
43             # Output form New Version
44             print $c->get_html_v2( 'your public key here' );
45              
46             # Version 1 (not recommended)
47             print $c->get_html( 'your public key here' );
48              
49             # Verify submission
50             my $result $c->check_answer_v2($private_key, $response, $ENV{REMOTE_ADDR});
51              
52             # Verify submission (Old Version)
53             my $result = $c->check_answer(
54             'your private key here', $ENV{'REMOTE_ADDR'},
55             $challenge, $response
56             );
57              
58             if ( $result->{is_valid} ) {
59             print "Yes!";
60             }
61             else {
62             # Error
63             $error = $result->{error};
64             }
65              
66             For complete examples see the /examples subdirectory
67              
68             =head1 DESCRIPTION
69              
70             reCAPTCHA version 1 is a hybrid mechanical turk and captcha that allows visitors
71             who complete the captcha to assist in the digitization of books.
72              
73             From L:
74              
75             reCAPTCHA improves the process of digitizing books by sending words that
76             cannot be read by computers to the Web in the form of CAPTCHAs for
77             humans to decipher. More specifically, each word that cannot be read
78             correctly by OCR is placed on an image and used as a CAPTCHA. This is
79             possible because most OCR programs alert you when a word cannot be read
80             correctly.
81              
82             version 1 of Perl implementation is modelled on the PHP interface that can be
83             found here:
84              
85             L
86              
87             To use reCAPTCHA you need to register your site here:
88              
89             L
90              
91              
92             Version 2 is a new and eaasy to solve captcha that is
93             "easy for humans to solve, but hard for 'bots' and other malicious software"
94              
95             =head1 INTERFACE
96              
97             =over
98              
99             =item C<< new >>
100              
101             Create a new C<< Captcha::reCAPTCHA >>.
102              
103             =cut
104              
105             sub new {
106 23     23 1 7430 my $class = shift;
107 23         40 my $self = bless {}, $class;
108 23         47 $self->_initialize( @_ );
109 22         82 return $self;
110             }
111              
112             sub _initialize {
113 23     23   19 my $self = shift;
114 23   100     86 my $args = shift || {};
115              
116 23 100       203 croak "new must be called with a reference to a hash of parameters"
117             unless 'HASH' eq ref $args;
118             }
119              
120 17   66 17   70 sub _html { shift->{_html} ||= HTML::Tiny->new }
121              
122             =item C<< get_options_setter( $options ) >>
123              
124             You can optionally customize the look of the reCAPTCHA widget with some
125             JavaScript settings. C returns a block of Javascript
126             wrapped in tags that will set the options to be used
127             by the widget.
128              
129             C<$options> is a reference to a hash that may contain the following keys:
130              
131             =over
132              
133             =item C
134              
135             Defines which theme to use for reCAPTCHA. Possible values are 'red',
136             'white' or 'blackglass'. The default is 'red'.
137              
138             =item C
139              
140             Sets a tabindex for the reCAPTCHA text box. If other elements in the
141             form use a tabindex, this should be set so that navigation is easier for
142             the user. Default: 0.
143              
144             =back
145              
146             =cut
147              
148             sub get_options_setter {
149 6     6 1 5 my $self = shift;
150 6   100     32 my $options = shift || return '';
151              
152 2 50       4 croak "The argument to get_options_setter must be a hashref"
153             unless 'HASH' eq ref $options;
154              
155 2         3 my $h = $self->_html;
156              
157 2         7 return $h->script(
158             { type => 'text/javascript' },
159             "\n//
160             . "var RecaptchaOptions = "
161             . $h->json_encode( $options )
162             . ";\n//]]>\n"
163             ) . "\n";
164             }
165              
166             =item C<< get_options_setter_div( $pubkey, $options ) >>
167              
168             You can optionally customize the look of the reCAPTCHA widget with some
169             settings. C returns a div element
170             wrapped in
..
tags that will set the options to be used
171             by the widget.
172              
173             C<$options> is a reference to a hash that may contain the following keys:
174              
175             =over
176              
177             =item C
178              
179             Defines which theme to use for reCAPTCHA. Possible values are 'dark',
180             'light'. The default is 'light'.
181              
182             =item C
183              
184             Defines the type of captcha to server. Possible values are 'audio' or 'image'.
185             Default is 'image'
186              
187             =item C
188              
189             Defines the size of the widget. Possible values are 'compact' or 'normal'.
190             Default is 'normal'
191              
192             =item C
193              
194             Defines the tabindex of the widget and challenge. If other elements in your
195             page use tabindex, it should be set to make user navigation easier.
196             Default is 0
197              
198             =item C
199              
200             Defines the name of your callback function to be executed when the user submits
201             a successful CAPTCHA response. The user's response, g-recaptcha-response,
202             will be the input for your callback function.
203              
204             =item C
205              
206             Defines the name of your callback function to be executed when the recaptcha
207             response expires and the user needs to solve a new CAPTCHA
208              
209             =back
210             =cut
211              
212             sub get_options_setter_div {
213 6     6 1 316 my $self = shift;
214 6         20 my ($pubkey, $options) = @_;
215              
216 6 100 100     186 croak "The argument to get_options_setter_div must be a hashref"
217             if $options && ref $options ne 'HASH';
218              
219             # Make option in to an empty hash if nothing there
220 5 100       7 $options = {} unless $options;
221              
222 5 50       7 croak "public key must be supplied" unless $pubkey;
223              
224 5         6 my $h = $self->_html;
225              
226             return $h->div({class => 'g-recaptcha',
227             'data-sitekey' => $pubkey,
228 5         31 %{$options}
  5         20  
229             });
230             }
231              
232             =item C<< get_html_v2( $pubkey, \%options ) >>
233              
234             Generates HTML to display the captcha using the new api
235             pubkey is public key for \%options types the same as get_options_setter
236              
237             print $captcha->get_html_v2($pubkey, $options);
238              
239             This uses ssl by default and does not display custom error messages
240              
241             =cut
242              
243             sub get_html_v2 {
244 4     4 1 4 my $self = shift;
245 4         6 my ($pubkey, $options) = @_;
246              
247 4 50       10 croak
248             "To use reCAPTCHA you must get an API key from https://www.google.com/recaptcha/admin/create"
249             unless $pubkey;
250              
251 4         11 my $h = $self->_html;
252              
253             # Use new version by default
254 4         147 return join('',
255             '',
256             $self->get_options_setter_div( $pubkey, $options )
257             );
258             }
259              
260             =item C<< get_html( $pubkey, $error, $use_ssl, \%options ) >>
261              
262             Generates HTML to display the captcha using api version 1.
263              
264             print $captcha->get_html( $PUB, $err );
265              
266             =over
267              
268             =item C<< $pubkey >>
269              
270             Your reCAPTCHA public key, from the API Signup Page
271              
272             =item C<< $error >>
273              
274             Optional. If set this should be either a string containing a reCAPTCHA
275             status code or a result hash as returned by C<< check_answer >>.
276              
277             =item C<< $use_ssl >>
278              
279             Optional. Should the SSL-based API be used? If you are displaying a page
280             to the user over SSL, be sure to set this to true so an error dialog
281             doesn't come up in the user's browser.
282              
283             =item C<< $options >>
284              
285             Optional. A reference to a hash of options for the captcha. See
286             C<< get_options_setter >> for more details.
287              
288             =back
289              
290             Returns a string containing the HTML that should be used to display
291             the captcha.
292              
293             =cut
294              
295             sub get_html {
296 8     8 1 8 my $self = shift;
297 8         11 my ( $pubkey, $error, $use_ssl, $options ) = @_;
298              
299 8 100       154 croak
300             "To use reCAPTCHA you must get an API key from https://www.google.com/recaptcha/admin/create"
301             unless $pubkey;
302              
303 6         12 my $h = $self->_html;
304 6 100       178 my $server = $use_ssl ? API_SECURE_SERVER : API_SERVER;
305              
306 6         10 my $query = { k => $pubkey };
307 6 100       10 if ( $error ) {
308             # Handle the case where the result hash from check_answer
309             # is passed.
310 2 100       5 if ( 'HASH' eq ref $error ) {
311 1 50       4 return '' if $error->{is_valid};
312 1         2 $error = $error->{error};
313             }
314 2         13 $query->{error} = $error;
315             }
316 6         10 my $qs = $h->query_encode( $query );
317              
318 6         182 return join(
319             '',
320             $self->get_options_setter( $options ),
321             $h->script(
322             {
323             type => 'text/javascript',
324             src => "$server/challenge?$qs",
325             }
326             ),
327             "\n",
328             $h->noscript(
329             [
330             $h->iframe(
331             {
332             src => "$server/noscript?$qs",
333             height => 300,
334             width => 500,
335             frameborder => 0
336             }
337             ),
338             $h->textarea(
339             {
340             name => 'recaptcha_challenge_field',
341             rows => 3,
342             cols => 40
343             }
344             ),
345             $h->input(
346             {
347             type => 'hidden',
348             name => 'recaptcha_response_field',
349             value => 'manual_challenge'
350             }
351             )
352             ]
353             ),
354             "\n"
355             );
356             }
357              
358             sub _post_request {
359 0     0   0 my $self = shift;
360 0         0 my ( $url, $args ) = @_;
361              
362 0         0 my $ua = LWP::UserAgent->new();
363 0         0 $ua->env_proxy();
364 0         0 return $ua->post( $url, $args );
365             }
366              
367             =item C<< check_answer_v2 >>
368              
369             After the user has filled out the HTML form, including their answer for
370             the CAPTCHA, use C<< check_answer >> to check their answer when they
371             submit the form. The user's answer will be in field,
372             g-recaptcha-response. The reCAPTCHA
373             library will make an HTTP request to the reCAPTCHA server and verify the
374             user's answer.
375              
376             =over
377              
378             =item C<< $privkey >>
379              
380             Your reCAPTCHA private key, from the API Signup Page.
381              
382             =item C<< $remoteip >>
383              
384             The user's IP address, in the format 192.168.0.1 (optional)
385              
386             =item C<< $response >>
387              
388             The value of the form field recaptcha_response_field.
389              
390             =back
391              
392             Returns a reference to a hash containing two fields: C
393             and C.
394              
395             # If your site does not use SSL then
396             $ENV{PERL_LWP_SSL_VERIFY_HOSTNAME} = 0;
397              
398             my $result = $c->check_answer_v2(
399             'your private key here', $response,
400             $ENV{'REMOTE_ADDR'}
401             );
402              
403             my $result = $c->check_answer_v2(
404             'your private key here', $response,
405             $ENV{'REMOTE_ADDR'}
406             );
407              
408             if ( $result->{is_valid} ) {
409             print "Yes!";
410             }
411             else {
412             # Error
413             $error = $result->{error};
414             }
415              
416             See the /examples subdirectory for examples of how to call C.
417              
418             Note: this method will make an HTTP request to Google to verify the user input.
419             If this request must be routed via a proxy in your environment, use the
420             standard environment variable to specify the proxy address, e.g.:
421              
422             $ENV{http_proxy} = 'http://myproxy:3128';
423              
424             =cut
425              
426             sub check_answer_v2 {
427 5     5 1 613 my $self = shift @_;
428              
429 5         7 my ($privkey, $response, $remoteip) = @_;
430              
431 5 100       131 croak
432             "To use reCAPTCHA you must get an API key from https://www.google.com/recaptcha/admin/create"
433             unless $privkey;
434              
435 4 100       66 croak "To check answer, the user response token must be provided" unless $response;
436              
437 3         6 my $request = {
438             secret => $privkey,
439             response => $response,
440             };
441 3 100       7 $request->{remoteip} = $remoteip if $remoteip;
442              
443 3         6 my $resp = $self->_post_request(
444             API_VERIFY_SERVER_V2,
445             $request
446             );
447              
448 3 50       294 if ( $resp->is_success ) {
449              
450 3 100       19 if ($resp->content =~ /success": true/) {
451 1         13 return { is_valid => 1 }
452             } else {
453 2         18 return { is_valid => 0, error => $resp->content};
454             }
455             }
456              
457 0         0 return { is_valid => 0, error => $resp->content }
458             }
459              
460             =item C<< check_answer >>
461              
462             After the user has filled out the HTML form, including their answer for
463             the CAPTCHA, use C<< check_answer >> to check their answer when they
464             submit the form. The user's answer will be in two form fields,
465             recaptcha_challenge_field and recaptcha_response_field. The reCAPTCHA
466             library will make an HTTP request to the reCAPTCHA server and verify the
467             user's answer.
468              
469             =over
470              
471             =item C<< $privkey >>
472              
473             Your reCAPTCHA private key, from the API Signup Page.
474              
475             =item C<< $remoteip >>
476              
477             The user's IP address, in the format 192.168.0.1.
478              
479             =item C<< $challenge >>
480              
481             The value of the form field recaptcha_challenge_field
482              
483             =item C<< $response >>
484              
485             The value of the form field recaptcha_response_field.
486              
487             =back
488              
489             Returns a reference to a hash containing two fields: C
490             and C.
491              
492             my $result = $c->check_answer(
493             'your private key here', $ENV{'REMOTE_ADDR'},
494             $challenge, $response
495             );
496              
497             if ( $result->{is_valid} ) {
498             print "Yes!";
499             }
500             else {
501             # Error
502             $error = $result->{error};
503             }
504              
505             See the /examples subdirectory for examples of how to call C.
506              
507             Note: this method will make an HTTP request to Google to verify the user input.
508             If this request must be routed via a proxy in your environment, use the
509             standard environment variable to specify the proxy address, e.g.:
510              
511             $ENV{http_proxy} = 'http://myproxy:3128';
512              
513             =back
514             =cut
515              
516             sub check_answer {
517 4     4 1 4 my $self = shift;
518 4         7 my ( $privkey, $remoteip, $challenge, $response ) = @_;
519              
520 4 100       72 croak
521             "To use reCAPTCHA you must get an API key from https://www.google.com/recaptcha/admin/create"
522             unless $privkey;
523              
524 3 100       69 croak "For security reasons, you must pass the remote ip to reCAPTCHA"
525             unless $remoteip;
526              
527 2 50 33     8 return { is_valid => 0, error => 'incorrect-captcha-sol' }
528             unless $challenge && $response;
529              
530 2         9 my $resp = $self->_post_request(
531             API_VERIFY_SERVER . '/recaptcha/api/verify',
532             {
533             privatekey => $privkey,
534             remoteip => $remoteip,
535             challenge => $challenge,
536             response => $response
537             }
538             );
539              
540 2 50       241 if ( $resp->is_success ) {
541 2         17 my ( $answer, $message ) = split( /\n/, $resp->content, 2 );
542 2 100       21 if ( $answer =~ /true/ ) {
543 1         31 return { is_valid => 1 };
544             }
545             else {
546 1         2 chomp $message;
547 1         8 return { is_valid => 0, error => $message };
548             }
549             }
550             else {
551 0           return { is_valid => 0, error => SERVER_ERROR };
552             }
553             }
554              
555             1;
556             __END__