// Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of "Eclipse Public License v1.0"
// which accompanies this distribution, and is available
// at the URL "http://www.eclipse.org/legal/epl-v10.html".
//
// Initial Contributors:
// Nokia Corporation - initial contribution.
//
// Contributors:
//
// Description:
// 
//
#include <e32test.h>
#include <bautils.h>
#include <sqldb.h>
#include <stdlib.h>
#include "sqlite3.h"
#include "SqliteSymbian.h"

///////////////////////////////////////////////////////////////////////////////////////

//In order to be able to compile the test, the following variables are defined (used inside the OS porting layer, when _SQLPROFILER macro is defined)
#ifdef _SQLPROFILER
TInt TheSqlSrvProfilerFileRead = 0;
TInt TheSqlSrvProfilerFileWrite = 0;
TInt TheSqlSrvProfilerFileSync = 0;
TInt TheSqlSrvProfilerFileSetSize = 0;
#endif

RSqlDatabase TheDb;
RTest TheTest(_L("t_sqlcorrupt test"));

_LIT(KTestDir, "c:\\test\\");

_LIT(KDbName, "c:[08770000]t_sqlcorrupt.db");
_LIT(KFullDbName, "c:\\private\\10281E17\\[08770000]t_sqlcorrupt.db");

_LIT(KDbName2, "c:[08770000]t_sqlcorrupt2.db");
_LIT(KFullDbName2, "c:\\private\\10281E17\\[08770000]t_sqlcorrupt2.db");

RFs TheFs;

///////////////////////////////////////////////////////////////////////////////////////

void DestroyTestEnv()
	{
	TheDb.Close();
	(void)RSqlDatabase::Delete(KDbName2);
	(void)RSqlDatabase::Delete(KDbName);
	TheFs.Close();
	}

///////////////////////////////////////////////////////////////////////////////////////
//Test macros and functions
void Check1(TInt aValue, TInt aLine)
	{
	if(!aValue)
		{
		DestroyTestEnv();
		TheTest.Printf(_L("*** Expression evaluated to false. Line %d\r\n"), aLine);
		TheTest(EFalse, aLine);
		}
	}
void Check2(TInt aValue, TInt aExpected, TInt aLine)
	{
	if(aValue != aExpected)
		{
		DestroyTestEnv();
		TheTest.Printf(_L("*** Line %d, Expected error: %d, got: %d\r\n"), aLine, aExpected, aValue);
		TheTest(EFalse, aLine);
		}
	}
#define TEST(arg) ::Check1((arg), __LINE__)
#define TEST2(aValue, aExpected) ::Check2(aValue, aExpected, __LINE__)

///////////////////////////////////////////////////////////////////////////////////////

RSqlSecurityPolicy CreateTestSecurityPolicy()
	{
	RSqlSecurityPolicy policy;
	TInt err = policy.Create(TSecurityPolicy::EAlwaysPass);
	TEST2(err, KErrNone);

	err = policy.SetDbPolicy(RSqlSecurityPolicy::ESchemaPolicy, TSecurityPolicy::EAlwaysPass);
	TEST2(err, KErrNone);
	err = policy.SetDbPolicy(RSqlSecurityPolicy::EWritePolicy, TSecurityPolicy::EAlwaysPass);
	TEST2(err, KErrNone);
	err = policy.SetDbPolicy(RSqlSecurityPolicy::EReadPolicy, TSecurityPolicy::EAlwaysPass);
	TEST2(err, KErrNone);

	err = policy.SetPolicy(RSqlSecurityPolicy::ETable, _L("A"), RSqlSecurityPolicy::EWritePolicy, TSecurityPolicy::EAlwaysPass);
	TEST2(err, KErrNone);
	err = policy.SetPolicy(RSqlSecurityPolicy::ETable, _L("A"), RSqlSecurityPolicy::EReadPolicy, TSecurityPolicy::EAlwaysPass);
	TEST2(err, KErrNone);
	
	return policy;
	}

enum TDbEncoding
	{
	EDbEncUtf16,
	EDbEncUtf8,
	};

