/*
* Copyright (c) 2002 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of the License "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: 
*  Decompression buffer manager for GZip data
*
*
*/

#include <EZStream.h>
#include "GZipBufMgr.h"
//-----------------------------------------------------------------------------

// NOTE: This code was taken from the DeflateFilter (s60/DeflateFilter/)

// ----------------------------------------------------------------------------
// StreamBufMgr::NewL
// Two-phase constructor
// ----------------------------------------------------------------------------
//
GZipBufMgr* GZipBufMgr::NewL(TPtrC8 in)
    {
    GZipBufMgr* self = new (ELeave)GZipBufMgr(in);
    CleanupStack::PushL(self);
    self->ConstructL();
    CleanupStack::Pop();
    return self;
    }//lint !e1746 const reference is not necessary here, because TPtrC8 takes only 4-bytes

// ----------------------------------------------------------------------------
// GZipBufMgr::GZipBufMgr
// Constructor
// ----------------------------------------------------------------------------
//
GZipBufMgr::GZipBufMgr(TPtrC8 in)
                    : iInput(in), iOutputDes(0, 0), iOutput(0),
                      iNeedInput(EFalse), iNeedOutput(EFalse), 
                      iFinalized(EFalse), iOffset(0), iID1(31), iID2(139)
    {
    } //lint !e1746

// ----------------------------------------------------------------------------
// GZipBufMgr::ConstructL
// Initialize the output buffer
// ----------------------------------------------------------------------------
//
void GZipBufMgr::ConstructL()
    {
    // Output buffer and descriptor
    iOutput = HBufC8::NewMaxL(1<<15);
    iOutputDes.Set(iOutput->Des());

    // Setup the GZip header, this sets iOffset to after the header and start
    // of the data
	TEZGZipHeader header;
    ReadGZipHeaderL(header);

    // Move pass the header and set the buffer to the data
	TPtrC8 data(REINTERPRET_CAST(const TUint8*, iInput.Ptr() + iOffset), iInput.Length() - iOffset);

    // Setup the data buffer
	SetGZipBuffer(data);

    }

// ----------------------------------------------------------------------------
// GZipBufMgr::~GZipBufMgr
// Destructor
// ----------------------------------------------------------------------------
//
GZipBufMgr::~GZipBufMgr()
    {
    delete iOutput;
    iOutput = NULL;
    }

// ----------------------------------------------------------------------------
// GZipBufMgr::InitializeL
// Overriden function from MEZBufferManager
// ----------------------------------------------------------------------------
//
void GZipBufMgr::InitializeL(CEZZStream& aZStream)
    {
    // read more
    aZStream.SetInput(iInput);
    iOutputDes.SetLength(0);
    aZStream.SetOutput(iOutputDes);
    iNeedInput = EFalse;
    iNeedOutput = EFalse;
    }

// ----------------------------------------------------------------------------
// GZipBufMgr::NeedInputL
// Overriden function from MEZBufferManager
// ----------------------------------------------------------------------------
//
void GZipBufMgr::NeedInputL(CEZZStream& /*aZStream*/)
    {
    //aZStream.SetInput(iInput);
    iNeedInput = ETrue;
    }

// ----------------------------------------------------------------------------
// GZipBufMgr::NeedOutputL
// Overriden function from MEZBufferManager
// ----------------------------------------------------------------------------
//
void GZipBufMgr::NeedOutputL(CEZZStream& /*aZStream*/)
    {
    //aZStream.SetOutput(iOutputDes);
    iNeedOutput = ETrue;
    }

// ----------------------------------------------------------------------------
// GZipBufMgr::NeedOutputL
// Overriden function from MEZBufferManager
// ----------------------------------------------------------------------------
//
void GZipBufMgr::FinalizeL(CEZZStream& /*aZStream*/)
    {
    // do nothing now, should check the checksum value here
    // read the checksum value from the last chunk
    //const TInt32* checksum = REINTERPRET_CAST(const TInt32*, iInput.Ptr() + (iInput.Length() - sizeof(TInt32)*2));
    iFinalized = ETrue;
    }


// ----------------------------------------------------------------------------
// Public methods
// ----------------------------------------------------------------------------

