#[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]###############################################################################


package donstdlib;

#This is the don philippe standard library
#It provides some basic fancy functions
########################################################################################################


#include Fcntl for locking. We have to use "use" because functions are imported
#to get the locking Constants, we have to provide the tag ":flock" to Fcntl's import() - function
use Fcntl ':flock';

#some defines
$errorpath = "messages/errors";
$messagepath = "messages/various";
$stderrfile = "error.err";

########################################################################################################
#Opens and reads a txt file - returns its contents in a scalar 
sub read_file{
	my $filename = shift;
	my $retvalue = open(FHANDLE, $filename);

	#check for success; If not, print message and quit.
	if(!(defined($retvalue))){ 
		hard_error("An error occured in donstdlib::read_file():\n The file $filename could not be opened for reading!\nError occured");
	};
	
	flock(FHANDLE,LOCK_EX) or hard_error("An error occured in donstdlib::read_file():\n A shared lock on $filename could not be established.\nError occured");
	
	my @flines = <FHANDLE>;
	close(FHANDLE) or hard_error("An error occured in donstdlib::read_file():\n The file $filename could not be closed after reading.\nError occured");
	 
	my $rtxt;
	foreach my $fline (@flines){
		$rtxt .= $fline;
	};
	return $rtxt;
};

########################################################################################################
#writes text $_[1] in file $_[0].
#if $_[2] is non_empty, it is written in binmode
sub write_file
{
	my $filename = $_[0];
	#check if filename is valid. this solution is not perfect yet.
	
	check_filename($filename,"allowed");
	
	#if file does not exist, create it. this could be resolved a bit more efficiently
	my $retvalue = open(FHANDLER, "$filename");
	close(FHANDLER);
	if(!(defined($retvalue))){
		open(FHANDLEW,"> $filename") or hard_error("An error occured in donstdlib::write_file():\n The file $filename could not be created. If the file exists, it can't be written and red.\nError occured");
		close(FHANDLEW);
	};
	$retvalue = open(FHANDLEW,"+< $filename");			#open for extended reading as writing would truncate it 
	
	
	#binmode?
	if($_[2] ne ""){
		binmode FHANDLEW;
	}
	
	if(!(defined($retvalue))){ 
		#As this is a "lowlevel" - function, no error() - function is available as error() uses read_file() 
		 hard_error("An error occured in donstdlib::write_file():\n The file $filename could not be opened for writing!\nError occured");
	};	
			
	#an exclusive lock is required for writing
	flock(FHANDLEW, LOCK_EX) or hard_error("An error occured in donstdlib::write_file():\n An exclusive lock on $filename could not be established.\nError occured");
	
	#rewind file
	seek(FHANDLEW,0,0) or hard_error("An error occured in donstdlib::write_file():\n File $filename could not be rewinded.\nError occured");
	
	#make file empty
	truncate(FHANDLEW,0) or hard_error hard_error("An error occured in donstdlib::write_file():\n File $filename could not be truncated.\nError occured");
	
	#write
	print FHANDLEW $_[1] or hard_error("An error occured in donstdlib::write_file():\n File $filename could not be written.\nError occured");
	
	#close and check for succes
	close(FHANDLEW) or hard_error("An error occured in donstdlib::write_file():\n File $filename could not be written.\nError occured");
		
};

########################################################################################################
#Standard error message - returns message in file $errorpath/$_[0].err
#The file $stderrfile will be openend and printed, the string [ERROR] will be replaced through the file
#$errorpath/$_[0].err
#In that file ($errorpath/$_[0].err), strings like [PARAM 1], [PARAM 2] will be replaced through $_[1],$_[2],...
sub get_error_message{
my $errbone;
	if($donparser::COF_mode ne "on"){	
			$errbone = "Content-Type: text/html\n\n";
	}		
	$errbone .= read_file($errorpath."/".$stderrfile);
	$errtxt = read_file($errorpath."/".$_[0].".err");
	my @errorparts;
	####################################################################################
		for(my $cnt = 1; $cnt < @_; $cnt++){
         $errorparts[$cnt] = doncgitools::txt_to_html($_[$cnt]);
      }
    	$errtxt =~ s/\[PARAM ([0-9])*\]/$errorparts[$1]/g;
	####################################################################################
	$errbone =~ s/\[ERROR\]/$errtxt/g;
	return $errbone;
}
########################################################################################################
#Standard error message - prints message in file $errorpath/$_[0].err and exits the programm
#The file $stderrfile will be openend and printed, the string [ERROR] will be replaced through the file
#$errorpath/$_[0].err
#In that file ($errorpath/$_[0].err), strings like [PARAM 1], [PARAM 2] will be replaced through $_[1],$_[2],...
sub error{
		#avoid endless recursions when an error in an error occurs
		if($error == 1){
			 hard_error("An error occured while displaying error $_[0]. This should not happen! Please send a bug-report!"); 
		}

	$error = 1;
	if($donparser::COF_mode ne "on"){	
		print get_error_message(@_);
		exit;
	}else{
	
		$donparser::error = get_error_message(@_);
			$error = 0;
		goto COF_NEXT;	#we immediately stop here!
		return $donparser::error;
	}
};
	
