# BEGIN LICENSE BLOCK
# 
# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
# 
# (Except where explictly superceded by other copyright notices)
# 
# 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.
# 
# Unless otherwise specified, all modifications, corrections or
# extensions to this work which alter its source code become the
# property of Best Practical Solutions, LLC when submitted for
# inclusion in the work.
# 
# 
# END LICENSE BLOCK
=head1 NAME

  RT::CurrentUser - an RT object representing the current user

=head1 SYNOPSIS

  use RT::CurrentUser


=head1 DESCRIPTION


=head1 METHODS


=begin testing

ok (require RT::CurrentUser);

=end testing

=cut


package RT::CurrentUser;

use RT::Record;
use RT::I18N;

use strict;
use vars qw/@ISA/;
@ISA= qw(RT::Record);

# {{{ sub _Init 

#The basic idea here is that $self->CurrentUser is always supposed
# to be a CurrentUser object. but that's hard to do when we're trying to load
# the CurrentUser object

sub _Init  {
  my $self = shift;
  my $Name = shift;

  $self->{'table'} = "Users";

  if (defined($Name)) {
    $self->Load($Name);
  }
  
 # $self->CurrentUser($self);

}
# }}}

# {{{ sub Create

sub Create {
    my $self = shift;
    return (0, $self->loc('Permission Denied'));
}

# }}}

# {{{ sub Delete

sub Delete {
    my $self = shift;
    return (0, $self->loc('Permission Denied'));
}

# }}}

# {{{ sub UserObj

=head2 UserObj

  Returns the RT::User object associated with this CurrentUser object.

=cut

sub UserObj {
    my $self = shift;
    
	use RT::User;
	my $user = RT::User->new($self);

	unless ($user->Load($self->Id)) {
	    $RT::Logger->err($self->loc("Couldn't load [_1] from the users database.\n", $self->Id));
	}
    return ($user);
}
# }}}

# {{{ sub PrincipalObj 

=head2 PrincipalObj

    Returns this user's principal object.  this is just a helper routine for
    $self->UserObj->PrincipalObj

=cut

sub PrincipalObj {
    my $self = shift;
    return($self->UserObj->PrincipalObj);
}


# }}}


# {{{ sub PrincipalId 

=head2 PrincipalId

    Returns this user's principal Id.  this is just a helper routine for
    $self->UserObj->PrincipalId

=cut

sub PrincipalId {
    my $self = shift;
    return($self->UserObj->PrincipalId);
}


# }}}


# {{{ sub _Accessible 
sub _Accessible  {
  my $self = shift;
  my %Cols = (
	      Name => 'read',
	      Gecos => 'read',
	      RealName => 'read',
	      Password => 'neither',
          Lang => 'read',
	      EmailAddress => 'read',
	      Privileged => 'read',
	      IsAdministrator => 'read'
	     );
  return($self->SUPER::_Accessible(@_, %Cols));
}
# }}}

# {{{ sub LoadByEmail

=head2 LoadByEmail

Loads a User into this CurrentUser object.
Takes the email address of the user to load.

=cut

sub LoadByEmail  {
    my $self = shift;
    my $identifier = shift;

    $identifier = RT::User::CanonicalizeEmailAddress(undef, $identifier);
        
    $self->LoadByCol("EmailAddress",$identifier);
    
}
# }}}

# {{{ sub LoadByGecos

=head2 LoadByGecos

Loads a User into this CurrentUser object.
Takes a unix username as its only argument.

=cut

sub LoadByGecos  {
    my $self = shift;
    my $identifier = shift;
        
    $self->LoadByCol("Gecos",$identifier);
    
}
# }}}

# {{{ sub LoadByName

=head2 LoadByName

Loads a User into this CurrentUser object.
Takes a Name.
=cut

sub LoadByName {
    my $self = shift;
    my $identifier = shift;
    $self->LoadByCol("Name",$identifier);
    
}
# }}}

# {{{ sub Load 

=head2 Load

Loads a User into this CurrentUser object.
Takes either an integer (users id column reference) or a Name
The latter is deprecated. Instead, you should use LoadByName.
Formerly, this routine also took email addresses. 

=cut

sub Load  {
  my $self = shift;
  my $identifier = shift;

  #if it's an int, load by id. otherwise, load by name.
  if ($identifier !~ /\D/) {
    $self->SUPER::LoadById($identifier);
  }

  elsif (UNIVERSAL::isa($identifier,"RT::User")) {
         # DWIM if they pass a user in
         $self->SUPER::LoadById($identifier->Id);
  } 
  else {
      # This is a bit dangerous, we might get false authen if somebody
      # uses ambigous userids or real names:
      $self->LoadByCol("Name",$identifier);
  }
}