void DoCorruptedSecureDbTest(TDbEncoding aEncoding)
	{
	(void)RSqlDatabase::Delete(KDbName);
	
	RSqlSecurityPolicy policy = CreateTestSecurityPolicy();
	TInt err = KErrNone;
	if(aEncoding == EDbEncUtf16)
		{
		err = TheDb.Create(KDbName, policy);
		}
	else
		{
		_LIT8(KConfig, "encoding = \"UTF-8\"");
		err = TheDb.Create(KDbName, policy, &KConfig);
		}
	TEST2(err, KErrNone);
	err = TheDb.Exec(_L("CREATE TABLE A(I INTEGER); INSERT INTO A VALUES(10)"));
	TEST(err >= 0);
	TheDb.Close();
	policy.Close();

	CFileMan* fm = NULL;
	TRAP(err, fm = CFileMan::NewL(TheFs));
	TEST2(err, KErrNone);
	
	//Make a copy of the database
	err = fm->Copy(KFullDbName, KFullDbName2);
	TEST2(err, KErrNone);
	//Get the database file size and calculate the iterations count.
	TEntry entry;
	err = TheFs.Entry(KFullDbName, entry);
	TEST2(err, KErrNone);
	const TInt KCorruptBlockLen = 19;
	const TInt KIterationCnt = entry.iSize / KCorruptBlockLen;
	//
	TBuf8<KCorruptBlockLen> invalidData;
	invalidData.SetLength(KCorruptBlockLen);
	TChar invalidChar[] = {TChar((TUint)0xCC), TChar(0x00), TChar((TUint)-1), TChar(0x1D)};
	for(TInt j=0;j<(sizeof(invalidChar)/sizeof(invalidChar[0]));++j)
		{
		invalidData.Fill(invalidChar[j]);
		TheTest.Printf(_L("Invalid char: %X\r\n"), (TUint)(invalidChar[j]));
		for(TInt i=0;i<KIterationCnt;++i)
			{
			TheTest.Printf(_L("% 4d\r"), i + 1);
			//Corrupt the database
			err = fm->Copy(KFullDbName2, KFullDbName);
			TEST2(err, KErrNone);
			RFile file;
			err = file.Open(TheFs, KFullDbName, EFileRead | EFileWrite);
			TEST2(err, KErrNone);
			TInt fpos = i * KCorruptBlockLen;
			err = file.Write(fpos, invalidData);
			TEST2(err, KErrNone);
			file.Close();
			//Try to open the database and read the record
			TBool testPassed = EFalse;
			err = TheDb.Open(KDbName);
			if(err == KErrNone)
				{
				RSqlStatement stmt;
				err = stmt.Prepare(TheDb, _L("SELECT I FROM A"));
				if(err == KErrNone)
					{
					err = stmt.Next();
					if(err == KSqlAtRow)
						{
						TInt val = stmt.ColumnInt(0);
						if(val == 10)
							{
							testPassed = ETrue;
							err = KErrNone;
							}
						else
							{
							err = KErrGeneral;
							}
						}
					stmt.Close();
					}
				}

			TheDb.Close();
			(void)RSqlDatabase::Delete(KDbName);
			TheTest.Printf(_L("Iteration % 4d, err=%d\r\n"), i + 1, err);
			if(!testPassed)
				{
				TEST(err != KErrNone);
				}
			}//end of - for(TInt i=0;i<KIterationCnt;++i)
		TheTest.Printf(_L("\r\n"));
		}
	(void)fm->Delete(KFullDbName2);
	delete fm;
	}

/**
@SYMTestCaseID          PDS-SQL-CT-4202
@SYMTestCaseDesc        Invalid UTF16 encoded secure database test.
@SYMTestPriority        High
@SYMTestActions         The test creates 16-bit encoded secure database with one table and one record.
						Then the test simulates a database corruption by writing 19 bytes with random values
						from "pos" to "pos + 19", where "pos" is a valid db file position, incremented by 19
						at the end of each test iteration.
@SYMTestExpectedResults Test must not fail
*/  
void CorruptedSecureDbTest16()
	{
	DoCorruptedSecureDbTest(EDbEncUtf16);
	}

