#!/usr/bin/perl
##############################################################################
# COFC password set/change screen
##############################################################################

use strict;
use warnings;

use Apache::Htpasswd;
use CGI;
use Data::Dumper;
use HTML::Entities;
use Template;
$Data::Dumper::Sortkeys = 1;

use lib '/home/httpd/cofhol/cgi-binBENSUT/mod';
use COFC;

eval { COFC::init(); };
if ( $@ ) {
    die $@;
}

my $password_file = '/home/apachecnfg/cofccust';

my $dbh = $COFC::dbh;
my $q = $COFC::CGI;

my %cgi_args = $q->Vars;

if ( 0 && keys %cgi_args ) {
    COFC::eject(\%cgi_args, \%ENV);
    print "Content-Type: text/plain\n\n";
    print Dumper({ _0_q_Vars => \%cgi_args, _1_ENV => \%ENV });
    exit 0;
}

my %gtg_all = (
    request_logo => ($ENV{'GTG_SMALL_LOGO'} || ''),
    company      => ($ENV{'GTG_COMPANY'} || ''),
);

my $tt = Template->new( ABSOLUTE => 1 );
my $tt_data = {
    request_logo => $gtg_all{'request_logo'},
    company => $gtg_all{'company'},
    current_year => (localtime())[5] + 1900,
    focus_id => 'email',
};

$$tt_data{'script_name'} = $ENV{'SCRIPT_NAME'} || '';

my @errors;

############################################################################
# Check for a valid password reset token, or if we're in basic "change"
# mode, where the user must enter their current password. When using a
# token, the user only needs to enter their email address and new password
############################################################################

my $token = $cgi_args{'token'};
$token = '' unless defined $token;
my $token_quote = $dbh->quote($token);
my $password_reset = {};

if ( $token eq 'change' ) {
    $$tt_data{'require_passwd'} = 1;
    $$tt_data{'title_action'} = 'Change';
}
elsif ( $token =~ /^[0-9a-f]{40}$/ ) {
    my $sql = qq(SELECT * FROM $COFC::CUST_DB.password_reset WHERE token = $token_quote);
    $password_reset = $dbh->selectrow_hashref($sql);

    if ( !$password_reset ) {
        warn "(IP $ENV{'REMOTE_ADDR'}) Password reset not found for token: $token";
        COFC::print_redirect('/auth/login');
    }
    if ( $$password_reset{'used'} !~ /^0000-/ ) {
        COFC::cofc_warn("email: $$password_reset{'email'}\nIP: $ENV{'REMOTE_ADDR'}\nPassword reset already used for token $token");
        push @{$$tt_data{'errors'}}, qq(This password reset link was already used.);
    }

    # make sure login associated with this token exists (thanks, ticket 15795!)
    my $userid_quote = $dbh->quote($$password_reset{'email'});
    $sql = qq(SELECT * FROM $COFC::CUST_DB.custrec WHERE userid = $userid_quote);
    my $user_hash = $dbh->selectrow_hashref($sql);
    if ( !$user_hash ) {
        push @{$$tt_data{'errors'}}, qq(Cannot set password. User does not exist: $$password_reset{'email'});
        $$tt_data{'hide_form'} = 1;
    }

    $$tt_data{'require_passwd'} = $$password_reset{'require_passwd'};
    $$tt_data{'title_action'} = $$tt_data{'require_passwd'} ? 'Change' : 'Set';
}
elsif ( !$token ) {
    warn "(IP $ENV{'REMOTE_ADDR'}) No token";
    COFC::print_redirect('/auth/login');
}
elsif ( $token !~ /^[0-9a-f]{40}$/ ) {
    warn "(IP $ENV{'REMOTE_ADDR'}) Token is not a 40 character hexadecimal string: $token";
    COFC::print_redirect('/auth/login');
}
#COFC::eject('foo');

$$tt_data{'page_title'} = "COFC Password " . $$tt_data{'title_action'};
$$tt_data{'token'} = $token;

$$tt_data{'email'} = $cgi_args{'email'};
#$$tt_data{'password'} = $cgi_args{'password'};
#$$tt_data{'password2'} = $cgi_args{'password2'};
#$$tt_data{'password3'} = $cgi_args{'password3'};

############################################################################
# process form if it was submitted
############################################################################

if ( $cgi_args{'form_submitted'} ) {
    my $continue = 1;
#COFC::eject(\%cgi_args, $tt_data, $password_reset);

    if ( $cgi_args{'token'} ne 'change' && $cgi_args{'email'} ne $$password_reset{'email'} ) {
        push @errors, qq(Email address is not correct);
        $continue = 0;
    }

    if ( $continue && $$tt_data{'require_passwd'} ) {
        if ( !exists $cgi_args{'password'} || $cgi_args{'password'} eq '' ) {
            $$tt_data{'focus_id'} = 'password';
            push @errors, qq(Current password is required);
            $continue = 0;
        }
        else {
            # check if current password is valid
            my $ht = Apache::Htpasswd->new({ passwdFile => $password_file, ReadOnly => 1 });
            my $result = $ht->htCheckPassword($cgi_args{'email'}, $cgi_args{'password'});
            if ( !$result ) {
                $$tt_data{'focus_id'} = 'password';
                push @errors, qq(Current password is not correct);
                $continue = 0;
            }
        }
    }

    if ( $continue && $cgi_args{'password2'} ne $cgi_args{'password3'} ) {
        $$tt_data{'focus_id'} = 'password3';
        push @errors, qq(New password fields do not match);
        $continue = 0;
    }

    if ( $continue ) {
        my ($success, $messages) = check_password_strength_cofc($cgi_args{'password2'});
        if ( !$success ) {
            push @errors, @$messages;
            $continue = 0;
        }
    }

    # so far so good, set password and mark password reset record as used
    if ( $continue ) {

        # try to update the db first, since it can be rolled back
        if ( $token ne 'change' ) {
            my $sql = qq(UPDATE $COFC::CUST_DB.password_reset SET used = NOW() WHERE token = $token_quote);
            my $result = $dbh->do($sql);
            if ( !defined $result || $result eq '0E0' ) {
                die "Could not mark password_reset used: $sql";
            }
        }

        my $htpass = Apache::Htpasswd->new({ passwdFile => $password_file, ReadOnly => 0, UseMD5 => 1, });
        if ( !$htpass ) {
            $dbh->rollback();
            die "Could not create Apache::Htpasswd object";
        }

        my $result = $htpass->htpasswd( $cgi_args{'email'}, $cgi_args{'password2'}, { 'overwrite' => 1 } );
        #COFC::cofc_warn("password change result for $$password_reset{'email'}: " . Dumper($result));
        if ( !$result ) {
            $dbh->rollback();
            die "password change error: " . Dumper( $htpass->error ) . ' ';
        }

        $dbh->commit();
        $$tt_data{'password_set'} = 1;
    }

#    COFC::eject('would process form here', $tt_data, $password_reset, \%cgi_args, \@errors) unless ( $continue );
}

############################################################################
# display password set/change form
############################################################################

print $q->header;
$$tt_data{'errors'} = \@errors if @errors;

my $out;
(my $template_dir = $0) =~ s#^(.+)/[^/]+$#$1#;
$tt->process( $template_dir . '/passwd.tt', $tt_data, \$out ) || die $tt->error(), "\n ";

print $out;


exit 0;




sub check_password_strength_cofc {
    my $password = shift;
    my @messages = ();

    if ( length($password) < 5 ) {
        push @messages, qq(New password must be at least 5 characters long);
    }

    if ( @messages ) {
        return (0, \@messages);
    }

    return 1;
}

