%# BEGIN BPS TAGGED BLOCK {{{
%# 
%# COPYRIGHT:
%#  
%# This software is Copyright (c) 1996-2006 Best Practical Solutions, LLC 
%#                                          <jesse@bestpractical.com>
%# 
%# (Except where explicitly superseded by other copyright notices)
%# 
%# 
%# LICENSE:
%# 
%# This work is made available to you under the terms of Version 2 of
%# the GNU General Public License. A copy of that license should have
%# been provided with this software, but in any event can be snarfed
%# from www.gnu.org.
%# 
%# This work is distributed in the hope that it will be useful, but
%# WITHOUT ANY WARRANTY; without even the implied warranty of
%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
%# General Public License for more details.
%# 
%# You should have received a copy of the GNU General Public License
%# along with this program; if not, write to the Free Software
%# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
%# 
%# 
%# CONTRIBUTION SUBMISSION POLICY:
%# 
%# (The following paragraph is not intended to limit the rights granted
%# to you to modify and distribute this software under the terms of
%# the GNU General Public License and is only of importance to you if
%# you choose to contribute your changes and enhancements to the
%# community by submitting them to Best Practical Solutions, LLC.)
%# 
%# By intentionally submitting any modifications, corrections or
%# derivatives to this work, or any other work intended for use with
%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
%# you are the copyright holder for those contributions and you grant
%# Best Practical Solutions,  LLC a nonexclusive, worldwide, irrevocable,
%# royalty-free, perpetual, license to use, copy, create derivative
%# works based on those contributions, and sublicense and distribute
%# those contributions and any derivatives thereof.
%# 
%# END BPS TAGGED BLOCK }}}
<%INIT>

# Roll back any dangling transactions from a previous failed connection
$RT::Handle->ForceRollback() if $RT::Handle->TransactionDepth;


if ($RT::StatementLog) {
    $RT::Handle->ClearSQLStatementLog;
    $RT::Handle->LogSQLStatements(1);
}

local *session
    unless $m->is_subrequest;    # avoid reentrancy, as suggested by masonbook

# Disable AutoFlush using an attribute
if ( $m->request_comp->attr_exists('AutoFlush') ) {
    $m->autoflush( $m->request_comp->attr('AutoFlush') );
}

%ARGS = map {

    # if they've passed multiple values, they'll be an array. if they've
    # passed just one, a scalar whatever they are, mark them as utf8
    my $type = ref($_);
    ( !$type )
        ? Encode::is_utf8($_)
        ? $_
        : Encode::decode( utf8 => $_, Encode::FB_PERLQQ )
        : ( $type eq 'ARRAY' )
        ? [
        map {
            ( ref($_) or Encode::is_utf8($_) )
                ? $_
                : Encode::decode( utf8 => $_, Encode::FB_PERLQQ )
            } @$_
        ]
        : ( $type eq 'HASH' )
        ? {
        map {
            ( ref($_) or Encode::is_utf8($_) )
                ? $_
                : Encode::decode( utf8 => $_, Encode::FB_PERLQQ )
            } %$_
        }
        : $_
} %ARGS;

# Latter in the code we use
# $m->comp( { base_comp => $m->request_comp }, $m->fetch_next, %ARGS );
# instead of $m->call_next to avoid problems with UTF8 keys in arguments.
# The call_next method pass through original arguments and if you have
# an argument with unicode key then in a next component you'll get two
# records in the args hash: one with key without UTF8 flag and another
# with the flag, which may result into errors. "{ base_comp => $m->request_comp }"
# is copied from mason's source to get the same results as we get from
# call_next method, this feature is not documented, so we just leave it
# here to avoid possible side effects.

