
use twtools;

package dbupdate;


######################################################################
# One time module initialization goes in here...
#
BEGIN 
{
	# This is the root directory we will be integrity checking
	#
	$root = "$twtools::twcwd/$twtools::twrootdir/dbupdate-test";

	# Here are the names of the report files this test will create
	#
	$report1 = "$twtools::twcwd/$twtools::twrootdir/report/dbupdate-1.twr";
	$report2 = "$twtools::twcwd/$twtools::twrootdir/report/dbupdate-2.twr";
	$report3 = "$twtools::twcwd/$twtools::twrootdir/report/dbupdate-3.twr";
	$report4 = "$twtools::twcwd/$twtools::twrootdir/report/dbupdate-4.twr";
}

######################################################################
# PolicyFileString -- return the policy text as a string
#
sub PolicyFileString
{
	return <<POLICY_END;	
	# Policy file generated by dbupdate test
	#
	$root -> \$(ReadOnly)+M; #read only plus MD5
	
POLICY_END

}

######################################################################
# CreateFile -- create a file with the specified contents
#   
# input:  path     -- path to the file; relative to $root
#         contents -- string to put in the file
#
sub CreateFile
{
	my ($path, $contents) = @_;
	
	system( "echo $contents > $root/$path" );

	$? && die "Create file failed for $root/$path\n";
}

######################################################################
# RemoveFile -- removes the named file
#   
sub RemoveFile
{
	my ($path) = @_;
	
	if( -e "$root/$path" )
	{
		system( "rm -f $root/$path" );
	}
	
	$? && die "Remove file failed for $root/$path\n";
}


######################################################################
# CreateDir -- create a directory
#
sub CreateDir
{
	my($dir) = @_;

	# NOTE: mkdir fails if it is already a directory!
	#
	if( ! -d "$root/$dir" )
	{
		system( "rm -f $root/$dir" );
		system( "mkdir -p $root/$dir" );
	
		$? && die "Mkdir failed for $root/$dir\n";
	}
}

######################################################################
# MoveFile -- move a file from one place to another
#             NOTE: file names are relative to $root
#   
# input:  old_name -- name of file to move
#         new_name -- where it should be moved to
#
sub MoveFile
{
	my($old, $new) = @_;
	
	system( "mv $root/$old $root/$new" );
	$? && die "mv $root/$old $root/$new failed!\n";
}

######################################################################
# PrintDatabase
#
sub PrintDatabase
{
	system( "$twtools::twrootdir/bin/twprint -m d -c $twtools::twrootdir/tw.cfg" );
}

######################################################################
# PrintReport
#
sub PrintReport
{
	my ($report) = @_;
	system( "$twtools::twrootdir/bin/twprint -m r -c $twtools::twrootdir/tw.cfg -r $report" );
}

######################################################################
# PrepareForTest -- creates the files that each test will be 
#                   integrity checking and initializes the database.
#
sub PrepareForTest
{
	# make sure we are cleaned up...
	#
	cleanup();

	# Make the files we will be using...
	#
	CreateDir ( "dog" );
	CreateFile( "dog/bark.txt", "bark bark" );
	CreateFile( "meow.txt",     "meow" );

	# Initialize the database
	#
	twtools::InitializeDatabase();
}

######################################################################
# RunBasicTest -- performs a rudimentary UpdateDatabase test
# 
sub RunBasicTest
{
    twtools::logStatus("*** Beginning dbupdate.basic test\n");
    printf("%-30s", "-- dbupdate.basic test");

    PrepareForTest();

    # make some violations...
    #
    MoveFile  ( "meow.txt", "cat.txt" );
    CreateFile( "dog/bark.txt", "bark bark bark" );

    # run the integrity check...
    #
    twtools::RunIntegrityCheck();

    # Make sure we got 4 violations: 2 mod, 1 add, 1 rm.
    #
    my $dir_mods = (($^O eq "skyos") || ($^O eq "dragonfly")) ? 0 : 1;
    my ($n, $a, $r, $c) = twtools::AnalyzeReport( twtools::RunReport() );
    my $n_expected = $dir_mods ? 4 : 3;
    my $c_expected = $dir_mods ? 2 : 1;

    if( ($n != $n_expected) || ($a != 1) || ($r != 1) || ($c != $c_expected) ) {

        twtools::logStatus("FAILED -- initial integrity check had unexpected results\n");
        return 0;
    }

    # do the database update...
    #
    if (0 != twtools::UpdateDatabase())
    {
        twtools::logStatus("FAILED -- db update did not succeed\n");
        return 0;
    }

    # do another IC and make sure there are no violations
    #
    twtools::RunIntegrityCheck();

    ($n, $a, $r, $c) = twtools::AnalyzeReport( twtools::RunReport() );

    if( $n != 0 )
    {
        twtools::logStatus("FAILED -- violations after update\n");
        return 0;
    }

    ++$twtools::twpassedtests;
    print "PASSED\n";
    return 1;
}

