#[BOFN]###############################################################################
#
#
#Pagenews - a free script to publish news on websites
#Copyright (C) 2004,2005,2006,2007,2008 Philipp Kindt
#
#This file is part of Pagenews.
#
# 	 This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program 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, see <http://www.gnu.org/licenses/>.
#[EOFN]###############################################################################


#this is the Pagenews user authorisation package.

package dnuser;
$userpath = "restricted/authdata";					#folder where user acces file is stored. MAKE SURE THIS IS NOT ACCESSIBLE

$userfile = "$userpath/users.cgi";					#user access file
$referenceuser = "$userpath/reference_user.cgi";		#file to retrive the data for a reference user which will be used to create a new user (see new()  )

require donlibs;
require dnfeeds;					#for check_use_of_posting()
require donsession;

########################################################################################################
#constructor - nothing cool
#if $_[0] is "reference", the reference user db is loaded.
sub new{
	
	my $self = {};
	my $classname = shift();
	bless($self,$classname);
	
	$self->{'dkvp'} = donkvparser->new();
	if($_[0] ne "reference"){
		$self->{'dkvp'}->read($userfile);
	}else{
		$self->{'dkvp'}->read($referenceuser);
		$self->{'dkvp'}->{'write_protection'} = 1;
	};
	return $self;
};


########################################################################################################
#returns the user with username $_[1];
sub get_user{
	my $self = $_[0];
	return $self->{'dkvp'}->{'data'}->[$self->{'dkvp'}->get_index($_[1],"data","username")];
};

########################################################################################################
#gets the %in hash reference by $_[1] and checks if the user has the privillage to acces the current section
#if $_[2] is empty, just the username/session is checked.
#if it contains a section, the access on the current section is checked.
#a section is represented through a key named USE_section, where "section" is the section.
#if the value of this key in the user's block is 1, access is granted
#if $_[3] is "check", instead of error messages a 1 is for OK or a 0 for not ok is retuned
sub check_auth{
	my $self = $_[0];
	my $in = $_[1];
	my $session = donsession->new();
	$donsession::session_timeout = $dnmain::settings->{'session_timeout'};
	my $index = $self->{'dkvp'}->get_index($in->{'username'},"data","username","new");
	if($index == -1){
		#user does not exist -> access denied
		if($_[3] ne "check"){
			donstdlib::error("dnuser_accesdenied","main area");
		}else{
			return 0;					
		};
	};
	
	if($in->{'init_session'} ne "true"){
		#session key mode_ there is a session given
		if($session->check_session($in->{'username'},$in->{'session_key'}) != -1){
			$session->update_session($in->{'username'});
		}else{
			#session key is invalid -> access denied. special message because session might have expired
			donstdlib::error("dnuser_session_expired","main area");		
		}	
		
	#there is no session key => check password and init one if passwords match
	#if they match, a new session will be inialuzed
	}else{	
		if($self->{'dkvp'}->{'data'}->[$index]->{'passwd'} ne $in->{'passwd'}){
			#wrong password => acces denied, no session can be initialized			
			if($_[3] ne "check"){	
				donstdlib::error("dnuser_accesdenied","main area");		
			}else{
				return 0;					
			}
		}else{
				#init a session, add key to global %in, remove init_session tag
				$donparser::in{'session_key'} = $session->init_session($in->{'username'});			
				$donparser::in{'init_session'} = "false";			
		}
	} 
	
	
	#if control reaches this point, user has a valid session key and is authenticated.
		
	#check for the right to use something
	if($_[2] ne ""){
		if($self->{'dkvp'}->get_value($self->{'dkvp'}->{'data'}->[$index]->{'DKVP_BID'},"USE_".$_[2]) != 1){
			#Superuser check:
			if($self->{'dkvp'}->get_value($self->{'dkvp'}->{'data'}->[$index]->{'DKVP_BID'},"USE_ALL") != 1){

				if($_[3] ne "check"){
					donstdlib::error("dnuser_accesdenied",$_[2]);				
				}else{
					return 0;					
				};	
			};		
		};
	};
	return 1;
}

########################################################################################################
#logs out user $_[1]
#does not throw an error message if $_[2] is non-empty
sub log_out{
 	my $session = donsession->new();
	$session->end_session($_[1],$_[2]);
}
########################################################################################################
#checks if the most important fields in the user hash (reference) are set correctly
#user hash ref in $_[1];
#if $_[2] is "new" (or non-empty), the user data will be checked for a new, NON-EXISTING user; otherwise
#it'll be checked for data modification

