/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2000, 2010 Oracle and/or its affiliates.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org 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 version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_bridges.hxx"
#include <string.h>

#include <osl/diagnose.h>

#include <rtl/alloc.h>
#include <rtl/ustrbuf.hxx>

#include <uno/data.h>
#include <uno/any2.h>
#include <uno/sequence2.h>

#include <com/sun/star/uno/Any.hxx>

#include "urp_unmarshal.hxx"
#include "urp_bridgeimpl.hxx"


using namespace ::rtl;
using namespace ::com::sun::star::uno;

namespace bridges_urp
{
static int g_nDetectLittleEndian = 1;
char g_bSystemIsLittleEndian = ((char*)&g_nDetectLittleEndian)[0];


Unmarshal::Unmarshal( struct urp_BridgeImpl *pBridgeImpl,
                      uno_Environment *pEnvRemote,
                      remote_createStubFunc callback ) :
    m_nBufferSize( 4096 ),
    m_base( (sal_Int8*) rtl_allocateMemory( m_nBufferSize ) ),
    m_pos( m_base ),
    m_nLength( 0 ),
    m_callback( callback ),
    m_pEnvRemote( pEnvRemote ),
    m_pBridgeImpl( pBridgeImpl )
{
}

Unmarshal::~Unmarshal()
{
    rtl_freeMemory( m_base );
}

// special unpacks
sal_Bool Unmarshal::unpackTid( sal_Sequence **ppThreadId )
{
    sal_Int32 nSize;
    sal_Bool bReturn = unpackCompressedSize( &nSize );
    if( bReturn )
    {
        if( nSize )
        {
            rtl_byte_sequence_constructFromArray( ppThreadId , m_pos , nSize );
            m_pos += nSize;
            sal_uInt16 nIndex;
            bReturn = unpackInt16( &nIndex );
            if( nIndex < m_pBridgeImpl->m_properties.nTidCacheSize )
            {
                m_pBridgeImpl->m_pTidIn[nIndex] = *(ByteSequence * )ppThreadId;
            }
            else if( 0xffff != nIndex )
            {
                bReturn = sal_False;
                rtl_byte_sequence_construct( ppThreadId , 0 );

                OUStringBuffer error( 128 );
                error.appendAscii( "cache index for tid " );
                OString o = byteSequence2HumanReadableString( *(ByteSequence * ) ppThreadId );
                error.appendAscii( o.getStr(), o.getLength() );
                error.appendAscii( "out of range(0x");
                error.append(  (sal_Int32) nIndex ,16 );
                error.appendAscii( ")" );
                m_pBridgeImpl->addError( error.makeStringAndClear() );
            }
        }
        else
        {
            sal_uInt16 nIndex;
            bReturn = unpackInt16( &nIndex );
            if( nIndex < m_pBridgeImpl->m_properties.nTidCacheSize )
            {
                *ppThreadId = m_pBridgeImpl->m_pTidIn[nIndex].getHandle();
                rtl_byte_sequence_acquire( *ppThreadId );
            }
            else
            {
                bReturn = sal_False;
                rtl_byte_sequence_construct( ppThreadId , 0 );
                OUStringBuffer error(128);
                error.appendAscii( "cache index for tids out of range(0x" );
                error.append( (sal_Int32) nIndex ,16 );
                error.appendAscii( ")" );
                m_pBridgeImpl->addError( error.makeStringAndClear() );
            }
        }
    }
    else
    {
        m_pBridgeImpl->addError( "couldn't unpack thread id because of previous errors" );
    }
    return bReturn;
}

sal_Bool Unmarshal::unpackOid( rtl_uString **ppOid )
{
    sal_Bool bReturn = sal_True;
    sal_uInt16 nCacheIndex = 0;

    bReturn = bReturn && unpackString( ppOid );
    bReturn = bReturn && unpackInt16( &nCacheIndex );

    if( bReturn &&
        ! ( 0xffff == nCacheIndex && 0 == (*ppOid)->length ) /* null reference */  )
    {
        if( (*ppOid)->length )
        {
            // new unknown reference
            if( 0xffff != nCacheIndex )
            {
                // oid should be cached ?
                if( nCacheIndex < m_pBridgeImpl->m_properties.nOidCacheSize )
                {
                    m_pBridgeImpl->m_pOidIn[nCacheIndex] = *ppOid;
                }
                else
                {
                    OUStringBuffer error( 128 );
                    error.appendAscii( "new oid provided (" );
                    error.append( *ppOid );
                    error.appendAscii( "), but new cache index is out of range(0x");
                    error.append(  (sal_Int32) nCacheIndex ,16 );
                    error.appendAscii( ")" );
                    m_pBridgeImpl->addError( error.makeStringAndClear() );

                    bReturn = sal_False;
                    rtl_uString_new( ppOid );
                }
            }
        }
        else
        {
            // reference in cache !
            if( nCacheIndex < m_pBridgeImpl->m_properties.nOidCacheSize )
            {
                rtl_uString_assign( ppOid , m_pBridgeImpl->m_pOidIn[nCacheIndex].pData );
            }
            else
            {
                bReturn = sal_False;
                rtl_uString_new( ppOid );

                OUStringBuffer error( 128 );
                error.appendAscii( "cache index for oids out of range(0x");
                error.append(  (sal_Int32) nCacheIndex ,16 );
                error.appendAscii( ")" );
                m_pBridgeImpl->addError( error.makeStringAndClear() );
            }
        }
    }

    return bReturn;
}

sal_Bool Unmarshal::unpack( void *pDestination ,
                            typelib_TypeDescription *pTypeDescr )
{
    // Note: We implement unpack functionality without recursions in order
    //       to avoid stack overflows caused by rotten URP blocks.

    m_aItemsToUnpack.push( UnpackItem( pDestination, pTypeDescr ) );

    sal_Bool bReturn = sal_True;
    do
    {
        void * pDest = m_aItemsToUnpack.top().pDest;
        typelib_TypeDescription * pType = m_aItemsToUnpack.top().pType;
        m_aItemsToUnpack.pop();

        switch( pType->eTypeClass )
        {
        case typelib_TypeClass_VOID:
            // do nothing
            break;
        case typelib_TypeClass_BYTE:
        {
            bReturn = unpackInt8( pDest );
            break;
        }
        case typelib_TypeClass_BOOLEAN:
        {
            bReturn = ! checkOverflow( 1 );
            if( bReturn )
            {
                *((sal_Bool*)pDest) = (sal_Bool ) ( *m_pos);
                m_pos ++;
            }
            else
            {
                *((sal_Bool*)pDest) = 0;
            }
            break;
        }

        case typelib_TypeClass_CHAR:
        case typelib_TypeClass_SHORT:
        case typelib_TypeClass_UNSIGNED_SHORT:
        {
            unpackInt16( pDest );
            break;
        }
        case typelib_TypeClass_ENUM:
        case typelib_TypeClass_FLOAT:
        case typelib_TypeClass_LONG:
        case typelib_TypeClass_UNSIGNED_LONG:
        {
            bReturn = unpackInt32( pDest );
            break;
        }
        case typelib_TypeClass_DOUBLE:
        case typelib_TypeClass_HYPER:
        case typelib_TypeClass_UNSIGNED_HYPER:
        {
            sal_Int8 * p = static_cast<  sal_Int8 * >(pDest);
            bReturn = ! checkOverflow( 8 );
            if( bReturn )
            {
                if( isSystemLittleEndian() )
                {
                    p[7] = m_pos[0];
                    p[6] = m_pos[1];
                    p[5] = m_pos[2];
                    p[4] = m_pos[3];
                    p[3] = m_pos[4];
                    p[2] = m_pos[5];
                    p[1] = m_pos[6];
                    p[0] = m_pos[7];
                }
                else
                {
                    p[0] = m_pos[0];
                    p[1] = m_pos[1];
                    p[2] = m_pos[2];
                    p[3] = m_pos[3];
                    p[4] = m_pos[4];
                    p[5] = m_pos[5];
                    p[6] = m_pos[6];
                    p[7] = m_pos[7];
                }
                m_pos += 8;
            }
            else
            {
                // Do not trigger alignment errors:
                p[0] = p[1] = p[2] = p[3] = p[4] = p[5] = p[6] = p[7] = 0;
            }
            break;
        }
        case typelib_TypeClass_STRING:
        {
            unpackString( pDest );
            break;
        }
        case typelib_TypeClass_ANY:
        {
            uno_Any *pAny = ( uno_Any * )pDest;

            pAny->pType = 0;
            // Type is acquired with typelib_typedescription_acquire

            bReturn = unpackType( &(pAny->pType) );

            typelib_TypeDescription *pDataType = 0;
            if( bReturn && pAny->pType )
            {
                typelib_typedescriptionreference_getDescription( &pDataType , pAny->pType );

                if( pDataType )
                {
                    switch (pDataType->eTypeClass)
                    {
                    case typelib_TypeClass_HYPER:
                    case typelib_TypeClass_UNSIGNED_HYPER:
                        if (sizeof(void *) < sizeof(sal_Int64))
                        {
                            pAny->pData = rtl_allocateMemory( sizeof(sal_Int64) );
                        }
                        else
                        {
                            pAny->pData = &pAny->pReserved;
                        }
                        break;
                    case typelib_TypeClass_FLOAT:
                        if (sizeof(void *) < sizeof(float))
                        {
                            pAny->pData = rtl_allocateMemory( sizeof(float) );
                        }
                        else
                        {
                            pAny->pData = &pAny->pReserved;
                        }
                        break;
                    case typelib_TypeClass_DOUBLE:
                        if (sizeof(void *) < sizeof(double))
                        {
                            pAny->pData = rtl_allocateMemory( sizeof(double) );
                        }
                        else
                        {
                            pAny->pData = &pAny->pReserved;
                        }
                        break;
                    case typelib_TypeClass_STRUCT:
                    case typelib_TypeClass_UNION:
                    case typelib_TypeClass_EXCEPTION:
                    case typelib_TypeClass_ARRAY:
                        pAny->pData = rtl_allocateMemory( pDataType->nSize );
                        break;
                    case typelib_TypeClass_ANY:
                    {
                        m_pBridgeImpl->addError(
                            OUString( RTL_CONSTASCII_USTRINGPARAM(
                                "can't unmarshal any: any in any not supported!" ) ) );

                        pAny->pData = 0;
                        Type type; // void
                        pAny->pType = type.getTypeLibType();
                        typelib_typedescriptionreference_acquire( pAny->pType );

                        bReturn = sal_False;
                        break;
                    }
                    default:
                        pAny->pData = &pAny->pReserved;
                    }

                    if ( bReturn )
                    {
                        m_aItemsToUnpack.push(
                            UnpackItem( pAny->pData, pDataType ) );
                    }
                }
                else
                {
                    OUStringBuffer error;
                    error.appendAscii( "can't unmarshal any because typedescription for " );
                    error.append( pAny->pType->pTypeName );
                    error.appendAscii( " is missing" );
                    m_pBridgeImpl->addError( error.makeStringAndClear() );
                }
            }

            if( pDataType )
            {
                typelib_typedescription_release( pDataType );
            }
            else
            {
                pAny->pData = 0;
                Type type; // void
                pAny->pType = type.getTypeLibType();
                typelib_typedescriptionreference_acquire( pAny->pType );

                bReturn = sal_False;
            }

            break;
        }
        case typelib_TypeClass_INTERFACE:
        {
            *(remote_Interface**)pDest = 0;

            rtl_uString *pString = 0;
            bReturn = unpackOid( &pString ) && bReturn;

            if( bReturn && pString && pString->length )
            {
                m_callback( (remote_Interface**) pDest ,
                            pString,
                            pType->pWeakRef ,
                            m_pEnvRemote,
                            urp_releaseRemoteCallback );
            }
            if( pString )
            {
                rtl_uString_release( pString );
            }
            break;
        }
        case typelib_TypeClass_TYPE:
        {
            bReturn = unpackType( pDest );
            break;
        }
        case typelib_TypeClass_STRUCT:
        case typelib_TypeClass_EXCEPTION:
        {
            typelib_CompoundTypeDescription * pCompType =
                (typelib_CompoundTypeDescription *)pType;

            // direct members
            typelib_TypeDescriptionReference ** ppTypeRefs = pCompType->ppTypeRefs;
            sal_Int32 * pMemberOffsets = pCompType->pMemberOffsets;
            sal_Int32 nDescr = pCompType->nMembers;

            // at least assume 1 byte per member
            bReturn = bReturn && ! checkOverflow( nDescr * 1 );
            for ( sal_Int32 nPos = nDescr; nPos; --nPos )
            {
                typelib_TypeDescription * pMemberType = 0;
                typelib_typedescriptionreference_getDescription(
                    &pMemberType, ppTypeRefs[ nPos - 1 ] );

                m_aItemsToUnpack.push(
                    UnpackItem( (char*)pDest + pMemberOffsets[ nPos - 1 ],
                                pMemberType,
                                true /* construct even in error case */ ) );

                m_aTypesToRelease.push_back( pMemberType );
            }

            // parent
            if (pCompType->pBaseTypeDescription)
            {
                m_aItemsToUnpack.push(
                    UnpackItem( pDest,
                                (typelib_TypeDescription *)
                                    pCompType->pBaseTypeDescription ) );
            }
            break;
        }
        case typelib_TypeClass_SEQUENCE:
        {
            sal_Int32 nLen;
            bReturn = unpackCompressedSize( &nLen );

            // urp protocol does not allow to use the elementsize as a guess, if enough data
            // is available. However, at least one byte per member must be within the message
            bReturn = bReturn && ! checkOverflow( 1 * nLen );
            uno_Sequence *pSequence = 0;
            if( nLen && bReturn )
            {
                typelib_TypeDescriptionReference * pETRef =
                    ((typelib_IndirectTypeDescription *)pType)->pType;

                typelib_TypeDescription * pET = 0;
                typelib_typedescriptionreference_getDescription( &pET , pETRef );

                if( pET )
                {
                    sal_Int32 nElementSize = pET->nSize;

                    pSequence = (uno_Sequence *)rtl_allocateMemory(
                        SAL_SEQUENCE_HEADER_SIZE + nElementSize * nLen );
                    pSequence->nRefCount = 1;
                    pSequence->nElements = nLen;

                    if( typelib_TypeClass_BYTE == pET->eTypeClass )
                    {
                        memcpy( pSequence->elements , m_pos , nLen );
                        m_pos += nLen;
                    }
                    else
                    {
                        for ( sal_Int32 i = nLen; i; --i )
                        {
                            m_aItemsToUnpack.push(
                                UnpackItem(
                                    ((char*)pSequence->elements)
                                        + (i - 1) * nElementSize,
                                    pET ) );
                        }
                    }
                    m_aTypesToRelease.push_back( pET );
                }
                else
                {
                    bReturn = sal_False;
                    uno_constructData( &pSequence , pType );
                    OUStringBuffer error;
                    error.appendAscii( "can't unmarshal sequence, because there is no typedescription for element type " );
                    error.append( pETRef->pTypeName );
                    error.appendAscii( " available" );
                    m_pBridgeImpl->addError( error.makeStringAndClear() );
                }
            }
            else
            {
                uno_constructData( &pSequence , pType );
            }

            *((uno_Sequence **)pDest) = pSequence;
            break;
        }
        case typelib_TypeClass_UNION:
        case typelib_TypeClass_ARRAY:
        case typelib_TypeClass_SERVICE:
        case typelib_TypeClass_MODULE:
        case typelib_TypeClass_INTERFACE_METHOD:
        case typelib_TypeClass_INTERFACE_ATTRIBUTE:
        case typelib_TypeClass_UNKNOWN:
        default:
        {
            ::rtl::OUStringBuffer buffer( 128 );
            buffer.appendAscii( RTL_CONSTASCII_STRINGPARAM("Unsupported typeclass during unmarshaling ("));
            buffer.append( ( sal_Int32 ) pType->eTypeClass , 10 );
            buffer.appendAscii( ")" );
            m_pBridgeImpl->addError( buffer.makeStringAndClear() );
            bReturn = sal_False;
        }
        }

        if ( !bReturn )
        {
            // construct default data for every remaining item.
            while ( !m_aItemsToUnpack.empty() )
            {
                const UnpackItem & rItem = m_aItemsToUnpack.top();

                if ( rItem.bMustBeConstructed )
                    uno_constructData( rItem.pDest , rItem.pType );

                m_aItemsToUnpack.pop();
            }
        }
    }
    while ( !m_aItemsToUnpack.empty() );

    // release pending type descriptions
    TypeDescVector::const_iterator it = m_aTypesToRelease.begin();
    const TypeDescVector::const_iterator end = m_aTypesToRelease.end();
    while ( it != end )
    {
        typelib_typedescription_release( *it );
        ++it;
    }
    m_aTypesToRelease.clear();

    return bReturn;
}

sal_Bool Unmarshal::unpackType( void *pDest )
{
    *(typelib_TypeDescriptionReference **) pDest = 0;

    sal_uInt8 nTypeClass;
    sal_Bool bReturn = unpackInt8( &nTypeClass );

    typelib_TypeDescriptionReference *pTypeRef = 0;
    if( bReturn )
    {
        if( nTypeClass <= 14 /* any */ )
        {
            pTypeRef = * typelib_static_type_getByTypeClass((typelib_TypeClass )nTypeClass);
            typelib_typedescriptionreference_acquire( pTypeRef );
        }
        else
        {
            sal_uInt16 nCacheIndex = 0;
            bReturn = bReturn && unpackInt16( &nCacheIndex  );

            if( bReturn )
            {
                if( nTypeClass & 0x80 )
                {
                    // new type
                    rtl_uString *pString = 0;
                    bReturn = bReturn && unpackString( &pString );
                    if( bReturn )
                    {
                        typelib_TypeDescription *pType = 0;
                        typelib_typedescription_getByName( &pType, pString );
                        if( pType )
                        {
                            // type is known in this process
                            if( (typelib_TypeClass )(nTypeClass & 0x7f) == pType->eTypeClass )
                            {
                                //typename and typeclass match, this is as it should be
                                pTypeRef = pType->pWeakRef;
                                typelib_typedescriptionreference_acquire( pTypeRef );
                            }
                            else
                            {
                                // typename and typeclass do not match, dispose the bridge
                                // as there must be inconsistent type base between both processes
                                // or trash comes over the wire ...
                                bReturn = sal_False;
                                OUStringBuffer error( 128 );
                                error.appendAscii( "it is tried to introduce type " );
                                error.append( pString );
                                error.appendAscii( "with typeclass " );
                                error.append( (sal_Int32)( nTypeClass & 0x7f ) , 10 );
                                error.appendAscii( " , which does not match with typeclass " );
                                error.append(  (sal_Int32) pType->eTypeClass ,10 );
                                m_pBridgeImpl->addError( error.makeStringAndClear() );
                            }
                            typelib_typedescription_release( pType );
                            pType = 0;
                        }
                        else
                        {
                            // a type by this name is not known in the process.
                            if( (nTypeClass & 0x7f) < typelib_TypeClass_UNKNOWN )
                            {
                                // typeclass is within a valid range, introduce the new
                                // type.
                                typelib_typedescriptionreference_new(
                                    &pTypeRef, (typelib_TypeClass )(nTypeClass & 0x7f), pString );
                            }
                            else
                            {
                                // typeclass is out of range !
                                bReturn = sal_False;
                                OUStringBuffer error( 128 );
                                error.appendAscii( "it is tried to introduce type " );
                                error.append( pString );
                                error.appendAscii( "with an out of range typeclass " );
                                error.append( (sal_Int32)( nTypeClass & 0x7f ) , 10 );
                                m_pBridgeImpl->addError( error.makeStringAndClear() );
                            }
                        }

                        if( bReturn && nCacheIndex != 0xffff )
                        {
                            if( nCacheIndex < m_pBridgeImpl->m_properties.nTypeCacheSize )
                            {
                                m_pBridgeImpl->m_pTypeIn[nCacheIndex] = *( Type * )&pTypeRef;
                            }
                            else
                            {
                                bReturn = sal_False;
                                OUStringBuffer error( 128 );
                                error.appendAscii( "cache index for type " );
                                error.append( pString );
                                error.appendAscii( "out of range(0x" );
                                error.append( (sal_Int32) nCacheIndex ,16 );
                                error.appendAscii( ")" );
                                m_pBridgeImpl->addError( error.makeStringAndClear() );
                            }
                        }
                    }
                    if( pString )
                    {
                        rtl_uString_release( pString );
                    }
                }
                else
                {
                    if( nCacheIndex < m_pBridgeImpl->m_properties.nTypeCacheSize )
                    {
                        pTypeRef = m_pBridgeImpl->m_pTypeIn[nCacheIndex].getTypeLibType();
                        typelib_typedescriptionreference_acquire( pTypeRef );
                    }
                    else
                    {
                        bReturn = sal_False;
                        OUStringBuffer error;
                        error.appendAscii( "cache index for types out of range(0x" );
                        error.append( (sal_Int32) nCacheIndex ,16 );
                        error.appendAscii( ")" );
                        m_pBridgeImpl->addError( error.makeStringAndClear() );
                    }
                }
            }
        }
    }
    if( ! pTypeRef )
    {
        pTypeRef = * typelib_static_type_getByTypeClass(typelib_TypeClass_VOID);
        typelib_typedescriptionreference_acquire( pTypeRef );
    }
    // pTypeRef is already acquired
    *(typelib_TypeDescriptionReference**)pDest = pTypeRef;
    return bReturn;
}

}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
