# Copyright (c) vhffs project and its contributors
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without 
# modification, are permitted provided that the following conditions 
# are met:
#
# 1. Redistributions of source code must retain the above copyright 
#   notice, this list of conditions and the following disclaimer.
#2. Redistributions in binary form must reproduce the above copyright
#   notice, this list of conditions and the following disclaimer in 
#   the documentation and/or other materials provided with the 
#   distribution.
#3. Neither the name of vhffs nor the names of its contributors 
#   may be used to endorse or promote products derived from this 
#   software without specific prior written permission.
#
#THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
#"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
#LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 
#FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 
#COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 
#INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
#BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
#LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
#CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
# POSSIBILITY OF SUCH DAMAGE.

package Vhffs::User;

#Vhffs::User written by sod` <dieu AT gunnm DOT org>
#Vhffs::User implements Unix authentification as an module of Vhffs.
#You can manage user with Unix.
#libnss-postgresql must be used with it to have an account on a machine with the database

use base qw(Vhffs::Object);
use strict;
use utf8;
use DBI;
use POSIX qw(locale_h);
use locale;
use Locale::gettext;
use Vhffs::Group;
use Vhffs::Functions;
use Vhffs::Acl;

=pod

=head1 NAME

Vhffs::User - Vhffs Interface to handle *NIX users

=head1 SYNOPSIS

	use Vhffs::Main;
	$vhffs = init Vhffs::Main;
	$user = Vhffs::User::get_by_username( $vhffs , "myuser" );
	if( defined $user )
	{
		print "User exists\n";
	}
	else
	{
		print "User does not exist\n";
	}
	...
    $user = Vhffs::User::create( $vhffs, 'myuser', 'apassword', 0, 'myuser@vhffs.org');
	if( defined $user )
	{
		print "User created";
	}
	else
	{
		print "User error\n";
	}
	...
	print "Username: $user->get_username";
	...
	if( $user->commit > 0 )
	{
		print "Update user status : succesfull\n";
	}

=head1 METHODS

=cut


sub _new {
    my ($class, $main, $uid, $gid, $oid, $username, $passwd, $homedir, $shell, $admin, $firstname, $lastname, $address, $zipcode, $city, $country, $mail, $gpg_key, $note, $language, $theme, $lastloginpanel, $ircnick, $date_creation, $description, $state) = @_;
    my $self = $class->SUPER::_new($main, $oid, $uid, $gid, $date_creation, $description, '', $state, Vhffs::Constants::TYPE_USER);
    return undef unless(defined $self);
    $self->{uid} = $uid;
    $self->{gid} = $gid;
    $self->{username} = $username;
    $self->{passwd} = $passwd;
    $self->{homedir} = $homedir;
    $self->{shell} = $shell;
    $self->{admin} = $admin;
    $self->{firstname} = $firstname;
    $self->{lastname} = $lastname;
    $self->{address} = $address;
    $self->{zipcode} = $zipcode;
    $self->{city} = $city;
    $self->{country} = $country;
    $self->{mail} = $mail;
    $self->{gpg_key} = $gpg_key;
    $self->{note} = $note;
    $self->{language} = $language;
    $self->{theme} = $theme;
    $self->{lastloginpanel} = $lastloginpanel;
    $self->{ircnick} = $ircnick;
    return $self;
}

=pod

=head2 exists

    print "User $username already exists\n" if(Vhffs::User::exists($username));

Indicates wether an username is already taken.

=cut

sub exists
{
    my ($main, $username) = @_;

    return ($main->get_db_object()->do('SELECT uid FROM vhffs_users WHERE username = ?', undef, $username) > 0);
}

=pod

=head2 check_username

    print 'Username valid' if(Vhffs::User::check_username($username));

returns false if username is not valid (length not between 3 and 12, name not
composed of alphanumeric chars)

=cut

sub check_username($) {
    my $username = shift;
    return ( defined($username) && ($username =~ /^[a-z0-9]{3,12}$/) );
}