sub check_user{
	my $self = $_[0];
	my $franzxaver = $_[1];
	if($franzxaver->{'username'} eq ""){
		donstdlib::error("dnuser_invalidname",$franzxaver->{'username'});
	};	

	if($franzxaver->{'username'} =~ m/\"/){
		donstdlib::error("dnuser_invalidname",$franzxaver->{'username'});		
	};

	if($franzxaver->{'username'} =~ m/\n/){
		donstdlib::error("dnuser_invalidname");		
	};

	if($franzxaver->{'passwd'} =~ m/\"/){
		donstdlib::error("dnuser_invalidpasswd");		
	};

	if($franzxaver->{'passwd'} =~ m/\n/){
		donstdlib::error("dnuser_invalidpasswd");		
	};
	
	if(length($franzxaver->{'passwd'}) < 4){
		donstdlib::error("dnuser_invalidpasswd");		
	};
	
	if($_[2] ne ""){
		if($self->{'dkvp'}->get_index($franzxaver->{'username'},"data","username","new") != -1){
			donstdlib::error("dnuser_useralreadyexists",$franzxaver->{'username'});			
		};
	}else{
		if($self->{'dkvp'}->get_index($franzxaver->{'username'},"data","username","new") == -1){
			donstdlib::error("dnuser_invalidname",$franzxaver->{'username'});			
		};	
	};
};


########################################################################################################
#checks if user $_[1] exists
#returns 0 or 1 (exists)

sub check_existance{
my $self = $_[0];
	if($self->{'dkvp'}->get_index($_[1],"data","username","new") != -1){
		return 1;
	};
	return 0;
};
########################################################################################################
################################functions that modify the user db#######################################
########################################################################################################
#creates a new user; user array in $_[1];
sub add_user{
	my $self = $_[0];
	my $myuser = $_[1];
	$self->check_user($myuser,"new");
	my $bid = $self->{'dkvp'}->add_block();
	$self->{'dkvp'}->set_block($bid,$myuser);
	$self->{'dkvp'}->write();
};

########################################################################################################
#sets the hash $_[2] field of user $_[1] to the value $_[3].
#if non-existing field, it'll be added 
sub set_property{
	my $self = $_[0];
	my $user = $self->get_user($_[1]);
	if($_[2] ne "username"){			#ignore writes on username as it might be dangerous
		$user->{$_[2]} = $_[3];
	};
	$self->modify_user($user);
	
};
########################################################################################################
#deletes the field $_[2] (if existing) of user $_[1] 
sub delete_property{
	my $self = $_[0];
	my $user = $self->get_user($_[1]);
	if($_[2] ne "username"){			#ignore writes on username as it might be dangerous
		delete $user->{$_[2]};
	};
	$self->modify_user($user);
	
};
########################################################################################################
#deletes the field $_[1] of all users

sub delete_property_of_all{
	my $self = $_[0];
	my @unames = $self->get_list_of_usernames();
	foreach $uname (@unames){
		$self->delete_property($uname,$_[1]);
	};
};
########################################################################################################
#stores the data in hash reference $_[1] for a user.
#fields that are not in the hash reference are left blank (!)
#if you wish to keep fields that were not set before, $_[2] must be "keep"  
sub modify_user{
	my $self = $_[0];
	my $modified_user = $_[1];
	$self->check_user($modified_user);
	my $original_user = {};
	if($_[2] eq "keep"){	
		$original_user = $self->get_user($modified_user->{'username'});
	};
	
	#update user information
	foreach my $key (keys(%$modified_user)){
		$original_user->{$key} = $modified_user->{$key};
	}
	
	#set new block - $original_user contains now the updated user data
	$index = $self->{'dkvp'}->get_index($modified_user->{'username'},"data","username");
	$self->{'dkvp'}->set_block($self->{'dkvp'}->{'data'}->[$index]->{'DKVP_BID'},$original_user);
	$self->{'dkvp'}->write();
};

########################################################################################################
#permanently removes the user having the username $_[1]
sub remove_user{
	my $self = $_[0];
	my $index = $self->{'dkvp'}->get_index($_[1],"data","username");
	$self->{'dkvp'}->delete_block($self->{'dkvp'}->{'data'}->[$index]->{'DKVP_BID'});
	$self->{'dkvp'}->write();
};