# This code canonicalizes time inputs in hours into minutes
foreach my $field ( keys %ARGS ) {
    next unless $field =~ /^(.*)-TimeUnits$/i && $ARGS{$1};
    my $local = $1;
    $ARGS{$local} =~ s{\b (?: (\d+) \s+ )? (\d+)/(\d+) \b}
                      {($1 || 0) + $3 ? $2 / $3 : 0}xe;
    if ( $ARGS{$field} && $ARGS{$field} =~ /hours/i ) {
        $ARGS{$local} *= 60;
    }
    delete $ARGS{$field};
}

$m->{'rt_base_time'} = [ Time::HiRes::gettimeofday() ];

$m->comp( '/Elements/SetupSessionCookie', %ARGS );

unless ( $session{'CurrentUser'} && $session{'CurrentUser'}->Id ) {
    $session{'CurrentUser'} = RT::CurrentUser->new();
}

# Set the proper encoding for the current language handle
$r->content_type("text/html; charset=utf-8");

# If it's a noauth file, don't ask for auth.
if ( $m->base_comp->path =~ $RT::WebNoAuthRegex ) {
    $m->comp( { base_comp => $m->request_comp }, $m->fetch_next, %ARGS);
    $m->abort;
}

# If RT is configured for external auth, let's go through and get REMOTE_USER
elsif ($RT::WebExternalAuth) {

    # do we actually have a REMOTE_USER equivlent?
    if ( RT::Interface::Web::WebCanonicalizeInfo() ) {

        my $orig_user = $user;

        $user = RT::Interface::Web::WebCanonicalizeInfo();
        $session{'CurrentUser'} = RT::CurrentUser->new();
        my $load_method = $RT::WebExternalGecos ? 'LoadByGecos' : 'Load';

        if ( $^O eq 'MSWin32' and $RT::WebExternalGecos ) {
            my $NodeName = Win32::NodeName();
            $user =~ s/^\Q$NodeName\E\\//i;
        }

        $session{'CurrentUser'}->$load_method($user);

        if ( $RT::WebExternalAuto and !$session{'CurrentUser'}->Id() ) {

            # Create users on-the-fly

            my $UserObj = RT::User->new( RT::CurrentUser->new('RT_System') );

            my ( $val, $msg ) = $UserObj->Create(
                %{ ref($RT::AutoCreate) ? $RT::AutoCreate : {} },
                Name  => $user,
                Gecos => $user,
            );

            if ($val) {

               # now get user specific information, to better create our user.
                my $new_user_info
                    = RT::Interface::Web::WebExternalAutoInfo($user);

        # set the attributes that have been defined.
        # FIXME: this is a horrible kludge. I'm sure there's something cleaner
                foreach my $attribute (
                    'Name',                  'Comments',
                    'Signature',             'EmailAddress',
                    'PagerEmailAddress',     'FreeformContactInfo',
                    'Organization',          'Disabled',
                    'Privileged',            'RealName',
                    'NickName',              'Lang',
                    'EmailEncoding',         'WebEncoding',
                    'ExternalContactInfoId', 'ContactInfoSystem',
                    'ExternalAuthId',        'Gecos',
                    'HomePhone',             'WorkPhone',
                    'MobilePhone',           'PagerPhone',
                    'Address1',              'Address2',
                    'City',                  'State',
                    'Zip',                   'Country'
                    )
                {
                    $m->comp( '/Elements/Callback', %ARGS,
                        _CallbackName => 'NewUser' );

                    my $method = "Set$attribute";
                    $UserObj->$method( $new_user_info->{$attribute} )
                        if ( defined $new_user_info->{$attribute} );
                }
                $session{'CurrentUser'}->Load($user);
            }
            else {

               # we failed to successfully create the user. abort abort abort.
                delete $session{'CurrentUser'};
                $m->abort() unless $RT::WebFallbackToInternalAuth;
                $m->comp( '/Elements/Login', %ARGS,
                    Error => loc( 'Cannot create user: [_1]', $msg ) );
            }
        }

        unless ( $session{'CurrentUser'}->Id() ) {
            delete $session{'CurrentUser'};
            $user = $orig_user;

            if ($RT::WebExternalOnly) {
                $m->comp( '/Elements/Login', %ARGS,
                    Error => loc('You are not an authorized user') );
                $m->abort();
            }
        }
    }
    elsif ($RT::WebFallbackToInternalAuth) {
        unless ( defined( $session{'CurrentUser'} ) ) {
            $m->comp( '/Elements/Login', %ARGS,
                Error => loc('You are not an authorized user') );
            $m->abort();
        }
    }
    else {

        # WebExternalAuth is set, but we don't have a REMOTE_USER. abort
        delete $session{'CurrentUser'} if defined $session{'CurrentUser'};
    }
}