=pod

=head2 create

    my $u = Vhffs::User::create($main, $username, $password, $admin,
                    $mail, $firstname, $lastname, $city, $zipcode,
                    $country, $address, $gpg_key);

Create in DB and return a fully functional user.

=cut

sub create {
    my ( $main, $username, $password, $admin, $mail, $firstname, $lastname, $city, $zipcode, $country, $address, $gpg_key ) = @_;
    return undef unless check_username($username);
    use Vhffs::Functions;

    my $userconf = $main->get_config->get_users;
    my $badusers = Vhffs::Functions::readfile( $userconf->{'bad_username_file'} );
    my $user;

    foreach ( @{$badusers} ) {
        return undef if ( $_ eq $username );
    }

    my $dbh = $main->get_db_object;
    # Localize RaiseError so it get restored after we finish
    # With this enabled, DBI automagically call die if a
    # query goes wrong.         
    local $dbh->{RaiseError} = 1;
    $dbh->begin_work;
    eval {
        # object(owner_uid) references user(uid) and user(object_id) object(object_id)
        # so we have to tell pg that constraints shouldn't be checked before the end
        # of transaction
        $dbh->do('SET CONSTRAINTS ALL DEFERRED');
        
        my ($uid) = $dbh->selectrow_array('SELECT nextval(\'vhffs_users_uid_seq\')');
        my ($gid) = $dbh->selectrow_array('SELECT nextval(\'vhffs_groups_gid_seq\')');
        
        # Create corresponding object
	# -- TODO, user moderation (easy to do now)
        my $parent = Vhffs::Object::create($main, $uid, $gid, '', Vhffs::Constants::WAITING_FOR_CREATION, Vhffs::Constants::TYPE_USER);
        die('Error creating parent') unless (defined $parent);

        # Insert base information
        $admin = 0 unless (defined $admin);
        $password = Vhffs::Functions::generate_random_password if( not defined $password or $password eq '' );
        my $homedir = Vhffs::Functions::hash_homename( $username  , $main );

        my $sth = $dbh->prepare('INSERT INTO vhffs_users (uid, gid, username, shell, passwd, homedir, admin, firstname, lastname, address, zipcode, city, country, mail, gpg_key, note, language, theme, lastloginpanel, object_id) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0, ?, NULL, NULL, ?)');
        $sth->execute($uid, $gid, $username, $userconf->{'default_shell'}, Vhffs::Functions::password_encrypt($password), $homedir, $admin, $firstname, $lastname, $address, $zipcode, $city, $country, $mail, $gpg_key, Vhffs::Constants::DEFAULT_LANG, $parent->get_oid);
        
        my $group = Vhffs::Group::create($main, $username, undef, $uid, $gid);
        die('Error creating group') unless (defined $group);
        $group->set_status(Vhffs::Constants::ACTIVATED);
	$group->set_quota( $userconf->{'default_quota'} || 1 );
        $group->commit;

        $dbh->commit;
        $user = get_by_uid($main, $uid);
    };

	if($@) {
		warn "Error creating user : $@\n";
		$dbh->rollback;
		undef $user;
	}
	else {
		my $subject = sprintf("Welcome on %s", $main->get_config->get_host_name );
		my $content = sprintf("Hello %s %s, Welcome on %s\n\nHere are your login information :\nUser: %s\nPassword: %s\n\nYour account is NOT created yet, you cannot login on the panel now, you\nare going to receive another mail in a few minutes upon account creation.\n\n%s Administrators\n", $user->get_firstname, $user->get_lastname, $main->get_config->get_host_name, $user->get_username, $password , $main->get_config->get_host_name );
		$user->send_mail_user( $subject, $content );
	}

	return $user;
}