// ----------------------------------------------------------------------------
// GZipBufMgr::ReadGZipHeaderL
// Read the header of Gzip-ed stream in Gzip format, RFC1952.
// Sets the iOffset pointer, which marks the end of the header and start of the
// data.  This method needs to be called (or iOffset set) before calling
// SetGZipBuffer method.
// ----------------------------------------------------------------------------
//
void GZipBufMgr::ReadGZipHeaderL(TEZGZipHeader& aHeader)
    {
    // construct a stream for reading
    RMemReadStream memBuf;
    memBuf.Open(iInput.Ptr(), iInput.Length());
    
    TInt obligatoryData = sizeof(aHeader.iId1) + sizeof(aHeader.iId2) +
                          sizeof(aHeader.iCompressionMethod) +
                          sizeof(aHeader.iFlags) + sizeof(aHeader.iTime) +
                          sizeof(aHeader.iExtraFlags) + sizeof(aHeader.iOs);
    
    // copy the header data
    TPtr8 des(&aHeader.iId1, 0, obligatoryData);
    TRAPD(error, memBuf.ReadL(des));
    
    if (error == KErrEof)
        {
        User::Leave(EZGZipFile::EBadGZipHeader);
        }
    
    if (des.Size() < obligatoryData)
        {
        // TODO: should consider the situation when not enough data has arrived
        User::Leave(EZGZipFile::EBadGZipHeader);
        }
    
    if (aHeader.iId1 != iID1 || aHeader.iId2 != iID2)
        {
        User::Leave(EZGZipFile::EBadGZipHeader);
        }
    
    if (aHeader.iFlags & (1 << EZGZipFile::EFExtra))
        {
        // The extra bit is set
        des.Set(REINTERPRET_CAST(TUint8 *, &aHeader.iXlen), 0, sizeof(aHeader.iXlen));
        memBuf.ReadL(des);
        if (des.Size() != sizeof(aHeader.iXlen) || aHeader.iXlen < 0) //lint !e737 sign/unsigned doesn't realy matter here
            {
            User::Leave(EZGZipFile::EBadGZipHeader);
            }
        
        aHeader.iExtra = HBufC8::NewMaxL(aHeader.iXlen);
        TPtr8 des1 = aHeader.iExtra->Des();
        memBuf.ReadL(des1);
        
        if (des1.Size() != aHeader.iXlen)
            {
            User::Leave(EZGZipFile::EBadGZipHeader);
            }
        }
    
    if (aHeader.iFlags & (1 << EZGZipFile::EFName))
        {
        // Read in filename
        ReadStringIntoDescriptorL(memBuf, &aHeader.iFname);
        }
    
    if (aHeader.iFlags & (1 << EZGZipFile::EFComment))
        {
        // Read in comment
        ReadStringIntoDescriptorL(memBuf, &aHeader.iComment);
        }
    
    if (aHeader.iFlags & (1 << EZGZipFile::EFHcrc))
        {
        des.Set(REINTERPRET_CAST(TUint8*, &aHeader.iCrc), 0, sizeof(aHeader.iCrc));
        memBuf.ReadL(des);
        if (des.Size() != sizeof(aHeader.iCrc)) //lint !e737 unsigned/signed doesn't matter
            {
            User::Leave(EZGZipFile::EBadGZipHeader);
            }
        }
    
    // Set iOffset to mark end of header and beginning of data
    iOffset = memBuf.Source()->TellL(MStreamBuf::ERead).Offset();
    
    memBuf.Close();
    }

// ----------------------------------------------------------------------------
// GZipBufMgr::ReadStringIntoDescriptorL
// Read null-terminated strings into the buffer
// ----------------------------------------------------------------------------
//
void GZipBufMgr::ReadStringIntoDescriptorL(RMemReadStream& aMem, HBufC8 **aDes) const
    {
    TInt i;
    CArrayFixFlat<TUint8> *bytes = new (ELeave) CArrayFixFlat<TUint8>(16);
    CleanupStack::PushL(bytes);
    TUint8 ch;
    
    while ((ch = aMem.ReadUint8L()) != '\0')
        {
        bytes->AppendL(ch);
        }
    
    *aDes = HBufC8::NewMaxL(bytes->Count());
    TPtr8 desPtr = (*aDes)->Des(); //lint because error #111 - separate desPtr used
    
    for (i = 0; i < bytes->Count(); i++)
        {
        desPtr[i] = (*bytes)[i]; //lint !e1058 force conversion here
        }
    
    CleanupStack::PopAndDestroy(); // delete bytes	
    }

// ----------------------------------------------------------------------------
// GZipBufMgr::SetBuffer
// Set the input buffer
// ----------------------------------------------------------------------------
//
void GZipBufMgr::SetGZipBuffer(TPtrC8 data)
    {
    iInput.Set(data);
    }//lint !e1746
