smartinstaller/adm/src/ADMXmlParser.cpp
author Santosh V Patil <santosh.v.patil@nokia.com>
Wed, 30 Jun 2010 11:01:26 +0530
branchADM
changeset 48 364021cecc90
permissions -rw-r--r--
SmartInstaller contribution based on the Nokia Qt SDK 1.0 release

/*
* Copyright (c) 2009-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: 
*     CXmlParser implementation
*
*
*/


#include <SenDomFragment.h>
#include <SenXmlReader.h>
#include <SenElement.h>
#include "ADMXmlParser.h"
#include "ADMPackageInfo.h"
#include "macros.h"
#include "config.h"

_LIT(KSisExtension,".sis");

CXmlParser* CXmlParser::NewLC()
	{
	CXmlParser* object = new ( ELeave ) CXmlParser();
	CleanupStack::PushL( object );
	object->ConstructL();
	return object;
	}

CXmlParser* CXmlParser::NewL()
	{
	CXmlParser* object = CXmlParser::NewLC();
	CleanupStack::Pop();
	return object;
	}

CXmlParser::CXmlParser()
	:iXmlReader(NULL),
	iDomFragment(NULL)
	{
	}

CXmlParser::~CXmlParser()
	{
	iRfs.Close();

	if ( iXmlReader )
		{
		delete iXmlReader;
		iXmlReader = NULL;
		}
	if ( iDomFragment )
		{
		delete iDomFragment;
		iDomFragment = NULL;
		}
	}

void CXmlParser::ConstructL()
	{
	User::LeaveIfError(iRfs.Connect());
	}

void CXmlParser::InitL()
	{
	// Do the cleanup as this function could be
	// called multiple times.
	if ( iXmlReader )
	   {
	   delete iXmlReader;
	   iXmlReader = NULL;
	   }

	if ( iDomFragment )
	   {
	   delete iDomFragment;
	   iDomFragment = NULL;
	   }

	iXmlReader = CSenXmlReader::NewL(EErrorOnUnrecognisedTags);
	iDomFragment = CSenDomFragment::NewL();

	iXmlReader->SetContentHandler(*iDomFragment);
	iDomFragment->SetReader(*iXmlReader);
	}

void CXmlParser::GetChangesInfoL(const TDesC& aChangesFileName, CPackageInfo *packageInfo,CDepTree* mainTree)
	{
	InitL();

	iXmlReader->ParseL(iRfs, aChangesFileName);

	// Get package uid
	// Leave if package uid not present
	CSenElement* packageElement;
	packageElement = iDomFragment->AsElement().Element(KFieldDepPackageUid);
	if (packageElement == NULL)
		{
		User::Leave(EMissingPackageUid);
		}
	TPtrC8 packageUidPtr = packageElement->Content();
	TUint32 packageUid=0;
	if (StringToInt(packageUidPtr,packageUid) != KErrNone)
		{
		User::Leave(EInvalidPackageUid);
		}

	//Check whether the packageId received in changes file is same as the one already stored in the packageinfo from dependency info.
	//If there is mismatch, exit. If it matches, proceed further.
	if(packageUid != packageInfo->iPackageUid)
		{
		User::Leave(EIncorrectPackageUid);
		}

	packageElement = iDomFragment->AsElement().Element(KFieldDepPackageName);
	if (packageElement)
		{
		DELETE_IF_NONNULL( packageInfo->iPackageName );
		packageInfo->iPackageName = packageElement->ContentUnicodeL();
		}
	else
		{
		User::Leave(EMissingPackageName);
		}

	// Get dependent package vendor
	packageElement = iDomFragment->AsElement().Element(KFieldDepVendor);
	if (packageElement)
		{
		DELETE_IF_NONNULL( packageInfo->iVendor );
		packageInfo->iVendor = packageElement->ContentUnicodeL();
		}
	else
		{
		User::Leave(EMissingVendorName);
		}

	// Get Priority for the drive
	//This is optional.Hence no Leave for Missing Priority.
	//Default is 0.
	packageElement = iDomFragment->AsElement().Element(KFieldDrivePriority);
	if(packageElement)
		{
		TPtrC8 drivePtr = packageElement->Content();
		if (StringToInt(drivePtr,packageInfo->iDrivePriority) != KErrNone)
			{
			User::Leave(EInvalidDrivePriority);
			}
		}

	// Get the mandatory drive.
	//This is optional.Hence no Leave for Missing info.
	//Default is none.
	packageElement = iDomFragment->AsElement().Element(KFieldMandatoryInstallDrive);
	if(packageElement)
		{
		packageInfo->iMandatoryInstallDrive = packageElement->ContentUnicodeL()->Des()[0];
		}

	GetVersionInfoL(packageInfo,mainTree->GetMachineId());
	}