######################################################################
# RunSecureModeTest -- test that secure_mode high and low are working
#
sub RunSecureModeTest
{
    twtools::logStatus("*** Beginning dbupdate.secure_mode test\n");
    printf("%-30s", "-- dbupdate.secure_mode test");

    PrepareForTest();

    # make a violation and generate a report
    #
    CreateFile( "dog/bark.txt", "bark bark bark" );
    twtools::RunIntegrityCheck( { report => $report1 } );

    # change the same file in a slightly different way and generate
    # another report
    #
    CreateFile( "dog/bark.txt", "bark bark bark woof" );
    twtools::RunIntegrityCheck( { report => $report2 } );

    # Remove a file and generate a third report
    #
    RemoveFile( "dog/bark.txt" );
    twtools::RunIntegrityCheck( { report => $report3 } );

    # Add a file and generate the fourth report
    #
    CreateFile( "dog/cow.txt", "moo moo" );
    twtools::RunIntegrityCheck( { report => $report4 } );

    # Update the database with report 1.
    #
    twtools::UpdateDatabase( { report => $report1 } );

    # Try to update the database with report 1 again ... this should fail
    # in secure_mode == high because the db can't accept same changes again.
    #
    if( 0 == twtools::UpdateDatabase(
    { report => $report1, secure_mode => "high" } ) )
    {
        twtools::logStatus("FAILED ... secure_mode high didn't catch a bad update\n");
        return 0;
    }

    # Try to update the database with report 2 ... this should fail
    # in secure_mode == high because the "old" values don't match.
    #
    if( 0 == twtools::UpdateDatabase(
        { report => $report2, secure_mode => "high" } ) )
    {
        twtools::logStatus("FAILED ... secure_mode high didn't catch a bad update\n");
        return 0;
    }

    # do a high severity update with report3 -- this should 
    # succeed 
    #
    if(  0 != twtools::UpdateDatabase(
        { report => $report3, secure_mode => "high" } ) )
    {
        twtools::logStatus("FAILED ... Update with report 3 failed\n");
        return 0;
    }

    # Try 2 again ... now we are trying to update an object that
    # doesn't exist in the database at all. This should
    # succeed in low but fail in high.
    #
    if( 0 == twtools::UpdateDatabase(
        { report => $report2, secure_mode => "high" } ) )
    {
        twtools::logStatus("FAILED ... Update with report 2 after 3 succeeded in high mode\n");
        return 0;
    }

    if( 0 != twtools::UpdateDatabase(
        { report => $report2, secure_mode => "low" } ) )
    {
        twtools::logStatus("FAILED ... Update with report 2 after 3 failed in low mode\n");
        return 0;
    }

    ++$twtools::twpassedtests;
    print "PASSED\n";
    return 1;
}


######################################################################
#
# Initialize the test
#

sub initialize 
{
    # Make the policy file
    #
    twtools::GeneratePolicyFile( PolicyFileString() );
    return 1;
}


######################################################################
#
# Run the test.
#
sub run 
{
    eval {
	RunBasicTest();
    } or do {
        my $e = $@;
	twtools::logStatus("Exception in DBUpdate RunBasicTest: $e\n");
	++$twtools::twfailedtests;
	print "*FAILED*\n";
    };

    # bump the total test count since this file's a twofer
    ++$twtools::twtotaltests;

    eval {
	RunSecureModeTest();
    } or do {
        my $e = $@;
	twtools::logStatus("Exception in DBUpdate RunSecureModeTest: $e\n");
	++$twtools::twfailedtests;
	print "*FAILED*\n";
    };
}

sub cleanup
{
	# remove all of the files we were integrity checking...
	#
	system( "rm -rf $root/*" );
	$? && print "WARNING: dbupdate cleanup failed.\n";

	# remove the report files we created...
	#
	system( "rm -f $report1" ) if (-e $report1);
	system( "rm -r $report2" ) if (-e $report2);
	system( "rm -r $report3" ) if (-e $report3);
	system( "rm -r $report4" ) if (-e $report4);

}


######################################################################
# One time module cleanup goes in here...
#
END 
{
}

1;