#modify a user
#use as this : modify_user($db , username , field , value);
#Example : modify_user($db , "soda" , "shell" , "/bin/bash");
#
# DEPRECATED WILL BE REMOVED use set_xxx instead
#
#
sub modify
{
    my $field;
    my $value;
    my $self;
    my $request;

    ( $self , $field , $value ) = @_;
    
    return -1 if( $field eq "uid" );
    return -2 if( $field eq "username" );


    if ( $field eq "passwd" )
    {    	
        $request = $self->{'db'}->prepare("UPDATE vhffs_users SET passwd='" + Vhffs::Main::password_encrypt $value + "' WHERE uid='$self->{'uid'}'") or return -3;
	
    }
    else
    {
        $request = $self->{'db'}->prepare("UPDATE vhffs_users SET $field='$value' WHERE uid='$self->{'uid'}'") or return -3;
    }
    
    $request->execute() or return -4;
}

sub commit
{
    my $self = shift;

    #Exec the super method !

    $self->{'shell'} = $self->{'main'}->get_config->get_users->{'default_shell'} if (! defined $self->{'shell'} );
    $self->{'status'} = 'I' if ( ! defined $self->{'status'} );
    $self->{'admin'} = 0 if ( ! defined $self->{'admin'} );
	return -1 if ( ( ! defined $self->{'passwd'} ) || ( $self->{'passwd'} eq "" ) );
    
    return -1 if( $self->SUPER::commit < 0 );
    
    my $sql = 'UPDATE vhffs_users SET shell = ?, passwd = ?, admin = ?, firstname = ?, lastname = ?, address = ?, zipcode = ?, country = ?, mail = ?, city = ?, gpg_key = ? , note = ? , lastloginpanel = ? , ircnick = ? WHERE uid = ?';
    my $sth = $self->{db}->prepare($sql) or return -1;
    $sth->execute($self->{'shell'}, $self->{'passwd'}, $self->{'admin'}, 
        $self->{'firstname'}, $self->{'lastname'}, $self->{'address'}, $self->{'zipcode'},
        $self->{'country'}, $self->{'mail'}, $self->{'city'}, $self->{'gpg_key'}, $self->{'note'}, $self->{'lastloginpanel'},
        $self->{'ircnick'}, $self->{'uid'}) or return -3;

    return 1;
}




#Delete a user in the database
sub delete
{
    my $request;
    my $self;

    $self = shift;

    return -1 if ( ! defined $self->{'username'} );
    return -1 if ( ! defined $self->{'uid'} );
    return -2 if ( $self->{'group'}->delete < 0 );

	# delete mail user if mail_user is enabled
	use Vhffs::Services::MailUser;
	my $mu = init Vhffs::Services::MailUser( $self->{'main'} , $self );
	if( defined $mu )
	{
		$mu->delbox;
		$mu->delforward;
	}

	# remove subscription from newsletter
	use Vhffs::Services::Newsletter;
	my $newsletter = init Vhffs::Services::Newsletter( $self->{'main'} , $self );
	$newsletter->del if defined $newsletter;

    # User references corresponding object with an ON DELETE cascade foreign key
    # so we don't even need to delete user
    # rows that reference this user will be deleted by foreign keys constraints
	return $self->SUPER::delete;
}


# Delete this user with a notice mail
sub pendingdeletion_withmail
{
	my $self = shift;

	my $vhffs = $self->{main};

	# TODO: write a beautiful module for INTL
	bindtextdomain('vhffs', '%localedir%');
	textdomain('vhffs');

	my $prevlocale = setlocale( LC_ALL );
	setlocale( LC_ALL , $self->get_lang );

	my $mail = sprintf(
		gettext( "Hello %s %s,\n\nYour account (%s) on %s has been deleted.\n\nThis is because it was left unused for a long time\n\nDon't be upset, create it again and reply to this email to tell us\nwhy you really need to keep your account.\nIf you need further information, just reply to this email !\n\nCheers,\nThe moderator team\n\n---------\n%s\n%s\n" ) ,

		$self->get_firstname,
		$self->get_lastname,
		$self->get_username,
		$vhffs->get_config->get_host_name,
		$vhffs->get_config->get_host_name,
		$vhffs->get_config->get_panel->{url}
		);

	my $subject = sprintf(
		gettext('Your account (%s) on %s has been deleted because it was unused for a long time'),
		$self->get_username,
		$vhffs->get_config->get_host_name
		);

	Vhffs::Functions::send_mail( $vhffs , $vhffs->get_config->get_moderator_mail , $self->get_mail , $vhffs->get_config->get_mailtag , $subject , $mail );

	setlocale( LC_ALL , $prevlocale );

	$self->set_status( Vhffs::Constants::TO_DELETE );
	$self->commit;
}