/**
@SYMTestCaseID          PDS-SQL-CT-4202
@SYMTestCaseDesc        Invalid UTF8 encoded secure database test.
@SYMTestPriority        High
@SYMTestActions         The test creates 8-bit encoded secure database with one table and one record.
						Then the test simulates a database corruption by writing 19 bytes with random values
						from "pos" to "pos + 19", where "pos" is a valid db file position, incremented by 19
						at the end of each test iteration.
@SYMTestExpectedResults Test must not fail
*/  
void CorruptedSecureDbTest8()
	{
	DoCorruptedSecureDbTest(EDbEncUtf8);
	}

const TInt KDbPolicyTypeTooSmall = 1;
const TInt KDbPolicyTypeTooBig = 2;
const TInt KDefaultPolicyWithObjName = 3;
const TInt KDbPolicyWithObjName = 4;
const TInt KInvObjPolicyWithObjName = 5;
const TInt KDefaultPolicyMoved = 6;
const TInt KDuplicatedDbPolicy = 7;
const TInt KTablePolicyTypeTooSmall = 8;
const TInt KTablePolicyTypeTooBig = 9;
const TInt KTablePolicyNameInvalid = 10;
const TInt KTwoDefaultPolicies = 11;

void CorruptDbPolicy(TInt aType)
	{
	TBuf8<200> dbName;
	dbName.Copy(KFullDbName());
	sqlite3* dbHandle = NULL;
	TInt err = sqlite3_open((const char*)(dbName.PtrZ()), &dbHandle);
	TEST2(err, SQLITE_OK);
	switch(aType)
		{
		case KDbPolicyTypeTooSmall: //Invalid database poicy type - less than the schema policy type
			err = sqlite3_exec(dbHandle, "UPDATE symbian_security SET PolicyType=-10 WHERE ObjectType=-1 AND PolicyType=0", NULL, 0, NULL);
			TEST2(err, SQLITE_OK);
			break;
		case KDbPolicyTypeTooBig: //Invalid database poicy type - bigger than the write policy type
			err = sqlite3_exec(dbHandle, "UPDATE symbian_security SET PolicyType=10 WHERE ObjectType=-1 AND PolicyType=0", NULL, 0, NULL);
			TEST2(err, SQLITE_OK);
			break;
		case KDefaultPolicyWithObjName: //Default database policy with valid object name 
			err = sqlite3_exec(dbHandle, "UPDATE symbian_security SET ObjectName='aaaa' WHERE ObjectType=-2", NULL, 0, NULL);
			TEST2(err, SQLITE_OK);
			break;
		case KDbPolicyWithObjName: //Database policy with valid object name 
			err = sqlite3_exec(dbHandle, "UPDATE symbian_security SET ObjectName='aaaa' WHERE ObjectType=-1", NULL, 0, NULL);
			TEST2(err, SQLITE_OK);
			break;
		case KInvObjPolicyWithObjName: //Invalid object type is with valid object name
			err = sqlite3_exec(dbHandle, "UPDATE symbian_security SET ObjectType=5, ObjectName='aaaa' WHERE ObjectType=0", NULL, 0, NULL);
			TEST2(err, SQLITE_OK);
			break;
		case KDefaultPolicyMoved: //The default security policy is the last in the table
			err = sqlite3_exec(dbHandle, "DELETE FROM symbian_security WHERE ObjectType=-2", NULL, 0, NULL);
			TEST2(err, SQLITE_OK);
			err = sqlite3_exec(dbHandle, "INSERT INTO symbian_security VALUES(20,-2,'',0, x'1122334455667788')", NULL, 0, NULL);
			TEST2(err, SQLITE_OK);
			break;
		case KDuplicatedDbPolicy: //Two database policies of the same type
			err = sqlite3_exec(dbHandle, "INSERT INTO symbian_security VALUES(9,-1,'',1, x'1122334455667788')", NULL, 0, NULL);
			TEST2(err, SQLITE_OK);
			break;
		case KTablePolicyTypeTooSmall: //Invalid table poicy type - less than the read policy type
			err = sqlite3_exec(dbHandle, "UPDATE symbian_security SET PolicyType=-10 WHERE ObjectType=0 AND PolicyType=1", NULL, 0, NULL);
			TEST2(err, SQLITE_OK);
			break;
		case KTablePolicyTypeTooBig: //Invalid table poicy type - less than the write policy type
			err = sqlite3_exec(dbHandle, "UPDATE symbian_security SET PolicyType=10 WHERE ObjectType=0 AND PolicyType=2", NULL, 0, NULL);
			TEST2(err, SQLITE_OK);
			break;
		case KTablePolicyNameInvalid:
			err = sqlite3_exec(dbHandle, "UPDATE symbian_security SET ObjectName='' WHERE ObjectType=0 AND PolicyType=2", NULL, 0, NULL);
			TEST2(err, SQLITE_OK);
			break;
		case KTwoDefaultPolicies:
			err = sqlite3_exec(dbHandle, "INSERT INTO symbian_security VALUES(9,-2,'',1, x'1122334455667788')", NULL, 0, NULL);
			TEST2(err, SQLITE_OK);
			break;
		default:
			TEST(0);
			break;
		}
	sqlite3_close(dbHandle);
	}