# }}}

# {{{ sub IsPassword

=head2 IsPassword

Takes a password as a string.  Passes it off to IsPassword in this
user's UserObj.  If it is the user's password and the user isn't
disabled, returns 1.

Otherwise, returns undef.

=cut

sub IsPassword { 
  my $self = shift;
  my $value = shift;
  
  return ($self->UserObj->IsPassword($value)); 
}

# }}}

=head2 Authenticate

Takes $password, $created and $nonce, and returns a boolean value
representing whether the authentication succeeded.

If both $nonce and $created are specified, treat $password as:

    md5_base64(md5_base64($real_password) . $created . $nonce)

and validate it, where $created is in unix timestamp format, and
$nonce is a random string no longer than 32 bytes.

Otherwise, simply pass $password to IsPassword().

=cut

sub Authenticate { 
    my ($self, $password, $created, $nonce) = @_;

    return $self->IsPassword($password)
        unless $password and $created and abs($created - time) < 864000 and $nonce;

    require Digest::MD5;
    require MIME::Base64;

    my $server_pass = $self->UserObj->__Value('Password') or return;
    if (length($server_pass) == 32) {
	$server_pass = MIME::Base64::encode_base64(pack('H*', $server_pass));
	$server_pass =~ s/\s*=+\s*$//;
    }

    Digest::MD5::md5_base64($server_pass . $created . $nonce);
    return ($password eq Digest::MD5::md5_base64($server_pass . $created . $nonce));
}

# }}}


# {{{ sub Privileged

=head2 Privileged

Returns true if the current user can be granted rights and be
a member of groups.

=cut

sub Privileged {
    my $self = shift;
    return ($self->UserObj->Privileged());
}

# }}}


# {{{ sub HasRight

=head2 HasRight

calls $self->UserObj->HasRight with the arguments passed in

=cut

sub HasRight {
  my $self = shift;
  return ($self->UserObj->HasRight(@_));
}

# }}}

# {{{ Localization

=head2 LanguageHandle

Returns this current user's langauge handle. Should take a language
specification. but currently doesn't

=begin testing

ok (my $cu = RT::CurrentUser->new('root'));
ok (my $lh = $cu->LanguageHandle);
ok ($lh != undef);
ok ($lh->isa('Locale::Maketext'));
ok ($cu->loc('TEST_STRING') eq "Concrete Mixer", "Localized TEST_STRING into English");
ok ($lh = $cu->LanguageHandle('fr'));
ok ($cu->loc('Before') eq "Avant", "Localized TEST_STRING into Frenc");

=end testing

=cut 

sub LanguageHandle {
    my $self = shift;
    if (   ( !defined $self->{'LangHandle'} )
        || ( !UNIVERSAL::can( $self->{'LangHandle'}, 'maketext' ) )
        || (@_) ) {
        if ( (!$RT::SystemUser || $self->id == $RT::SystemUser->id() )) {
            @_ = ($RT::SystemLanguage || 'en-US');
        }

        elsif ( $self->Lang ) {
            push @_, $self->Lang;
        }
        $self->{'LangHandle'} = RT::I18N->get_handle(@_);
    }

    # Fall back to english.
    unless ( $self->{'LangHandle'} ) {
        die "We couldn't get a dictionary. Nye mogu naidti slovar. No puedo encontrar dictionario.";
    }
    return ( $self->{'LangHandle'} );
}

sub loc {
    my $self = shift;
    return '' if $_[0] eq '';

    my $handle = $self->LanguageHandle;

    if (@_ == 1) {
	# pre-scan the lexicon hashes to return _AUTO keys verbatim,
	# to keep locstrings containing '[' and '~' from tripping over Maketext
	return $_[0] unless grep { exists $_->{$_[0]} } @{ $handle->_lex_refs };
    }

    return $handle->maketext(@_);
}

sub loc_fuzzy {
    my $self = shift;
    return '' if $_[0] eq '';

    # XXX: work around perl's deficiency when matching utf8 data
    return $_[0] if Encode::is_utf8($_[0]);
    my $result = $self->LanguageHandle->maketext_fuzzy(@_);

    return($result);
}
# }}}


=head2 CurrentUser

Return  the current currentuser object

=cut

sub CurrentUser  {
    my $self = shift;
    return($self);

}

eval "require RT::CurrentUser_Vendor";
die $@ if ($@ && $@ !~ qr{^Can't locate RT/CurrentUser_Vendor.pm});
eval "require RT::CurrentUser_Local";
die $@ if ($@ && $@ !~ qr{^Can't locate RT/CurrentUser_Local.pm});

1;
 
