/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*************************************************************************
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (the "License"); You may not use this file
 *  except in compliance with the License. You may obtain a copy of the
 *  License at http://www.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: IBM Corporation
 *
 *  Copyright: 2008 by IBM Corporation
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/
 
#include <sal/config.h>
#include <sal/log.hxx>
 
#include <cstring>
 
#include "first.hxx"
#include <assert.h>
namespace OpenStormBento
{
 
BenError
CBenTOCReader::ReadLabelAndTOC()
{
    BenError Err;
 
    unsigned long TOCOffset;
    if ((Err = ReadLabel(&TOCOffset, &cTOCSize)) != BenErr_OK)
        return Err;
 
    sal_uLong nLength = cpContainer->GetSize();
 
    if (TOCOffset > nLength)
        return BenErr_ReadPastEndOfTOC;
 
    if (cTOCSize > nLength - TOCOffset)
        return BenErr_ReadPastEndOfTOC;
 
    cpContainer->SeekToPosition(TOCOffset);
 
    cpTOC.reset( new BenByte[cTOCSize] );
    if ((Err = cpContainer->ReadKnownSize(cpTOC.get(), cTOCSize)) != BenErr_OK)
        return Err;
 
    if ((Err = ReadTOC()) != BenErr_OK)
        return Err;
 
    return BenErr_OK;
}
 
BenError
CBenTOCReader::ReadLabel(unsigned long * pTOCOffset, unsigned long * pTOCSize)
{
    // If seek fails, then probably because stream is smaller than
    // BEN_LABEL_SIZE and thus can't be Bento container
    BenError Err;
    cpContainer->SeekFromEnd(-BEN_LABEL_SIZE);
 
    BenByte Label[BEN_LABEL_SIZE];
    if ((Err = cpContainer->ReadKnownSize(Label, BEN_LABEL_SIZE)) != BenErr_OK)
        return Err;
 
    if (memcmp(Label, gsBenMagicBytes, BEN_MAGIC_BYTES_SIZE) != 0)
        if ((Err = SearchForLabel(Label)) != BenErr_OK)
            return Err;
 
    BenByte * pCurrLabel = Label + BEN_MAGIC_BYTES_SIZE;
 
    BenWord Flags =
        UtGetIntelWord(pCurrLabel); pCurrLabel += 2; // Flags
    // Newer files are 0x0101--indicates if big or little endian.  Older
    // files are 0x0 for flags
    if (Flags != 0x0101 && Flags != 0x0)
        return BenErr_UnknownBentoFormatVersion;
 
    cBlockSize = UtGetIntelWord(pCurrLabel) * 1024; pCurrLabel += 2;
    if (cBlockSize == 0)
        return BenErr_NotBentoContainer;
 
    // Check major version
    if (UtGetIntelWord(pCurrLabel) != BEN_CURR_MAJOR_VERSION)
        return BenErr_UnknownBentoFormatVersion;
    pCurrLabel += 2;
 
    pCurrLabel += 2;    // Minor version
 
    *pTOCOffset = UtGetIntelDWord(pCurrLabel); pCurrLabel += 4;
    *pTOCSize = UtGetIntelDWord(pCurrLabel);
 
    assert(pCurrLabel + 4 == Label + BEN_LABEL_SIZE);
 
    return BenErr_OK;
}
 
#define LABEL_READ_BUFFER_SIZE 500
#define MAX_SEARCH_AMOUNT 1024 * 1024
 
BenError
CBenTOCReader::SearchForLabel(BenByte * pLabel)
{
    BenError Err;
 
    sal_uLong Length = cpContainer->GetSize();
 
    // Always ready to check for MagicBytes from
    // CurrOffset - BEN_MAGIC_BYTES_SIZE to CurrOffset - 1
    unsigned long CurrOffset = Length - BEN_LABEL_SIZE + BEN_MAGIC_BYTES_SIZE -
      1;
 
    char Buffer[LABEL_READ_BUFFER_SIZE] = {0};
 
    unsigned long BufferStartOffset = Length;   // Init to big value
 
    while (CurrOffset >= BEN_MAGIC_BYTES_SIZE)
    {
        // Don't search backwards more than 1 meg
        if (Length - CurrOffset > MAX_SEARCH_AMOUNT)
            break;
 
        // If before beginning of buffer
        if (CurrOffset - BEN_MAGIC_BYTES_SIZE < BufferStartOffset)
        {
            unsigned long UsedBufferSize;
            if (CurrOffset < LABEL_READ_BUFFER_SIZE)
                UsedBufferSize = CurrOffset;
            else UsedBufferSize = LABEL_READ_BUFFER_SIZE;
 
            cpContainer->SeekToPosition(CurrOffset - UsedBufferSize);
 
            if ((Err = cpContainer->ReadKnownSize(Buffer, UsedBufferSize)) !=
              BenErr_OK)
                return Err;
 
            BufferStartOffset = CurrOffset - UsedBufferSize;
        }
 
        if (memcmp(Buffer + (CurrOffset - BEN_MAGIC_BYTES_SIZE -
          BufferStartOffset), gsBenMagicBytes, BEN_MAGIC_BYTES_SIZE) == 0)
        {
            cpContainer->SeekToPosition(CurrOffset -
              BEN_MAGIC_BYTES_SIZE);
 
            return cpContainer->ReadKnownSize(pLabel, BEN_LABEL_SIZE);
        }
 
        --CurrOffset;
    }
 
    return BenErr_NotBentoContainer;    // Didn't find magic bytes
}
 
BenError
CBenTOCReader::ReadTOC()
{
    BenError Err;
    BenByte LookAhead = GetCode();
    BenGeneration Generation = 0;
 
    // Read in all objects
    while (LookAhead == BEN_NEW_OBJECT)
    {
        BenObjectID ObjectID;
        if ((Err = GetDWord(&ObjectID)) != BenErr_OK)
            return Err;
        CBenObject * pObject = nullptr;
 
        // Read in all properties for object
        do
        {
            BenObjectID PropertyID;
 
            if ((Err = GetDWord(&PropertyID)) != BenErr_OK)
                return Err;
            CBenProperty * pProperty = nullptr;
 
            // Read in all values for property
            do
            {
                BenObjectID ReferencedListID = 0;
 
                BenObjectID TypeID;
                if ((Err = GetDWord(&TypeID)) != BenErr_OK)
                    return Err;
                LookAhead = GetCode();
 
                if (LookAhead == BEN_EXPLICIT_GEN)
                {
                    if ((Err = GetDWord(&Generation)) != BenErr_OK)
                        return Err;
                    LookAhead = GetCode();
                }
 
                if (LookAhead == BEN_REFERENCE_LIST_ID)
                {
                    if ((Err = GetDWord(&ReferencedListID)) != BenErr_OK)
                        return Err;
                    LookAhead = GetCode();
                }
 
                if (PropertyID == BEN_PROPID_GLOBAL_PROPERTY_NAME ||
                    PropertyID == BEN_PROPID_GLOBAL_TYPE_NAME)
                {
                    // Read property or type name
 
                    if (pObject != nullptr || TypeID != BEN_TYPEID_7_BIT_ASCII ||
                      LookAhead != BEN_OFFSET4_LEN4)
                        return BenErr_NamedObjectError;
 
                    BenContainerPos Pos;
                    sal_uInt32 Length;
 
                    if ((Err = GetDWord(&Pos)) != BenErr_OK)
                        return Err;
                    if ((Err = GetDWord(&Length)) != BenErr_OK)
                        return Err;
                    LookAhead = GetCode();
 
                    cpContainer->SeekToPosition(Pos);
 
                    const auto nRemainingSize = cpContainer->remainingSize();
                    if (Length > nRemainingSize)
                    {
                        SAL_WARN("lwp", "stream too short for claimed no of records");
                        Length = nRemainingSize;
                    }
 
                    #define STACK_BUFFER_SIZE 256
                    char sStackBuffer[STACK_BUFFER_SIZE];
                    char * sAllocBuffer;
                    char * sBuffer;
                    if (Length > STACK_BUFFER_SIZE)
                    {
                        sBuffer = new char[Length];
                        sAllocBuffer = sBuffer;
                    }
                    else
                    {
                        sBuffer = sStackBuffer;
                        sAllocBuffer = nullptr;
                    }
 
                    if ((Err = cpContainer->ReadKnownSize(sBuffer, Length)) !=
                      BenErr_OK)
                    {
                        delete[] sAllocBuffer;
                        return Err;
                    }
 
                    OString sName(sBuffer, Length);
 
                    CUtListElmt * pPrevNamedObjectListElmt;
                    if (FindNamedObject(&cpContainer->GetNamedObjects(),
                      sName, &pPrevNamedObjectListElmt) != nullptr)
                    {
                        delete[] sAllocBuffer;
                        return BenErr_DuplicateName;
                    }
 
                    CUtListElmt* pPrevObject = cpContainer->GetObjects().GetLast();
 
                    if (PropertyID == BEN_PROPID_GLOBAL_PROPERTY_NAME)
                        pObject = new CBenPropertyName(cpContainer, ObjectID,
                          pPrevObject, sName, pPrevNamedObjectListElmt);
                    else pObject = new CBenTypeName(cpContainer, ObjectID,
                      pPrevObject, sName, pPrevNamedObjectListElmt);
 
                    delete[] sAllocBuffer;
                }
                else if (PropertyID == BEN_PROPID_OBJ_REFERENCES)
                {
                    // Don't need to read in references object--we assume
                    // that all references use object ID as key
                    if ((Err = ReadSegments(nullptr, &LookAhead)) != BenErr_OK)
                        return Err;
                }
                else if (ObjectID == BEN_OBJID_TOC)
                {
                    if (PropertyID == BEN_PROPID_TOC_SEED)
                    {
                        if (TypeID != BEN_TYPEID_TOC_TYPE ||
                          LookAhead !=  BEN_IMMEDIATE4)
                            return BenErr_TOCSeedError;
 
                        BenDWord Data;
                        if ((Err = GetDWord(&Data)) != BenErr_OK)
                            return Err;
                        LookAhead = GetCode();
 
                        cpContainer->SetNextAvailObjectID(Data);
                    }
                    else
                    {
                        // Ignore the other BEN_OBJID_TOC properties
                        if ((Err = ReadSegments(nullptr, &LookAhead)) != BenErr_OK)
                            return Err;
                    }
                }
                else
                {
                    if (pProperty != nullptr)
                        return BenErr_PropertyWithMoreThanOneValue;
 
                    if (pObject == nullptr)
                        pObject = new CBenObject(cpContainer, ObjectID,
                          cpContainer->GetObjects().GetLast());
 
                    pProperty = new CBenProperty(pObject, PropertyID, TypeID,
                      pObject->GetProperties().GetLast());
 
                    if ((Err = ReadSegments(&pProperty->UseValue(),
                      &LookAhead)) != BenErr_OK)
                        return Err;
                }
            } while (LookAhead == BEN_NEW_TYPE);
        } while (LookAhead == BEN_NEW_PROPERTY);
    }
 
    if (LookAhead == BEN_READ_PAST_END_OF_TOC)
        return BenErr_OK;
    else return BenErr_InvalidTOC;
}
 
BenError
CBenTOCReader::ReadSegments(CBenValue * pValue, BenByte * pLookAhead)
{
    BenError Err;
 
    while (*pLookAhead >= BEN_SEGMENT_CODE_START &&
      *pLookAhead <= BEN_SEGMENT_CODE_END)
    {
        if ((Err = ReadSegment(pValue, pLookAhead)) !=
          BenErr_OK)
            return Err;
    }
 
    return BenErr_OK;
}
 
BenError
CBenTOCReader::ReadSegment(CBenValue * pValue, BenByte * pLookAhead)
{
    BenError Err;
 
    bool Immediate = false;
    bool EightByteOffset = false;
    sal_uInt32 Offset(0), Length(0);
 
    switch (*pLookAhead)
    {
        case BEN_CONT_OFFSET4_LEN4:
        case BEN_OFFSET4_LEN4:
            if ((Err = GetDWord(&Offset))  != BenErr_OK)
                return Err;
            if ((Err = GetDWord(&Length))  != BenErr_OK)
                return Err;
            break;
 
        case BEN_IMMEDIATE0:
            Length = 0; Immediate = true;
            break;
 
        case BEN_IMMEDIATE1:
            Length = 1; Immediate = true;
            break;
 
        case BEN_IMMEDIATE2:
            Length = 2; Immediate = true;
            break;
 
        case BEN_IMMEDIATE3:
            Length = 3; Immediate = true;
            break;
 
        case BEN_CONT_IMMEDIATE4:
        case BEN_IMMEDIATE4:
            Length = 4; Immediate = true;
            break;
 
        case BEN_CONT_OFFSET8_LEN4:
        case BEN_OFFSET8_LEN4:
            EightByteOffset = true;
            break;
 
        default:
            return BenErr_OK;
    }
 
    BenByte ImmData[4];
    if (Immediate && Length != 0)
        if ((Err = GetData(ImmData, 4)) != BenErr_OK)
            return Err;
 
    *pLookAhead = GetCode();
 
    if (EightByteOffset)
        return BenErr_64BitOffsetNotSupported;
 
    if (pValue != nullptr)
    {
        if (! Immediate)
            new CBenValueSegment(pValue, Offset, Length);
        else if (Length != 0)
        {
            assert(Length <= 4);
            new CBenValueSegment(pValue, ImmData, static_cast<unsigned short>(Length));
        }
    }
 
    return BenErr_OK;
}
 
bool
CBenTOCReader::CanGetData(unsigned long Amt)
{
    return cCurr + Amt <= cTOCSize;
}
 
BenError
CBenTOCReader::GetByte(BenByte * pByte)
{
    if (! CanGetData(1))
        return BenErr_ReadPastEndOfTOC;
 
    *pByte = UtGetIntelByte(cpTOC.get() + cCurr);
    ++cCurr;
    return BenErr_OK;
}
 
BenError
CBenTOCReader::GetDWord(BenDWord * pDWord)
{
    if (! CanGetData(4))
        return BenErr_ReadPastEndOfTOC;
 
    *pDWord = UtGetIntelDWord(cpTOC.get() + cCurr);
    cCurr += 4;
    return BenErr_OK;
}
 
BenByte
CBenTOCReader::GetCode()
{
    BenByte Code;
    do
    {
        if (GetByte(&Code) != BenErr_OK)
            return BEN_READ_PAST_END_OF_TOC;
 
        if (Code == BEN_END_OF_BUFFER)
            // Advance to next block
            cCurr = cBlockSize * ((cCurr + (cBlockSize - 1)) /
              cBlockSize);
    }
    while (Code == BEN_NOOP || Code == BEN_END_OF_BUFFER);
    return Code;
}
 
BenError
CBenTOCReader::GetData(void * pBuffer, unsigned long Amt)
{
    if (! CanGetData(Amt))
        return BenErr_ReadPastEndOfTOC;
 
    std::memcpy(pBuffer, cpTOC.get() + cCurr, Amt);
    cCurr += Amt;
    return BenErr_OK;
}
}//end OpenStormBento namespace
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

V512 A call of the 'memcmp' function will lead to underflow of the buffer 'Label'.