#
# $Id: update-db-backend.pl,v 1.32 2002/03/18 11:00:44 fpmip Exp $
#

# Author : Tycho Fruru <tycho@fruru.com>

# Copyright (C) 2001 Tycho Fruru <tycho@fruru.com>
# Copyright (C) 2001 Conostix S.A. Luxembourg - Tycho Fruru <tycho@conostix.com>

# 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 2 of the License, or (at your
# option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
#
# 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.
#

use XML::Simple;
use Data::Dumper;	# for debugging those XML data structures
#use strict;

require "../../lib/ipfcsign-xmlconfig.pm";
require "../../lib/ipfcsign.pm";

use IPFC::Backend::DB::Interface;
use IPFC::Event;
use IPFC::EventGroup;
use IPFC::Logfile;
use IPFC::LogUnit;

my $table_names;

sub update_db_backend() {
	IPFC::Log::logger(5,"Beginning of update_db_backend");
	
	my ($dbh,$sth_agents,$agent_id,$agent_type_name);
	
	IPFC::Log::logger(5,"Getting database schema version");

	$dbh = DBI->connect($DB_CONNECT_STRING,$DB_USERNAME,$DB_PASSWORD) or
				die "Database connection failed";
	$dbh->{AutoCommit} = 0;
	
	my $backend_interface = IPFC::Backend::DB::Interface->new($dbh); # object for encapsulating backend accesses
	
	$sth_agents = $dbh->prepare("select version from ipfc_schema where component = 'db-schema' 
					    order by date desc limit 1") or
			die "select version from ipfc_schema failed";
	$sth_agents->execute or die "select version from ipfc_schema execution failed";
	($version) = $sth_agents->fetchrow_array;
	$sth_agents->finish;
	
	if (!defined($version) || ($version lt "20020207-01")) {
		die "Database version incompatible (you have version $version) !";
	}

	IPFC::Log::logger(4,"Getting all attribute types and rules");
	
#	my $result = IPFC::DB::Attributes->init($dbh);
	
#	if (!$result) {
#		IPFC::Log::logger(1,"Cannot get attributes and rules, aborting !");
#		die;
#	}
	
	IPFC::Log::logger(4,"Getting all table names and function names");
	$sth_agents = $dbh->prepare(
			"select agent_type_name,entry_type,entry_subtype,entry_version,entry_transport,table_id,transport_function_name,function_name from table_names");
	$sth_agents->execute;
	while (my ($agent_type_name,$entry_type,$entry_subtype,$entry_version,$entry_transport,$table_id,$transport_function_name,$function_name) =
		   $sth_agents->fetchrow_array) {
	    $table_names->{$agent_type_name}->{$entry_type}->{$entry_subtype}->{$entry_version}->{$entry_transport}->{"table_id"} = $table_id;
	    $table_names->{$agent_type_name}->{$entry_type}->{$entry_subtype}->{$entry_version}->{$entry_transport}->{"transport_function_name"} = $transport_function_name;
	    $table_names->{$agent_type_name}->{$entry_type}->{$entry_subtype}->{$entry_version}->{$entry_transport}->{"function_name"} = $function_name;
	}	    
	$sth_agents->finish;
	
#	print Dumper($table_names);
		
	IPFC::Log::logger(4,"Getting all agents which this db-backend manages");	 
	$sth_agents = $dbh->prepare(
			"select agent_id,agent_type_name from agents") or
				die "select agent_id failed";
	$sth_agents->execute or die "select agent_id execution failed";

	while (($agent_id,$agent_type_name) = $sth_agents->fetchrow_array) {
		IPFC::Log::logger(4,"Starting on agent $agent_id, ".
			   "type $agent_type_name\n");

		process_messages($agent_id,$agent_type_name,$backend_interface);

	}
	
	$sth_agents->finish;
	$dbh->disconnect;
}


sub process_messages($$$) {
	my $agent_id = shift;
	my $agent_type_name = shift;
	my $backend_interface = shift;
	if (!$backend_interface->isa("IPFC::Backend::Interface")) {
		IPFC::Log::logger(1,"Process_messages was called without IPFC::Backend::Interface object !");
		die;
	}
	IPFC::Log::logger(4,"Beginning of process_messages (agent $agent_id)\n");
	my (@ok_files,$current_file);
	my $current_directory;
	
	# reread keys file for XML signatures
	IPFC::Sign::set_config(IPFC::Sign::Xmlconfig::read_config($XML_KEYS_FILE));	
	
	foreach $current_directory ('status','events') {
		IPFC::Log::logger(6,"Getting all files in the $current_directory dir\n");
		@ok_files = get_all_ok_files("$XML_DOWNLOAD_DIRECTORY/$agent_id/$current_directory");
		while ($current_file = shift(@ok_files)) {
			IPFC::Log::logger(3,"Treating file $agent_id/$current_directory/$current_file\n");
			my $rc = process_one_file($agent_id,$agent_type_name,$backend_interface,
						  "$XML_DOWNLOAD_DIRECTORY/$agent_id/$current_directory/$current_file");
			if ($rc) { 
				IPFC::Log::logger(5,"OK, moving the file to the archive location\n");
				if (-e "$XML_ARCHIVE_DIRECTORY/$agent_id/$current_directory/$current_file" ) {
					IPFC::Log::logger(1,"Archive file $XML_ARCHIVE_DIRECTORY/$agent_id/$current_directory/$current_file already exists...");
				}
				rename "$XML_DOWNLOAD_DIRECTORY/$agent_id/$current_directory/$current_file",
			       		"$XML_ARCHIVE_DIRECTORY/$agent_id/$current_directory/$current_file" or
						die "Cannot move treated file to $XML_ARCHIVE_DIRECTORY/$agent_id/$current_directory";
				unlink "$XML_DOWNLOAD_DIRECTORY/$agent_id/$current_directory/${current_file}.ok";
			} else {
				IPFC::Log::logger(1,"Problem with file $XML_DOWNLOAD_DIRECTORY/$agent_id/$current_directory/$current_file\n");
				if (-e "$XML_PROBLEM_DIRECTORY/$agent_id/$current_directory/$current_file" ) {
					IPFC::Log::logger(1,"Problem file $XML_ARCHIVE_DIRECTORY/$agent_id/$current_directory/$current_file already exists...");
				}
				rename "$XML_DOWNLOAD_DIRECTORY/$agent_id/$current_directory/$current_file",
					"$XML_PROBLEM_DIRECTORY/$agent_id/$current_directory/$current_file" or 
						die "Cannot move problematic file to $XML_PROBLEM_DIRECTORY/$agent_id/$current_directory";
				unlink "$XML_DOWNLOAD_DIRECTORY/$agent_id/$current_directory/${current_file}.ok";
			}
			IPFC::Log::logger(5,"Finished file $agent_id/$current_directory/$current_file\n");
		}
	}	
}

sub get_all_ok_files($) {
# returns a list with all files which have a corresponding OK file
	IPFC::Log::logger(7,"Beginning get_all_ok_files ");
	my $directory = shift;
	IPFC::Log::logger(7,"in directory $directory\n");
	opendir DIRHANDLE,$directory or
		die "Cannot open directory $directory";
	
	my $current_file;
	my @ok_files=();
	
	while ($current_file = readdir DIRHANDLE) {
		IPFC::Log::logger(10,"Current file : $current_file\n");
		if (($current_file eq ".") ||
		    ($current_file eq "..")) {
		    	next;
		}
		IPFC::Log::logger(10,"Treating file $current_file\n");
		if ($current_file =~ /^([0-9a-zA-Z_\-\.]*)\.ok$/) {
			if (-f "$directory/$1") {
				# The corresponding status file exists
				IPFC::Log::logger(7,"File $current_file is an OK file for file $1\n");
				push @ok_files,$1;
			} else { 
				IPFC::Log::logger(2,"File $current_file is an OK file without real file $1\n");
			}
		}	
	}
	closedir DIRHANDLE;
	return @ok_files;
} 

sub process_one_file($$$$) {
	my $agent_id = shift;
	my $agent_type_name = shift;
	my $backend_interface = shift;
	my $filename = shift;
	
	if (!$backend_interface->isa("IPFC::Backend::Interface")) {
		IPFC::Log::logger(1,"Process_one_file was called without IPFC::Backend::Interface object !");
		die;
	}
	
	if (not -f "$filename") {
		IPFC::Log::logger(1,"process_one_file: The file $filename does not exist!\n");
		return 0;
	}

	if (!open(RAW_FILE,"<",$filename)) {
		IPFC::Log::logger(1,"Cannot open file $filename for reading");
		return 0;
	}
	
	# first read the complete file in $raw_file_contents
	my $raw_file_contents="";
	my $current_amount_read;
	my $current_read;
	
	$current_amount_read = read(RAW_FILE,$current_read,100000);
	while(defined($current_amount_read) && $current_amount_read > 0) {
		$raw_file_contents=$raw_file_contents.$current_read;
		$current_amount_read = read(RAW_FILE,$current_read,100000);
	}
	
	undef $current_amount_read;
	
	my $file;
	my $xml_message = IPFC::Sign::check_signature($raw_file_contents);
	
	undef $raw_file_contents;
	
	if (!defined($xml_message)) {
		IPFC::Log::logger(1,"Problem checking signature for $filename");
		#sleep 1;
		return 0;
	}
	
	eval { 
		$file = XMLin($xml_message,forcearray=>1,keyattr=>[],
			      contentkey=>'content');
	};
	
#print Dumper($file);

	undef $xml_message;
	
	if ($@) { 
		IPFC::Log::logger(1,"Problem parsing the XML content for file $filename");
		return 0;
	}
	
	my $xml_agent_id = check_numeric($file->{agent}->[0]->{id});
	if (!defined($xml_agent_id)) {
		IPFC::Log::logger(1,"Missing agent_id in $filename");
		return 0;
	}
	if ($xml_agent_id != $agent_id) {
		IPFC::Log::logger(1,"WRONG agent_id in $filename, XML agent id = $xml_agent_id, should be $agent_id");
		return 0;
	}

	my $agent_time = check_string($file->{agent}->[0]->{date});
	if (!defined($agent_time)) {
		IPFC::Log::logger(1,"Strange module_date in $filename\n");
		return 0;
	};
	my $wrapper_id = $file->{agent}->[0]->{wrapperid};
	if (!defined($wrapper_id)) {
		$wrapper_id = 0;
	}
	$wrapper_id = check_numeric($wrapper_id);
	if (!defined($wrapper_id)) {
		IPFC::Log::logger(1,"Strange wrapper_id in $filename\n");
		return 0;
	}
	my $generation_id = check_string($file->{agent}->[0]->{generationid});
	if (!defined($generation_id)) {
		IPFC::Log::logger(1,"Strange generation_id in $filename\n");
		return 0;
	};
	my $sequence_id = check_numeric($file->{agent}->[0]->{sequenceid});
	if (!defined($sequence_id)) {
		IPFC::Log::logger(1,"Strange sequence_id in $filename\n");
		return 0;
	};
	my $transac_id = check_numeric($file->{agent}->[0]->{transacid});
	if (!defined($transac_id)) {
		IPFC::Log::logger(1,"Strange transac_id in $filename\n");
		return 0;
	};
	
	my $logfile = IPFC::Logfile->new;
	$logfile->set_agent_id($agent_id);
	$logfile->set_wrapper_id($wrapper_id);
	$logfile->set_generation_id($generation_id);
	$logfile->set_sequence_id($sequence_id);
	$logfile->set_transac_id($transac_id);
	$logfile->set_agent_time($agent_time);
	
	my $eventgroup = IPFC::EventGroup->new;
	
	my $rc = 1;
# Now we must process all data entries
	
	if (ref($file->{data}) eq "ARRAY") {
		# there are multiple entries
		IPFC::Log::logger(8,"The reference is to an ARRAY containing perhaps multiple data fields\n");
		
		my $index = 0;
		while (defined($file->{data}->[$index] && $rc==1)) {
			my $current_eventgroup = process_one_entry($agent_id,$agent_type_name,$logfile_id,$file->{data}->[$index]);
			$eventgroup->add_eventgroup($current_eventgroup) or $rc = 0;
			$index++;
		};		
	} else {
		IPFC::Log::logger(1,"There are no <data> in this file, committing just the existence of the file...");
	};

	my $logunit = IPFC::LogUnit->new;
	$logunit->set_logfile($logfile);
	$logunit->set_eventgroup($eventgroup);
	
	if ($rc == 1) {
		$rc = $backend_interface->put_logunit($logunit);
	}
	
	if ($rc == 1) {
		IPFC::Log::logger(5,"Performing pre-commit...\n");
		$rc = $backend_interface->pre_commit;
	}
	if ($rc == 1 && $DO_COMMIT == 1) {
		IPFC::Log::logger(5,"Performing commit...\n");
		$backend_interface->commit or die "Cannot commit !";			
	} else {
		IPFC::Log::logger(5,"Performing rollback...\n");
		$backend_interface->rollback or die "Cannot rollback !";
	}
	
	return $rc;

};


sub process_one_entry($$$$) {
# agent_id
# agent_type_name
# logfile_id
# ->{data}->[index]
	
	my $agent_id = shift;
	my $agent_type_name = shift;
	my $logfile_id = shift;
	my $entry = shift;
	
	my $entry_type = check_string($entry->{type});
	my $entry_subtype = check_string($entry->{subtype});
	my $entry_version = check_numeric($entry->{version});
	my $entry_transport = check_string($entry->{transport});
	
	if (!defined($entry_transport)) {
		$entry_transport = 'null';
	}
	
	IPFC::Log::logger(6,"type $entry_type, category $entry_subtype, version $entry_version, transport $entry_transport\n");
	IPFC::Log::logger(6,"trying to find the right tablename : ");
		
	my $table_id = 
	    $table_names->{$agent_type_name}->{$entry_type}->{$entry_subtype}->{$entry_version}->{$entry_transport}->{"table_id"};
	my $transport_function_name = 
	    $table_names->{$agent_type_name}->{$entry_type}->{$entry_subtype}->{$entry_version}->{$entry_transport}->{"transport_function_name"};
	my $function_name = 
	    $table_names->{$agent_type_name}->{$entry_type}->{$entry_subtype}->{$entry_version}->{$entry_transport}->{"function_name"};
	
	IPFC::Log::logger(6,"Table id		: $table_id");
	IPFC::Log::logger(6,"Transport function : $transport_function_name");
	IPFC::Log::logger(6,"Proc function name : $function_name");
	
# prepare the SQL fields and values we know already

	my $event = IPFC::Event->new;
	$event->add_attributes({ "agent_id"   => $agent_id,
				 "table_id"   => $table_id,
				 "logfile_id" => $logfile_id });
	
# process all entries using the right subroutine
	
	my $eventgroup = $transport_function_name->parse($entry,$function_name,$event);
#	print Dumper($eventgroup);
	
	if ($eventgroup == 0) {
		IPFC::Log::logger(1,"processing of $transport_function_name , $function_name did not succeed !");
	}
	
	return $eventgroup;
}

return 1;