sub uid_exists
{
    my ( $dbh , $uid ) = @_;
    my $request;

    return -1 if ( !defined $uid);

    $request = $dbh->prepare('SELECT COUNT(*) FROM vhffs_users WHERE uid=?');
    $request->execute($uid);
    
    my ($result) = $request->fetchrow_array();
    return $result;
}


sub check_password
{
	use Crypt::PasswdMD5;

	my $self = shift;
	my $clearpass = shift;

	my $dbpass = $self->get_password;
	return 1 if( $dbpass eq unix_md5_crypt($clearpass, $dbpass) );
	return 0;		
}


sub send_mail_user
{
	use Vhffs::Functions;
	my ( $user, $subject, $content ) = @_;

	my $vhffs = $user->{'main'};

	my $from;
	$from = $vhffs->get_config->get_master_mail;
	$from = "vhffs\@nosrc.com" if( ! defined $from );

	Vhffs::Functions::send_mail( $vhffs , $from , $user->get_mail , $vhffs->get_config->get_mailtag , $subject , $content );
}


#
# Some accessors
################

sub get_username
{
    my $self = shift;
    return $self->{'username'};
}

sub get_firstname
{
    my $self = shift;
    return $self->{'firstname'};
}

sub get_lastname
{
    my $self = shift;
    return $self->{'lastname'};
}

sub get_city
{
    my $self = shift;
    return $self->{'city'};
}

sub get_country
{
     my $self = shift;
     return $self->{'country'};
}

sub get_gpg
{
    my $self = shift;
    return $self->{'gpg_key'};
}

sub get_zipcode
{
    my $self = shift;
    return $self->{'zipcode'};
}

sub get_home
{
    my $self = shift;
    return $self->{'homedir'}
}

sub get_password
{
    my $self = shift;
    return $self->{'passwd'}
}

sub get_uid
{
    my $self = shift;
    return $self->{'uid'} if( defined $self->{'uid'} );
    return -1;
}

sub get_gid
{
    my $self = shift;
    return $self->{'gid'} if( defined $self->{'gid'} );
    return -1;
}

sub get_lang
{
    my $self = shift;
    return $self->{language};
}

sub get_mail
{
    my $self = shift;
    return $self->{'mail'};
}

sub get_theme
{
    my $self = shift;
    return $self->{theme};
}

sub get_address
{
    my $self = shift;
    return $self->{'address'};
}

sub get_group
{
    my $self = shift;
    return $self->{'group'};
}

sub get_shell
{
    my $self = shift;
    return $self->{'shell'};
}

sub get_gpgkey
{
    my $self = shift;
    return( $self->{'gpg_key'} );
}

sub get_note
{
    my $self = shift;
    return( $self->{'note'} );
}

sub get_lastloginpanel
{
    my $self = shift;
    return( $self->{'lastloginpanel'} );
}

sub get_ircnick
{
    my $self = shift;
    return( $self->{'ircnick'} );
}

sub set_shell
{
    my $self = shift;
    my $value = shift;
    $self->{'shell'} = $value;
}

sub set_firstname
{
    my $self = shift;
    my $value = shift;
    $self->{'firstname'} = $value;
}

sub set_lastname
{
    my $self = shift;
    my $value = shift;
    $self->{'lastname'} = $value;
}