void CXmlParser::GetVersionInfoL(CPackageInfo *packageInfo,TUint32 machineId)
	{
	RPointerArray<CSenElement> versionInfo;
	CleanupClosePushL(versionInfo);

	iDomFragment->AsElement().ElementsL(versionInfo,KFieldVersionInformation);
	const TInt versionInfoCount(versionInfo.Count());
	TVersion latestVersion(0,0,0);
	TInt latestVersionCount = -1;
	CSenElement* packageElement;

	//Changes file will contain different versions of the package along
	//with the list of supporrted devices for that perticular version.

	//If no version was specified in the dep file, then the latest should be read.
	//While reading the latest, it should be confirmed whether the same supports the given device.

	for (TInt i=0; i<versionInfoCount; ++i)
		{
		packageElement = versionInfo[i]->Element(KFieldDepPackageVersion);
		if (packageElement == NULL)
			{
			User::Leave(EMissingDepVersion);
			}

		TPtrC8 versionPtr = packageElement->Content();
		TVersion ver;
		if (SetVersion(versionPtr, ver) == EFalse)
			{
			User::Leave(EInvalidVersion);
			}

		//Check whether the MachineID of this device is supported for this corresponding version in the changes file.
		if(checkMachineIdL(versionInfo[i],machineId))
			{
			if((CompareVersions(packageInfo->iVersion,ver) == EEqualVersion)||(CompareVersions(packageInfo->iVersion,ver) == EGreaterSecondVersion))
				{
				//Version specified in the dep file of parent matches the version in the changes file which inturn matches to the MachineID. :)
				//Or there exists a greater version in changes file.
				latestVersion = ver;
				latestVersionCount = i;
				}
			}
		}//end of for loop

	if(latestVersionCount == -1)
		{
		//No matching Machine IDs at all for any version specified in the changes file.
		User::Leave(EUnsupportedDevice);
		}
	else
		{
		packageInfo->iVersion = latestVersion;
		}

	// Get dependent package URL
	packageElement= versionInfo[latestVersionCount]->Element(KFieldDepUrl);
	if (packageElement)
		{
		DELETE_IF_NONNULL( packageInfo->iUrl );
		TPtrC8 urlPtr = packageElement->Content();

		HBufC8* url = urlPtr.AllocL();

		packageInfo->iUrl = url;
		}
	else
		{
		User::Leave(EMissingPackageUrl);
		}


	packageElement = versionInfo[latestVersionCount]->Element(KFieldDepFileName);
	if (packageElement)
		{
		DELETE_IF_NONNULL( packageInfo->iDepFileName );
		packageInfo->iDepFileName = packageElement->ContentUnicodeL();
		}
	else
		{
		User::Leave(EMissingDepFileName);
		}

	packageElement = versionInfo[latestVersionCount]->Element(KFieldInstallPackageName);
	if (packageElement)
		{
		DELETE_IF_NONNULL( packageInfo->iSisPackageName );
		packageInfo->iSisPackageName = packageElement->ContentUnicodeL();
		}
	else
		{
		User::Leave(EMissingSisFileName);
		}

	packageElement = versionInfo[latestVersionCount]->Element(KFieldDateOfSubmission);
	if (packageElement)
		{
		DELETE_IF_NONNULL( packageInfo->iDateOfSubmission );
		packageInfo->iDateOfSubmission = packageElement->ContentUnicodeL();
		}
	else
		{
		User::Leave(EMissingSubmissionDate);
		}
	//TODO: There needs to be a separate function to convert the Date format into a string format.
	//Call that function here.
	packageElement = versionInfo[latestVersionCount]->Element(KFieldDateOfModification);
	if (packageElement)
		{
		DELETE_IF_NONNULL( packageInfo->iDateOfModification );
		packageInfo->iDateOfModification = packageElement->ContentUnicodeL();
		}
	else
		{
		User::Leave(EMissingModificationDate);
		}

	packageElement = versionInfo[latestVersionCount]->Element(KFieldDownloadSize);
	TPtrC8 downloadSizePtr = packageElement->Content();
	TUint32 downloadSize=0;
	if (StringToInt(downloadSizePtr,downloadSize) != KErrNone)
		{
		User::Leave(EInvalidPackageUid);
		}
	if(downloadSize)
		{
		packageInfo->iDownloadSize = downloadSize;
		}
	else
		{
		User::Leave(EMissingDownloadSize);
		}

	packageElement = versionInfo[latestVersionCount]->Element(KFieldInstalledSize);
	TPtrC8 installedSizePtr = packageElement->Content();
	TUint32 installedSize=0;
	if (StringToInt(installedSizePtr,installedSize) != KErrNone)
		{
		User::Leave(EInvalidPackageUid);
		}
	if(installedSize)
		{
		packageInfo->iInstalledSize = installedSize;
		}
	else
		{
		User::Leave(EMissingInstalledSize);
		}
	CleanupStack::PopAndDestroy(&versionInfo);
}