########################################################################################################
#checks if current user may use a certain feed
#causes error message when not.
#$_[1] must be the username,
#$_[2] must be the feed name
#if $_[3] is not empty, instead of an errmessage 1 for allowance or 0 for interdition is returned.
#if $_[4] is not empty, USE_ALL and USE_ALL_FEEDS are ignored.
#if $_[5] is not empty, creating, deleting and moving postings in this feed are checked and prohibited if USE_MOD_FEED in user data is not set  
#in addition, if $_[5] is MOD_ONLY_CHECK if $_[6] contains the [BROWSERPARAM dnadmin_postid]
#passed on to the editor, do not gramble if we do not create a new one, that means if $_[6] is empty

sub check_use_of_feed{
	my $self = $_[0];
	my $username = $_[1];

	my $index = $self->{'dkvp'}->get_index($username,"data","username");

	#superuser check:
	if(($_[4] eq "")&&($self->{'dkvp'}->{'data'}->[$index]->{"USE_ALL"} == 1)){
		return 1;		
	};

	#all feeds check:
	if(($_[4] eq "")&&($self->{'dkvp'}->{'data'}->[$index]->{"USE_ALL_FEEDS"} == 1)){
		return 1;		
	};

	my $useflag = $self->{'dkvp'}->{'data'}->[$index]->{"USE_FEED_".$_[2]};
	if($useflag == 1){
		#check "modify existing postings only"		
		if($_[5] ne ""){
			if($self->{'dkvp'}->{'data'}->[$index]->{"USE_MOD_FEED_".$_[2]} == 1){
				return 1;			
			}else{
				if(($_[5] eq "MOD_ONLY_CHECK")&&($_[6] ne "")&&($_[6] !~ /create/i)){
					return 1;
				}else{
					if($_[3] eq ""){
						&donstdlib::error("dnuser_accesdenied","USE_MOD_FEED_".$_[2]);	
					}else{
						return 0;					
					}				
				}			
			}
		}else{
			return 1;
		}		
	}else{
		if($_[3] eq ""){
			&donstdlib::error("dnuser_accesdenied","USE_FEED_".$_[2]);
		}else{
			return 0;
		};
	};
}; 



########################################################################################################
#checks if current user may edit a certain posting
#causes error message when not.
#$_[1] must be the username,
#$_[2] must be the POST_ID of the feed
#$_[3] must be the feed name
#if $_[4] is not emty, instead of an errmessage 1 for allowance or 0 for interdition is returned.
#if $_[5] is not empty, instead of the id the index is used. 
sub check_use_of_posting{
	my $self = $_[0];
	my $username = $_[1];
	my $pid = $_[2];
	my $feedname = $_[3];

	my $index = $self->{'dkvp'}->get_index($username,"data","username");
	my $feed = dnfeeds->new($feedname);
	
	my $post;
	#create new posting?

	if($_[5] eq ""){

		$post = $feed->get_posting_by_id($pid);

	}else{
		my @posts = $feed->get_postings_by_start_order($pid,1);
		$post = $posts[0];
	};
	my $otheruser = $post->{'poster'};

	#superuser check:
	if($self->{'dkvp'}->{'data'}->[$index]->{"USE_ALL"} == 1){
		return 1;		
	};
	if($username eq $otheruser){
		return 1;
	};
	my $useflag = $self->{'dkvp'}->{'data'}->[$index]->{"USE_edit postings of others"};
	if($useflag == 1){
		return 1;		
	}else{
		if($_[4] eq ""){
			&donstdlib::error("dnuser_accesdenied",donstdlib::get_message("dnuser_post_not_allowed"));
		}else{
			return 0;
		};
	};
}; 



########################################################################################################
#retuns a list of usernames sorted alphabetically
#if $_[1] is not emtpy, only users where the key $_[1] has value $_[2] are returned 
sub get_list_of_usernames{
	my $self = $_[0];
	my @list;
	for(my $cnt = 0; $cnt < $self->{'dkvp'}->{'info'}->{'data'}->{'nrofblocks'}; $cnt++){
		if(($_[1] eq "")||($self->{'dkvp'}->{'data'}->[$cnt]->{$_[1]} eq $_[2])){
			push(@list,$self->{'dkvp'}->{'data'}->[$cnt]->{'username'});
		};
	};
	
	@list = sort(@list);
	return @list;

};

1;