########################################################################################################
#this is the hard version of error messaging.
#it does not rely on any external file or (donlib) function.
#it is called when doing file operations to avoid endless loping errors.
#it displays the message $_[0] which must be an html-valid string, newlines are replaced through html-<br>s.
#this function is NOT very fancy - do not use when not beeing forced to 
sub hard_error{
	$_[1] =~ s/\n/\<br\>\n/g; 

$errheader = <<ERRTXT
Content-Type: text/html

ERRTXT
;
$errtxt = <<ERRTXT
<html>
<body>
<big>
<center>
<b>An error occured...</b><br>
<br>
<br>
<br>
<br>

<hr>
<big>
<i>$_[0]</i><br>
</big>
<hr>
<br>
<br>
<br>
</big>
The program has been terminated after displaying this message.<br>
However, things that have been done before the error occured should have taken effect.<br>
<br>
When doing system - critical things like rewriting the configuration,<br>
please check your installation. It might have taken damages.<br> 
</center>
</body>
</html>
ERRTXT
;
	if($donparser::COF_mode ne "on"){	
		print $errheader.$errtxt;
		exit;
	}else{
	
		$donparser::error = $errheader.$errtxt;

		$error = 0;
		goto COF_NEXT;	#we immediately stop here!
		return $donparser::error;
	}

		exit;

}

########################################################################################################
#makes sure that file $_[0] exits - if not a error message is displayed
#if $_[1] contains some string, there will be no error message, but a return code:
#1 = file exits
#0 = file does not exist
sub make_sure_that_exists{
	my $filename = shift();
	my $retvalue = open(FHANDLE2, $filename);
	close(FHANDLE2);
	#check for success; If not, print message and quit.
	
	if(!(defined($retvalue))){ 
			
			if(shift() ne ""){
				return 0;
			}else{	
				error("donstdlib_filedoesnotexist",$filename);
			};
	};
	
	return 1;

};

########################################################################################################
#this function checks the string $_[0] for appearance of the string $_[1].
#if found, the errormessage file $_[2] will be displayed (or donstdlib_invalidchars)
sub check_string{
	if($_[0] =~ /$_[1]/){
		if($_[2] eq ""){
			$_[2] = "donstdlib_invalidchars";
		};
		donstdlib::error($_[2],$_[1]);
	};
};

########################################################################################################
#deletes file $_[0]; error message in case of failure
#if $_[1] is not emty, there is no error message displayed
sub delete_file{
	check_filename($_[0],"allowed");
	my $retvalue = unlink("$_[0]");
		if(($retvalue == 0)&&($_[1] eq "")){ 
		error("donstdlib_couldnotdelete",$_[0]);
	};
	return $retvalue;
};
########################################################################################################
#deletes file pattern $_[0] by calling "rm $_[0]" in a shell
#if $_[1] is not emty, there is no error message displayed
#FILENAME IS NOT CHECKED FOR CORRECTNESS!
sub delete_file_by_shell{
	my $retvalue = system("rm $_[0]");
	if(($retvalue != 0)&&($_[1] eq "")){ 
		error("donstdlib_couldnotdelete",$_[0]);
	};

}

########################################################################################################
#returns a sorted array of files in directory $_[0].
#recognizes just files - no subdirectories
#if $_[1] is a non - empty string, the filenames are returned without the extension
#if $_[2] is non-emtpy, only directories instead of files are returned.
#if $_[3] is non-empty, .. and . are removed when in directory mode
sub glob_directory{	
	opendir(DIRH,"$_[0]");
 	my @files;
 	#check each item if it's  a file
 	while(my $file = readdir(DIRH)){
		if((($_[2] eq "")&&(-f "$_[0]/$file"))||(($_[2] ne "")&&(-d "$_[0]/$file"))){
 			if($_[1] ne ""){
 				(my @filenameparts) = split(/\./,$file);
 				pop(@filenameparts);		
 				$file = array_to_string(\@filenameparts,".");
 			}
			if(($_[2] ne "")&&($_[3] ne "")){
				if(($file ne ".")&&($file ne "..")){			
				 push @files, $file;			
				}			
			}else{
				 push @files, $file;			
 			}
 
  		}
 	}
	
	closedir(DIRH);
	my @rfiles = sort @files;				#sort list
	return @rfiles;
};


########################################################################################################
#converts the array reference in $_[0] to a string by joining the parts together.
#between the parts, the string $_[1] is inserted

sub array_to_string{
	my @array = @{$_[0]};
	my $string;
	for(my $cnt = 0; $cnt < @array; $cnt++){
		$string .= $array[$cnt]."$_[1]";		
	}
	
	#cut away the last $_[1] 
	$string = substr($string,0,(length($string)-length($_[1])));
	return $string;
};
  
########################################################################################################
#returns the message $_[0].
sub get_message{
		$msgparser = donparser->new($messagepath);
		$msgparser->open($_[0]);
		$msgparser->parse();
		return $msgparser->get_text();
};

########################################################################################################
#checks if filename §_[0] contains suspicious characters
#causes an errormessage when invalid
#if directories and dots shall be allowed, $_[1] must be a non-emty string different from the options below
#if only valid subdirectorie names (with a file within)shall be allowed, $_[1] must be "subdir"
sub check_filename{
	if($_[1] eq ""){
		if($_[0] =~ /[^ \w\_\ \-\[\]\(\)]/){
			error("donstdlib_invalid_filename",$_[0]);
		};
	}elsif($_[1] eq "subdir"){
		if($_[0] =~ /[^ \w\_\/\.\ \-\[\]\(\)]/){
			error("donstdlib_invalid_filename",$_[0]);
		}
		if($_[0] =~ /^\/	/){
			error("donstdlib_invalid_filename",$_[0]);
		}
		if($_[0] =~ /\.\./){
			error("donstdlib_invalid_filename",$_[0]);
		}
		if(substr($_[0],0,1) eq "/"){
			error("donstdlib_invalid_filename",$_[0]);
		}
	}else{
		if($_[0] =~ /[^ \w\_\-\.\ \/\~\[\]\(\)x]/){
			error("donstdlib_invalid_filename",$_[0]);
		}
	};
};

1;