/**
@SYMTestCaseID          PDS-SQL-CT-4240
@SYMTestCaseDesc        Invalid settings in symbian_security table.
@SYMTestPriority        High
@SYMTestActions         The test creates a secure test database. Then the test runs a set of test 
						 iterations. On each itertaion the test makes onle of the stored security 
						 policies invalied (symbian_security table). Then the test attempts to open 
						 the database and checks the behaviour of the SQL server - whether an error
						 will be reproted back or not.
@SYMTestExpectedResults Test must not fail
*/  
void BadDbPolicyTest()
	{
	sqlite3SymbianLibInit();    
	
	RSqlSecurityPolicy policy = CreateTestSecurityPolicy();
	//
	(void)RSqlDatabase::Delete(KDbName);
	TInt err = TheDb.Create(KDbName, policy);
	TEST2(err, KErrNone);
	err = TheDb.Exec(_L("CREATE TABLE A(I INTEGER); INSERT INTO A VALUES(10)"));
	TEST(err >= 0);
	TheDb.Close();
	policy.Close();
	//
	CFileMan* fm = NULL;
	TRAP(err, fm = CFileMan::NewL(TheFs));
	TEST2(err, KErrNone);
	//Make a copy of the database
	err = fm->Copy(KFullDbName, KFullDbName2);
	TEST2(err, KErrNone);
	//Invalid database policy - policy type value less than the schema policy type value
	CorruptDbPolicy(KDbPolicyTypeTooSmall);
	err = TheDb.Open(KDbName);
	TheDb.Close();
	TEST2(err, KErrGeneral);
	err = fm->Copy(KFullDbName2, KFullDbName);
	TEST2(err, KErrNone);
	//Invalid database policy - policy type value bigger than the write policy type value
	CorruptDbPolicy(KDbPolicyTypeTooBig);
	err = TheDb.Open(KDbName);
	TheDb.Close();
	TEST2(err, KErrGeneral);
	err = fm->Copy(KFullDbName2, KFullDbName);
	TEST2(err, KErrNone);
	//Invalid database policy - the default database policy is with valid object name
	CorruptDbPolicy(KDefaultPolicyWithObjName);
	err = TheDb.Open(KDbName);
	TheDb.Close();
	TEST2(err, KErrNone);
	err = fm->Copy(KFullDbName2, KFullDbName);
	TEST2(err, KErrNone);
	//Invalid database policy - database policy is with valid object name
	CorruptDbPolicy(KDbPolicyWithObjName);
	err = TheDb.Open(KDbName);
	TheDb.Close();
	TEST2(err, KErrNone);
	err = fm->Copy(KFullDbName2, KFullDbName);
	TEST2(err, KErrNone);
	//Invalid database policy - invalid object type is with valid object name
	CorruptDbPolicy(KInvObjPolicyWithObjName);
	err = TheDb.Open(KDbName);
	TheDb.Close();
	TEST2(err, KErrGeneral);
	err = fm->Copy(KFullDbName2, KFullDbName);
	TEST2(err, KErrNone);
	//Invalid database policy - the default security policy is the last in the table
	CorruptDbPolicy(KDefaultPolicyMoved);
	err = TheDb.Open(KDbName);
	TheDb.Close();
	TEST2(err, KErrNone);
	err = fm->Copy(KFullDbName2, KFullDbName);
	TEST2(err, KErrNone);
	//Invalid database policy - duplicated database policy
	CorruptDbPolicy(KDuplicatedDbPolicy);
	err = TheDb.Open(KDbName);
	TheDb.Close();
	TEST2(err, KErrGeneral);
	err = fm->Copy(KFullDbName2, KFullDbName);
	TEST2(err, KErrNone);
	//Invalid table policy - policy type value less than the read policy type value
	CorruptDbPolicy(KTablePolicyTypeTooSmall);
	err = TheDb.Open(KDbName);
	TheDb.Close();
	TEST2(err, KErrGeneral);
	err = fm->Copy(KFullDbName2, KFullDbName);
	TEST2(err, KErrNone);
	//Invalid table policy - policy type value bigger than the write policy type value
	CorruptDbPolicy(KTablePolicyTypeTooBig);
	err = TheDb.Open(KDbName);
	TheDb.Close();
	TEST2(err, KErrGeneral);
	err = fm->Copy(KFullDbName2, KFullDbName);
	TEST2(err, KErrNone);
	//Invalid table policy - emtpy string as a table name 
	CorruptDbPolicy(KTablePolicyNameInvalid);
	err = TheDb.Open(KDbName);
	TheDb.Close();
	TEST2(err, KErrGeneral);
	err = fm->Copy(KFullDbName2, KFullDbName);
	TEST2(err, KErrNone);
	//Two default policies
	CorruptDbPolicy(KTwoDefaultPolicies);
	err = TheDb.Open(KDbName);
	TheDb.Close();
	TEST2(err, KErrGeneral);
	err = fm->Copy(KFullDbName2, KFullDbName);
	TEST2(err, KErrNone);
	//
	(void)RSqlDatabase::Delete(KDbName);
	(void)fm->Delete(KFullDbName2);
	delete fm;
	
	sqlite3SymbianLibFinalize();
	CloseSTDLIB();
	}