TBool CXmlParser::checkMachineIdL(CSenElement *versionInfo, TUint32 machineId)
	{
	RPointerArray<CSenElement> deviceInfo;
	CleanupClosePushL(deviceInfo);

	versionInfo->ElementsL(deviceInfo,KFieldSupportedDevice);
	const TInt deviceInfoCount(deviceInfo.Count());

	CSenElement* packageElement;

	// See if the device is supported by this
	// particular version, by comparing the list
	// of machine ids supported by the version
	// with the machine id of device. .
	for(TInt i=0;i<deviceInfoCount;i++)
		{
		packageElement = deviceInfo[i]->Element(KFieldDeviceMachineId);

		if (packageElement == NULL)
			{
			User::Leave(EMissingMachineID);
			}
		TPtrC8 packageUidPtr = packageElement->Content();
		TUint32 changesMachineId = 0;
		if (StringToInt(packageUidPtr,changesMachineId) != KErrNone)
			{
			User::Leave(EInvalidMachineID);
			}
		if(changesMachineId == machineId)
			{
			CleanupStack::PopAndDestroy(&deviceInfo);
			return ETrue;
			}
		}
	CleanupStack::PopAndDestroy(&deviceInfo);
	return EFalse;
	}

CPackageInfo* CXmlParser::GetDepContentsL(const TDesC& aDepFileName, CPackageInfo *packageInfo, CDepTree* aMainTree)
	{
	InitL();

	iXmlReader->ParseL(iRfs, aDepFileName);

	// Get package uid
	// Leave if package uid not present
	CSenElement* packageElement;
	packageElement = iDomFragment->AsElement().Element(KFieldPackageUid);
	if (packageElement == NULL)
		{
		User::Leave(EMissingPackageUid);
		}
	TPtrC8 packageUidPtr = packageElement->Content();
	TUint32 packageUid=0;
	if (StringToInt(packageUidPtr,packageUid) != KErrNone)
		{
		User::Leave(EInvalidPackageUid);
		}

	User::LeaveIfNull(aMainTree);
	if(aMainTree->IsDepTreeEmpty())
		{
		// Add root package(first package) to the list
		packageInfo = CPackageInfo::NewL();
		packageInfo->iPackageUid = packageUid;
		packageInfo->iPackageStatus = EPackageRootToBeInstalled;
		aMainTree->AddPackageInfo(packageInfo);
		}
	else
		{
		User::LeaveIfNull(packageInfo);
		if(packageInfo->iPackageUid != packageUid)
			{
			//the package pointer doesn't match the depfile
			User::Leave(EInvalidPackageUid);
			}
		}

	// Get Dep version
	// Leave if Dep version is not present
	packageElement = iDomFragment->AsElement().Element(KFieldDepVersion);
	if (packageElement == NULL)
		{
		User::Leave(EMissingDepVersion);
		}
	else
		{
		TPtrC8 versionPtr = packageElement->Content();
		TVersion ver;
		if (SetVersion(versionPtr, ver) == EFalse)
			{
			User::Leave(EInvalidVersion);
			}
		packageInfo->iDepVersion = ver;
		}

	// Parse the dependencies
	SetDependenciesL(packageInfo, aMainTree);

	return packageInfo;
	}