sub set_city
{
    my $self = shift;
    my $value = shift;
    $self->{'city'} = $value;
}

sub set_zipcode
{
    my $self = shift;
    my $value = shift;    
    $self->{'zipcode'} = $value;
}

sub set_country
{
    my $self = shift;
    my $value = shift;
    $self->{'country'} = $value;
}

sub set_address
{
    my $self = shift;
    my $value = shift;
    $self->{'address'} = $value;
}

sub set_mail
{
	use Vhffs::Functions;
   	my $self  = shift;
	my $value = shift;
   
	if( Vhffs::Functions::valid_mail( $value ) )
	{
		$self->{'mail'} = $value;
		return 0;
	}
	else
	{
		return -1;
	}
}

sub set_gpgkey
{
    my $self = shift;
    my $value = shift;
    $self->{'gpg_key'} = $value;
}

sub set_note
{
    my $self = shift;
    my $value = shift;
    $self->{'note'} = $value;
}

sub set_password
{
    use Vhffs::Functions;
    
    my $self = shift;
    my $value = shift;
    $self->{'passwd'} = Vhffs::Functions::password_encrypt( $value );
}

sub update_lastloginpanel
{
	my $self = shift;
	$self->{'lastloginpanel'} = time();
}

sub set_ircnick
{
    my $self = shift;
    my $value = shift;
    $self->{'ircnick'} = $value;
}

sub set_admin
{
    my ( $self , $value ) = @_;
    $self->{'admin'} = $value;
}

=pod

See C<Vhffs::Object::get_label>.

=cut

sub get_label
{
    my $self = shift;
    return $self->{username};
}

sub is_admin
{
	my $self = shift;
	if( ( defined $self->{'admin'} ) && ( $self->{'admin'} == Vhffs::Constants::USER_ADMIN  ) )
	{
		return 1;
	}
	else
	{
		return 0;
	}
}


sub is_moderator
{
	my $self = shift;
	if( ( defined $self->{'admin'} ) && ( $self->{'admin'} == Vhffs::Constants::USER_MODERATOR  ) )
	{
		return 1;
	}
	else
	{
		return 0;
	}
}

sub get_permissions {
    my $self = shift;
    return $self->{admin};
}

sub get_name_by_uid
{   
    my $main = shift;
    my $uid = shift;
    my $request = $main->{'db'}->prepare('SELECT username FROM vhffs_users where uid=?') or return -2;
    my $rows = $request->execute($uid);
    
    if( $rows == 0 )
    { 
        return( "nobody" );
    }
    else
    {
        my ($result) = $request->fetchrow_array();
        return $result;
    }

}


sub getall
{
	my $vhffs = shift;
	my $state = shift;
	my $name = shift;
	my @users;
	my @params;
	return unless defined $vhffs;

	my $query = 'SELECT username FROM vhffs_users vu INNER JOIN vhffs_object o ON o.object_id=vu.object_id WHERE o.object_id=vu.object_id ';

	if(defined $name) {
		$name = '%'.$name.'%';
		$query .= ' AND ( vu.username LIKE ? OR vu.firstname LIKE ? OR vu.lastname LIKE ? )';
		push @params, $name, $name, $name;
	}
	(push(@params, $state), $query.= ' AND o.state=?') if( defined $state );
	
	$query .= 'ORDER BY username';
	
	my $request = $vhffs->{'db'}->prepare( $query );
	$request->execute(@params);
	while( my ($name) = $request->fetchrow_array )
	{   
		my $user = Vhffs::User::get_by_username( $vhffs , $name );
		push( @users , $user ) if( defined $user );
	}

	return \@users;
}


