package Catalyst::TraitFor::Controller::reCAPTCHA;
# ABSTRACT: authenticate people and read books!
$Catalyst::TraitFor::Controller::reCAPTCHA::VERSION = '2.00';
use Moose::Role;
use MooseX::MethodAttributes::Role;
use namespace::autoclean;

use Captcha::reCAPTCHA;
use Carp 'croak';

has recaptcha => ( is => 'ro', default => sub { Captcha::reCAPTCHA->new } );

sub captcha_get :Private {
    my ( $self, $c ) = @_;
    my $recaptcha;

    if (lc($c->config->{recaptcha}{version} || '') eq 'v2') {
        $recaptcha = $self->recaptcha->get_html_v2(
            $c->config->{recaptcha}{pub_key},
            $c->config->{recaptcha}{options}
        );
    }
    else {
        $recaptcha = $self->recaptcha->get_html(
            $c->config->{recaptcha}{pub_key},
            $c->stash->{recaptcha_error},
            $c->req->secure,
            $c->config->{recaptcha}{options}
        );
    }

    $c->stash( recaptcha => $recaptcha );
}

sub captcha_check :Private {
    my ( $self, $c ) = @_;

    my $challenge = $c->req->param('recaptcha_challenge_field');
    my $response  = $c->req->param('recaptcha_response_field');
    my $response_v2 = $c->req->param('g-recaptcha-response');

    unless ( ($response && $challenge || $response_v2)) {
        $c->stash->{recaptcha_error} = 'User appears not to have submitted a recaptcha';
        return;
    }

    my $res;
    if (lc($c->config->{recaptcha}{version} || '') eq 'v2') {
        $ENV{PERL_LWP_SSL_VERIFY_HOSTNAME} = $c->req->secure;
        $res = $self->recaptcha->check_answer_v2(
            $c->config->{recaptcha}{priv_key},
            $response_v2,
            $c->req->address
        );
    }
    else {
        $res = $self->recaptcha->check_answer(
            $c->config->{recaptcha}{priv_key},
            $c->req->address,
            $challenge,
            $response
        );
    }

    croak 'Failed to get valid result from reCaptcha'
        unless ref $res eq 'HASH';

    unless ( $res->{is_valid} ) {
        $c->stash( recaptcha_error => $res->{error} || 'Invalid recaptcha' );
    }

    $c->stash( recaptcha_ok => $res->{is_valid} );
    return $res->{is_valid};
}

1;

__END__

=pod

=encoding UTF-8

=head1 NAME

Catalyst::TraitFor::Controller::reCAPTCHA - authenticate people and read books!

=head1 VERSION

version 2.00

=head1 SYNOPSIS

In your controller

    package MyApp::Controller::Comment;
    use Moose;
    use namespace::autoclean;

    BEGIN { extends 'Catalyst::Controller' }
    with 'Catalyst::TraitFor::Controller::reCAPTCHA';

    sub example : Local {
        my ( $self, $c ) = @;

        # validate received form
        if ( $c->forward('captcha_check') ) {
            $c->detach('my_form_is_ok');
        }

        # Set reCAPTCHA html code
        $c->forward('captcha_get');
    }

    1;

=head1 SUMMARY

Catalyst::Controller role around L<Captcha::reCAPTCHA>.  Provides
a number of C<Private> methods that deal with the recaptcha.

This module is based/copied from L<Catalyst::Controller::reCAPTCHA>,
it just adds support for option passing and automatically sets ssl
when used on a secure request.

If you are using L<Catalyst::Controller::reCAPTCHA> and want to move
to this role, you only need to stop extending L<Catalyst::Controller>
and apply this role as shown in the SYNOPSIS.

=head2 CONFIGURATION

In MyApp.pm (or equivalent in config file):

 __PACKAGE__->config->{recaptcha} = {
    pub_key  => '6LcsbAAAAAAAAPDSlBaVGXjMo1kJHwUiHzO2TDze',
    priv_key => '6LcsbAAAAAAAANQQGqwsnkrTd7QTGRBKQQZwBH-L',
    options  => { theme => 'white' },
    version  => 'v2' ## reCaptcha version default (v1)
 };

(the two keys above work for http://localhost unless someone hammers the
reCAPTCHA server with failures, in which case the API keys get a temporary
ban).

=head2 METHODS

=head3 captcha_get : Private

Sets $c->stash->{recaptcha} to be the html form for the L<http://recaptcha.net/> reCAPTCHA service which can be included in your HTML form.

=head3 captcha_check : Private

Validates the reCaptcha using L<Captcha::reCAPTCHA>.  sets
$c->stash->{recaptcha_ok} which will be 1 on success. The action also returns
true if there is success. This means you can do:

 if ( $c->forward(captcha_check) ) {
   # do something based on the reCAPTCHA passing
 }

or alternatively:

 $c->forward(captcha_check);
 if ( $c->stash->{recaptcha_ok} ) {
   # do something based on the reCAPTCHA passing
 }

If there's an error, $c->stash->{recaptcha_error} is
set with the error string provided by L<Captcha::reCAPTCHA>.

=head1 SEE ALSO

=over 4

=item *

L<Captcha::reCAPTCHA>

=item *

L<Catalyst::Controller>

=item *

L<Catalyst>

=back

=head1 ACKNOWLEDGEMENTS

This module is almost copied from Kieren Diment L<Catalyst::Controller::reCAPTCHA>.

=head1 AUTHOR

Diego Kuperman <diego@freekeylabs.com>

=head1 COPYRIGHT AND LICENSE

This software is copyright (c) 2017 by Diego Kuperman.

This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.

=cut