void CXmlParser::SetDependenciesL(CPackageInfo* aPackageInfo, CDepTree* aMainTree)
	{
	// Get dependencies
	RPointerArray<CSenElement> dependency;
	CleanupClosePushL(dependency);

	iDomFragment->AsElement().ElementsL(dependency,KFieldDependency);
	const TInt DependencyCount(dependency.Count());

	User::LeaveIfNull(aMainTree);
	User::LeaveIfNull(aPackageInfo);


	TBool IsNonRootNodeDep = 1;

	// Check if the dependencies are of the root node.
	// If yes, then clear the flag
	if (aMainTree->GetRootNode() == aPackageInfo)
		{
		IsNonRootNodeDep = 0;
		}

	for (TInt i=0; i<DependencyCount; ++i)
		{
		// Get dependency UID
		CSenElement* packageElement;
		TUint32 depPackageUid = 0;
		packageElement = dependency[i]->Element(KFieldDepPackageUid);
		if (packageElement == NULL)
			{
			User::Leave(EMissingPackageUid);
			}
		TPtrC8 depPackageUidPtr = packageElement->Content();
		if ( StringToInt(depPackageUidPtr,depPackageUid) != KErrNone )
			{
			User::Leave(EInvalidPackageUid);
			}

		// Check if the dependency already present in the tree.
		CPackageInfo* depPackageInfo = aMainTree->LocatePackageInDepTree(depPackageUid);

		// If dependency is listed in the tree,
		// create a new edge for it
		if (depPackageInfo != NULL)
			{
			aPackageInfo->AddEdgeL(depPackageInfo);
			continue;
			}

		// Dependency is not already present, so create a new node
		depPackageInfo = CPackageInfo::NewL();
		aMainTree->AddPackageInfo(depPackageInfo);

		depPackageInfo->iPackageUid = depPackageUid;
		aPackageInfo->AddEdgeL(depPackageInfo);

		// Get dependent package name
		packageElement = dependency[i]->Element(KFieldDepPackageName);
		if (packageElement)
			{
			depPackageInfo->iPackageName = packageElement->ContentUnicodeL();
			}
		else if(IsNonRootNodeDep)
			{
			User::Leave(EMissingPackageName);
			}
		else
			{
			depPackageInfo->iPackageName = HBufC::NewL(
												depPackageUidPtr.Length() +
												KSisExtension().Length());

			TPtr depPackageName(depPackageInfo->iPackageName->Des());
			depPackageName.Copy(depPackageUidPtr);
			depPackageName.Append(KSisExtension);
			}


		// Get dependent package version
		packageElement = dependency[i]->Element(KFieldDepPackageVersion);

		if(packageElement)
			{
			TPtrC8 versionPtr = packageElement->Content();
			TVersion ver;
			if (SetVersion(versionPtr, ver) == EFalse)
				{
				User::Leave(EInvalidVersion);
				}
			depPackageInfo->iVersion = ver;
			}
		else if(IsNonRootNodeDep)
			{
			User::Leave(EMissingPackageVersion);
			}

		// Get dependent package vendor
		packageElement = dependency[i]->Element(KFieldDepVendor);
		if (packageElement)
			{
			depPackageInfo->iVendor = packageElement->ContentUnicodeL();
			}
		else if(IsNonRootNodeDep)
			{
			User::Leave(EMissingVendorName);
			}

#ifdef url_support
		// Get dependent package URL
		packageElement = dependency[i]->Element(KFieldDepUrl);

		if (packageElement)
			{
			TPtrC8 urlPtr = packageElement->Content();
			HBufC8* url = urlPtr.AllocL();

			TPtr8 urlPtr2 = url->Des();
			urlPtr2.Copy(urlPtr);

			depPackageInfo->iUrl = url;
			}
		else if(IsNonRootNodeDep)
			{
			User::Leave(EMissingPackageUrl);
			}
#else
		// Ignore the Url(if specified) for the root node.
		if(IsNonRootNodeDep)
			{
			// Get dependent package URL
			packageElement = dependency[i]->Element(KFieldDepUrl);
			if (packageElement)
				{
				depPackageInfo->iUrl = packageElement->Content().AllocL();
				}
			else
				{
				User::Leave(EMissingPackageUrl);
				}
			}
#endif
		//Set the changes file name
		depPackageInfo->SetChangesFileNameL();
		}
	CleanupStack::PopAndDestroy(&dependency);
	}

TInt CXmlParser::StringToInt(const TDesC8& aString, TUint32& aInt)
	{
	 // Function will return EFalse if string conversion fails.
	 TLex8 lex(aString);
	 lex.SkipSpace();

	 if (lex.Peek() == '0')
		 {
		 lex.Inc();
		 TChar c( lex.Peek() );
		 c.LowerCase();
		 if (c == 'x')
			 {
			 lex.Inc();
			 return ( lex.Val(aInt,EHex) );
			 }
		 else
			 {
			 lex.UnGet();
			 }
		 }
	 return ( lex.Val(aInt,EDecimal) );
	}