sub get_unused_accounts
{
	my $vhffs = shift;
	my $age = shift;
	my @users;
	return unless defined $vhffs;

	my $ts = time() - $age;

	my $query = 'SELECT u.uid FROM vhffs_users u INNER JOIN vhffs_object o ON o.object_id=u.object_id WHERE u.admin=? AND o.state=? AND o.date_creation<? AND ( u.lastloginpanel IS NULL OR u.lastloginpanel<? ) AND u.uid NOT IN (SELECT u.uid FROM vhffs_users u INNER JOIN vhffs_user_group ug ON u.uid=ug.uid)';

	my $request = $vhffs->{'db'}->prepare( $query );
	$request->execute( Vhffs::Constants::USER_NORMAL , Vhffs::Constants::ACTIVATED , $ts , $ts );
	while( my $uid = $request->fetchrow_array )
	{   
		my $user = Vhffs::User::get_by_uid( $vhffs , $uid );
		push( @users , $user ) if( defined $user );
	}

	return \@users;
}


sub get_groups
{
	my $self = shift;

	my @groups;
	my $uid = $self->get_uid;
	my $query = 'SELECT g.groupname FROM vhffs_groups g INNER JOIN vhffs_user_group ug ON g.gid=ug.gid WHERE ug.uid=? ORDER BY g.groupname';
	my $request = $self->{'db'}->prepare( $query );
	return undef if( ! $request->execute($uid) );
	while( my ($groupname) = $request->fetchrow_array )
	{
		my $group = Vhffs::Group::get_by_groupname( $self->{'main'} , $groupname );
		push( @groups , $group ) if( defined($group) );
	}
	return \@groups;
}

=head2 can_view

    die("You are not allowed to view this object\n")
                                unless($user->can_view($object));

Returns true if the user on which the method is called can view the given
object.

=cut

sub can_view
{
	my ($self, $o) = @_;
	return 0 unless( $o->get_status == Vhffs::Constants::ACTIVATED || $self->is_admin || $self->is_moderator );
	return ( Vhffs::Acl::get_perm( $self->get_main, $o, $self )  >=  Vhffs::Constants::ACL_VIEW );
}


=head2 can_modify

    die("You are not allowed to modify this object\n")
                                unless($user->can_modify($object));

Returns true if the user on which the method is called can modify the given
object.

=cut

sub can_modify
{
	my ($self, $o) = @_;
	return 0 unless( $o->get_status == Vhffs::Constants::ACTIVATED || $self->is_admin || $self->is_moderator );
	return ( Vhffs::Acl::get_perm( $self->get_main, $o, $self )  >=  Vhffs::Constants::ACL_MODIFY );
}


=head2 can_manageacl

    die("You are not allowed to manage acl on this object\n")
                                unless($user->can_manageacl($object));

Returns true if the user on which the method is called can modify ACLs on the given object.

=cut

sub can_manageacl
{
	my ($self, $o) = @_;
	return ( Vhffs::Acl::get_perm( $self->get_main, $o, $self )  >=  Vhffs::Constants::ACL_MANAGEACL );
}


=head2 can_delete

    die("You are not allowed to delete this object\n")
                                unless($user->can_delete($object));

Returns true if the user on which the method is called can delete the given
object.

=cut

sub can_delete
{
	my ($self, $o) = @_;
	return 0 unless( $o->get_status == Vhffs::Constants::ACTIVATED || $self->is_admin || $self->is_moderator );
	return ( Vhffs::Acl::get_perm( $self->get_main, $o, $self )  >=  Vhffs::Constants::ACL_DELETE );
}


=head2 get_perm

    $perm = $user->get_perm($object);

Returns the permission level of the user on the given object.

=cut

sub get_perm
{
	my ($self, $o) = @_;
	return Vhffs::Acl::get_perm( $self->get_main, $o, $self );
}



sub have_activegroups
{
	my $self = shift;

	my $uid = $self->get_uid;

	my $query = 'SELECT COUNT(g.groupname) FROM vhffs_groups g, vhffs_user_group ug, vhffs_object o WHERE ug.uid=? AND g.gid=ug.gid AND o.object_id=g.object_id AND o.state='.Vhffs::Constants::ACTIVATED;
	my $request = $self->{'db'}->prepare( $query );
	return -1 if( !$request->execute($uid) );

	my $row = $request->fetchrow_arrayref;
	return -1 if ( ! defined $row );
	return $row->[0];
}