delete $session{'CurrentUser'}
    unless $session{'CurrentUser'}
    and $session{'CurrentUser'}->Id;

# Process per-page authentication callbacks
$m->comp( '/Elements/Callback', %ARGS, _CallbackName => 'Auth' );

# If the user is logging in, let's authenticate
if ( !$session{'CurrentUser'} && defined $user && defined $pass ) {
    $session{'CurrentUser'} = RT::CurrentUser->new();
    $session{'CurrentUser'}->Load($user);

    unless ( $session{'CurrentUser'}->id
        && $session{'CurrentUser'}->IsPassword($pass) )
    {
        delete $session{'CurrentUser'};
        $RT::Logger->error("FAILED LOGIN for $user from $ENV{'REMOTE_ADDR'}");
        $m->comp( '/Elements/Login', %ARGS,
            Error => loc('Your username or password is incorrect') );
        $m->comp( '/Elements/Callback', %ARGS, _CallbackName => 'FailedLogin' );
        $m->abort;
    }
    else {
        $RT::Logger->info(
            "Successful login for $user from $ENV{'REMOTE_ADDR'}");
        $m->comp( '/Elements/Callback', %ARGS, _CallbackName => 'SuccessfulLogin' );
    }
}

# If we've got credentials, let's serve the file up.
if (    ( defined $session{'CurrentUser'} )
    and ( $session{'CurrentUser'}->Id ) )
{

    # Process per-page global callbacks
    $m->comp( '/Elements/Callback', %ARGS );

    # If the user isn't privileged, they can only see SelfService
    if ( not $session{'CurrentUser'}->Privileged ) {

        # if the user is trying to access a ticket, redirect them
        if (    $m->request_comp->path =~ '^(/+)Ticket/Display.html'
            and $ARGS{'id'} )
        {
            $m->comp( "/SelfService/Display.html", %ARGS );
            $m->comp( "/Elements/Footer",          %ARGS );
            $m->abort();
        }

        # otherwise, drop the user at the SelfService default page
        elsif ( $m->base_comp->path !~ '^(/+)SelfService/' ) {
            $m->comp('/SelfService/index.html');
            $m->abort();
        }
        else {
            $m->comp( { base_comp => $m->request_comp }, $m->fetch_next, %ARGS);
        }
    }
    else {
        $m->comp( { base_comp => $m->request_comp }, $m->fetch_next, %ARGS);
    }
}

# If we have no credentials
else {
    $m->comp( '/Elements/Login', %ARGS );
    $m->abort();
}

if ($RT::StatementLog) {
    my @log = $RT::Handle->SQLStatementLog;
    $RT::Handle->ClearSQLStatementLog;
    for my $stmt (@log) {
        my ( $time, $sql, $bind, $duration ) = @{$stmt};
        my @bind;
        if ( ref $bind ) {
            @bind = @{$bind};
        }
        else {

            # Older DBIx-SB
            $duration = $bind;
        }
        $RT::Logger->log(
            level   => $RT::StatementLog,
            message => "SQL(" . sprintf( "%.2f", $duration ) . "s): $sql;"
                . (
                @bind ? "  [ bound values: @{[map{qq|'$_'|} @bind]} ]" : ""
                )
        );
    }
}

</%INIT>
<& /Elements/Footer, %ARGS &>
<%ARGS>
$user => undef
$pass => undef
$menu => undef
</%ARGS>