void CreateTestEnv()
    {
    TInt err = TheFs.Connect();
    TEST2(err, KErrNone);

    err = TheFs.MkDir(KTestDir);
    TEST(err == KErrNone || err == KErrAlreadyExists);

    err = TheFs.CreatePrivatePath(EDriveC);
    TEST(err == KErrNone || err == KErrAlreadyExists);
    }

void DoTestsL()
	{
	TheTest.Start(_L("@SYMTestCaseID:PDS-SQL-CT-4202 Corrupted UTF16 encoded secure database test"));
	CorruptedSecureDbTest16();
	
	TheTest.Next(_L("@SYMTestCaseID:PDS-SQL-CT-4203 Corrupted UTF8 encoded secure database test"));
	CorruptedSecureDbTest8();
	
	TheTest.Next(_L("@SYMTestCaseID:PDS-SQL-CT-4240 Secure database with bad policy test"));
	BadDbPolicyTest();
	}

TInt E32Main()
	{
	TheTest.Title();

	CTrapCleanup* tc = CTrapCleanup::New();
	TheTest(tc != NULL);
	
	__UHEAP_MARK;
		
	CreateTestEnv();
	TRAPD(err, DoTestsL());
	DestroyTestEnv();
	TEST2(err, KErrNone);

	__UHEAP_MARKEND;
	
	TheTest.End();
	TheTest.Close();
	
	delete tc;

	User::Heap().Check();
	return KErrNone;
	}