sub get_by_uid {
    my ($main, $uid) = @_;
    my $query = 'SELECT u.uid, u.gid, u.object_id, u.username, u.passwd, u.homedir, u.shell, u.admin, u.firstname, u.lastname, u.address, u.zipcode, u.city, u.country, u.mail, u.gpg_key, u.note, u.language, u.theme, u.lastloginpanel, u.ircnick, o.date_creation, o.description, o.state FROM vhffs_users u INNER JOIN vhffs_object o ON o.object_id = u.object_id WHERE u.uid = ?';
    my $dbh = $main->get_db_object;
    my @params = $dbh->selectrow_array($query, undef, $uid);
    return undef unless(@params);
    my $user = _new Vhffs::User($main, @params);
    $user->{group} = Vhffs::Group::get_by_gid($main, $user->get_gid);
    return $user;
}

=pod

=head2 get_by_username

    my $user = Vhffs::User::get_by_username($main, $username);
    die('User not found') unless defined($user);

Fetches an user using its username. Returned user is fully functional.

=cut

sub get_by_username {
    my ($main, $username) = @_;
    my $query = 'SELECT u.uid, u.gid, u.object_id, u.username, u.passwd, u.homedir, u.shell, u.admin, u.firstname, u.lastname, u.address, u.zipcode, u.city, u.country, u.mail, u.gpg_key, u.note, u.language, u.theme, u.lastloginpanel, u.ircnick, o.date_creation, o.description, o.state FROM vhffs_users u INNER JOIN vhffs_object o ON o.object_id = u.object_id WHERE u.username = ?';
    my $dbh = $main->get_db_object;
    my @params = $dbh->selectrow_array($query, undef, $username);
    return undef unless(@params);
    my $user = _new Vhffs::User($main, @params);
    $user->{group} = Vhffs::Group::get_by_gid($main, $user->get_gid);
    return $user;

}

=head2 get_by_ircnick

    my $user = Vhffs::User::get_by_ircnick($main, $ircnick);
    die('User not found') unless defined($user);

Fetches an user using its IRC nick. Returned user is fully functional.

=cut

sub get_by_ircnick {
    my ($main, $ircnick) = @_;
    my $query = 'SELECT u.uid, u.gid, u.object_id, u.username, u.passwd, u.homedir, u.shell, u.admin, u.firstname, u.lastname, u.address, u.zipcode, u.city, u.country, u.mail, u.gpg_key, u.note, u.language, u.theme, u.lastloginpanel, u.ircnick, o.date_creation, o.description, o.state FROM vhffs_users u INNER JOIN vhffs_object o ON o.object_id = u.object_id WHERE u.ircnick = ?';
    my $dbh = $main->get_db_object;
    my @params = $dbh->selectrow_array($query, undef, $ircnick);
    return undef unless(@params);
    my $user = _new Vhffs::User($main, @params);
    $user->{group} = Vhffs::Group::get_by_gid($main, $user->get_gid);
    return $user;

}

=head2 fill_object

See C<Vhffs::Object::fill_object>.

=cut

sub fill_object {
# We just add some specific fields
    my ($class, $obj) = @_;
    my $sql = q{SELECT uid, gid, username, shell, passwd, homedir, admin,
        firstname, lastname, address, zipcode, city, country, mail, gpg_key,
        note, language, theme FROM vhffs_users WHERE object_id = ?};
    return $class->SUPER::_fill_object($obj, $sql);
}

1;

__END__

=head1 SEE ALSO

Vhffs::Group , Vhffs::Main , Vhffs::Constants

=head1 AUTHORS

Julien Delange <julien at tuxfamily dot org>

Sebastien Le Ray <beuss at tuxfamily dot org>
