Files
harbour-core/contrib/hbamf/amfdec.c
Viktor Szakats e00f50079e 2017-09-11 13:12 UTC Viktor Szakats (vszakats users.noreply.github.com)
* contrib/gtqtc/gtqtc1.cpp
  * contrib/gtwvg/gtwgud.c
  * contrib/gtwvw/wvwdraw.c
  * contrib/hbamf/amfdec.c
  * contrib/hbamf/amfstdio.c
  * contrib/hbbz2io/bz2io.c
  * contrib/hbgzio/gzio.c
  * contrib/hbhpdf/core.c
  * contrib/hbmemio/memio.c
  * contrib/hbmisc/irm.c
  * contrib/hbmisc/spd.c
  * contrib/hbnetio/netiosrv.c
  * contrib/hbssl/ssl_sock.c
  * contrib/hbwin/olecore.c
  * contrib/hbwin/wapi_commctrl.c
  * contrib/hbwin/wapi_winuser.c
  * contrib/hbwin/win_bmpd.c
  * contrib/hbwin/win_misc.c
  * contrib/hbzebra/qrcode.c
  * contrib/rddads/ads1.c
  * contrib/rddads/adsfunc.c
  * contrib/rddsql/sqlmix.c
  * contrib/sddfb/core.c
  * contrib/xhb/cstructc.c
  * contrib/xhb/hboutdbg.c
  * contrib/xhb/hbserv.c
  * include/hbdefs.h
  * include/hbexprb.c
  * include/hbrddcdx.h
  * include/hbthread.h
  * include/hbwmain.c
  * src/common/hbffind.c
  * src/compiler/complex.c
  * src/compiler/hbmain.c
  * src/compiler/hbopt.c
  * src/debug/dbgentry.c
  * src/rdd/dbf1.c
  * src/rdd/dbfcdx/dbfcdx1.c
  * src/rdd/dbffpt/dbffpt1.c
  * src/rdd/dbfnsx/dbfnsx1.c
  * src/rdd/dbfntx/dbfntx1.c
  * src/rdd/hbsix/sxcompr.c
  * src/rdd/wacore.c
  * src/rdd/workarea.c
  * src/rtl/cputime.c
  * src/rtl/dates.c
  * src/rtl/gtchrmap.c
  * src/rtl/gtcrs/gtcrs.c
  * src/rtl/gtpca/gtpca.c
  * src/rtl/gtsln/gtsln.c
  * src/rtl/gtsln/kbsln.c
  * src/rtl/gtsln/keytrans.c
  * src/rtl/gtstd/gtstd.c
  * src/rtl/gttrm/gttrm.c
  * src/rtl/gtwvt/gtwvt.c
  * src/rtl/gtxwc/gtxwc.c
  * src/rtl/hbcom.c
  * src/rtl/hbproces.c
  * src/rtl/itemseri.c
  * src/rtl/langapi.c
  * src/rtl/run.c
  * src/rtl/sha1.c
  * src/rtl/space.c
  * src/vm/classes.c
  * src/vm/hvm.c
  * src/vm/itemapi.c
  * src/vm/set.c
  * src/vm/thread.c
    * convert commented C code to #if 0 blocks, or clean them up in
      different ways
2017-09-11 13:27:12 +00:00

1274 lines
31 KiB
C

/* by Aleksander Czajczynski <hb/at/fki.pl> 2011-2012
*
* Decoding AMF3 to Harbour items
*
* Contains portions from
* Dave Thompson's MIT licensed
* AmFast C library for Python
*/
#include "hbapi.h"
#include "hbapiitm.h"
#include "hbapistr.h"
#include "hbapicls.h" /* for hb_objSetClass() */
#include "hbstack.h"
#include "amf.h"
#include "hbdate.h"
#include "hbmath.h"
#include "hbvm.h"
typedef struct
{
const char * cBuf;
HB_ISIZ position;
HB_ISIZ length;
PHB_ITEM obj_ref;
PHB_ITEM str_ref;
PHB_ITEM class_ref;
PHB_ITEM conv_function;
} amfContext;
static HB_BOOL amf3_getItem( amfContext * context, PHB_ITEM pItem );
extern HB_BOOL hbamf_is_cls_externalizable( HB_USHORT uiClass );
static PHB_ITEM hbamf_cls_externalizable_instance( PHB_ITEM pClassFuncStr )
{
PHB_DYNS pSymbol = hb_dynsymGet( hb_itemGetCPtr( pClassFuncStr ) );
if( pSymbol )
{
PHB_ITEM pRetCopy = hb_itemNew( NULL );
PHB_ITEM pNewItem = hb_itemNew( NULL );
hb_itemMove( pRetCopy, hb_stackReturnItem() );
hb_vmPushDynSym( pSymbol );
hb_vmPushNil();
hb_vmDo( 0 );
hb_objSendMsg( hb_stackReturnItem(), "NEW", 0 );
hb_itemMove( pNewItem, hb_stackReturnItem() );
hb_itemMove( hb_stackReturnItem(), pRetCopy );
hb_itemRelease( pRetCopy );
if( pNewItem )
{
if( ! HB_IS_OBJECT( pNewItem ) )
{
hb_itemRelease( pNewItem );
pNewItem = NULL;
}
}
return pNewItem;
}
return NULL;
}
static const char * readByte( amfContext * context )
{
HB_ISIZ new_position = context->position + 1;
const char * byte_ref;
if( new_position < 0 || new_position > context->length )
return NULL;
byte_ref = context->cBuf + context->position;
context->position = new_position;
return byte_ref;
}
static const char * readBytes( amfContext * context, HB_ISIZ len )
{
HB_ISIZ new_position = context->position + len;
const char * result;
if( new_position < 0 || new_position > context->length )
return NULL;
result = context->cBuf + context->position;
context->position = new_position;
return result;
}
static HB_BOOL amfX_decode_double( amfContext * context, double * val )
{
#ifndef HB_BIG_ENDIAN
char c_val[ 8 ];
#endif
const char * bytes = readBytes( context, 8 );
if( ! bytes )
return HB_FALSE;
/*
* Put bytes from byte array into double
* FIXME: does this alignment work on any platform?
union aligned
{
double d_val;
char c_val[ 8 ];
} d;
*/
/* AMF transmitted double is always in network byte order */
#ifdef HB_BIG_ENDIAN
memcpy( val, bytes, 8 );
#else
/* Flip endianness */
c_val[ 0 ] = bytes[ 7 ];
c_val[ 1 ] = bytes[ 6 ];
c_val[ 2 ] = bytes[ 5 ];
c_val[ 3 ] = bytes[ 4 ];
c_val[ 4 ] = bytes[ 3 ];
c_val[ 5 ] = bytes[ 2 ];
c_val[ 6 ] = bytes[ 1 ];
c_val[ 7 ] = bytes[ 0 ];
memcpy( val, c_val, 8 );
#endif
return HB_TRUE;
}
static HB_BOOL amf3_decode_int( amfContext * context, int * iVal )
{
const char * byte_ref;
char byte;
int result = 0;
int byte_cnt = 0;
byte_ref = readByte( context );
if( ! byte_ref )
return HB_FALSE;
byte = byte_ref[ 0 ];
/* If 0x80 is set, int includes the next byte, up to 4 total bytes */
while( ( byte & 0x80 ) && ( byte_cnt < 3 ) )
{
result <<= 7;
result |= byte & 0x7F;
byte_ref = readByte( context );
if( ! byte_ref )
return HB_FALSE;
byte = byte_ref[ 0 ];
byte_cnt++;
}
/* shift bits in last byte */
if( byte_cnt < 3 )
{
result <<= 7; /* shift by 7, since the 1st bit is reserved for next byte flag */
result |= byte & 0x7F;
}
else
{
result <<= 8; /* shift by 8, since no further bytes are possible and 1st bit is not used for flag. */
result |= byte & 0xff;
}
/* Move sign bit, since we're converting 29-bit -> 32-bit */
if( result & 0x10000000 )
result -= 0x20000000;
*iVal = result;
return HB_TRUE;
}
#if 0
static HB_BOOL amf3_decode_reference( PHB_ITEM pHash, int val, PHB_ITEM pRefItem )
{
/* Check for index reference */
if( ( val & REFERENCE_BIT ) == 0 )
{
PHB_ITEM pKey = hb_itemNew( NULL );
hb_itemPutNI( pKey, val >> 1 );
pRefItem = hb_hashGetItemPtr( pHash, pKey, 0 );
hb_itemRelease( pKey );
return HB_TRUE;
}
return HB_FALSE;
}
#endif
static PHB_ITEM amf3_decode_reference( PHB_ITEM pHash, int val )
{
/* Check for index reference */
if( ( val & REFERENCE_BIT ) == 0 )
{
PHB_ITEM pKey = hb_itemNew( NULL );
PHB_ITEM pRefItem;
hb_itemPutNI( pKey, val >> 1 );
pRefItem = hb_hashGetItemPtr( pHash, pKey, 0 );
if( ! pRefItem )
hb_itemPutL( pRefItem, HB_FALSE );
hb_itemRelease( pKey );
return pRefItem;
}
return NULL;
}
static void amf3_add_reference( PHB_ITEM pHash, PHB_ITEM pItem )
{
HB_SIZE iRef = hb_hashLen( pHash );
PHB_ITEM pKey = hb_itemNew( NULL );
/* the reference id in AMF starts from 0, and increase
sequentially - this means we can also use an array,
not hash for the purpose */
hb_itemPutNS( pKey, /* ++ first one was 0 */ iRef );
hb_hashAdd( pHash, pKey, pItem );
hb_itemRelease( pKey );
}
static HB_BOOL amfX_decode_string( amfContext * context, PHB_ITEM pItem, unsigned int string_size )
{
const char * str = readBytes( context, string_size );
if( ! str )
return HB_FALSE;
hb_itemPutStrLenUTF8( pItem, str, string_size );
return HB_TRUE;
}
static HB_BOOL amf3_deserialize_string( amfContext * context, PHB_ITEM pItem )
{
int header;
int * header_p = &header;
PHB_ITEM pRefItem;
PHB_ITEM pHash = context->str_ref;
if( ! amf3_decode_int( context, header_p ) )
return HB_FALSE;
/* Check for null string */
if( header == EMPTY_STRING_TYPE )
{
hb_itemPutC( pItem, NULL );
return HB_TRUE;
}
/* Check for reference */
pRefItem = amf3_decode_reference( pHash, header );
if( pRefItem )
{
if( HB_IS_LOGICAL( pRefItem ) )
{
/* Logical value means a problem getting reference from the hash */
hb_itemRelease( pRefItem );
return HB_FALSE;
}
/* Copies string from reference hash pRefItem -> pItem */
hb_itemCopy( pItem, pRefItem );
return HB_TRUE;
}
/* No reference found */
if( ! amfX_decode_string( context, pItem, header >> 1 ) )
return HB_FALSE;
/* Adds reference */
amf3_add_reference( pHash, pItem );
return HB_TRUE;
}
/* Add the dynamic attributes of an encoded obj to a dict. */
static HB_BOOL amf3_decode_dynamic_dict( amfContext * context, PHB_ITEM pItem )
{
for( ;; )
{
PHB_ITEM pKey;
PHB_ITEM pValue;
HB_BOOL result;
pKey = hb_itemNew( NULL );
if( ! amf3_deserialize_string( context, pKey ) )
{
hb_itemRelease( pKey );
return HB_FALSE;
}
if( hb_itemGetCLen( pKey ) == 0 )
{
/* Empty string marks end of name/value pairs */
hb_itemRelease( pKey );
return HB_TRUE;
}
pValue = hb_itemNew( NULL );
if( ! amf3_getItem( context, pValue ) )
{
hb_itemRelease( pKey );
hb_itemRelease( pValue );
return HB_FALSE;
}
result = hb_hashAdd( pItem, pKey, pValue );
hb_itemRelease( pKey );
hb_itemRelease( pValue );
if( ! result )
return HB_FALSE;
}
}
/* Populate an array with values from the buffer. */
static HB_BOOL decode_dynamic_array_AMF3( amfContext * context, PHB_ITEM pItem, int array_len, HB_BOOL dict )
{
int i;
HB_BOOL lRet;
if( dict )
{
/* Object is a dict, set item index as key. */
for( i = 0; i < array_len; i++ )
{
PHB_ITEM pValue = hb_itemNew( NULL );
lRet = HB_FALSE;
if( amf3_getItem( context, pValue ) )
{
PHB_ITEM pKey = hb_itemNew( NULL );
hb_itemPutNI( pKey, i );
if( hb_hashAdd( pItem, pKey, pValue ) )
lRet = HB_TRUE;
hb_itemRelease( pKey );
}
hb_itemRelease( pValue );
if( ! lRet )
return HB_FALSE;
}
}
else
{
/* Standard array. */
for( i = 0; i < array_len; i++ )
{
PHB_ITEM pValue = hb_itemNew( NULL );
lRet = HB_FALSE;
if( amf3_getItem( context, pValue ) )
{
if( hb_arraySet( pItem, i + 1, pValue ) )
lRet = HB_TRUE;
}
hb_itemRelease( pValue );
if( ! lRet )
return HB_FALSE;
}
}
return HB_TRUE;
}
static HB_BOOL amf3_deserialize_array( amfContext * context, PHB_ITEM pItem, HB_BOOL collection )
{
int header;
int * header_p = &header;
PHB_ITEM pRefItem;
PHB_ITEM pHash = context->obj_ref;
int array_len;
HB_BOOL mixed; /* if the result will be a Hash with both numbers and strings as keys */
const char * byte_ref;
if( ! amf3_decode_int( context, header_p ) )
return HB_FALSE;
/* Check for reference */
pRefItem = amf3_decode_reference( pHash, header );
if( pRefItem )
{
if( HB_IS_LOGICAL( pRefItem ) )
{
/* Logical value means a problem getting reference from the hash */
hb_itemRelease( pRefItem );
return HB_FALSE;
}
/* Reference found */
if( collection )
{
/* Map ArrayCollection idx to ref, since
it points to the same list. */
amf3_add_reference( pHash, pRefItem );
}
/* Copies string from reference hash pRefItem -> pItem */
hb_itemCopy( pItem, pRefItem );
return HB_TRUE;
}
array_len = ( int ) ( header >> 1 );
/* Original Python comment was:
Cannot use array_len to create a list of known
length, see ticket #46
I think that this is not a problem for Harbour */
/* Determine if array is mixed (associative) or not */
mixed = HB_FALSE;
byte_ref = readByte( context );
if( byte_ref == NULL )
return HB_FALSE;
if( byte_ref[ 0 ] == EMPTY_STRING_TYPE )
{
/* Dense array */
hb_arrayNew( pItem, array_len );
}
else
{
context->position--;
hb_hashNew( pItem );
hb_hashPreallocate( pItem, array_len );
if( ! amf3_decode_dynamic_dict( context, pItem ) )
return HB_FALSE;
mixed = HB_TRUE;
}
amf3_add_reference( pHash, pItem );
/* Originally a python comment.
I don't understand reasons for following,
but let it be like this.
ADD: oh, maybe it's because parsing of
ArrayCollection starts as Object
--
If this is an ArrayCollection,
we need to add another reference,
so there is one that
points to the array and one that points
to the collection.
*/
if( collection )
amf3_add_reference( pHash, pItem );
return decode_dynamic_array_AMF3( context, pItem, array_len, mixed );
#if 0
return HB_TRUE;
#endif
}
/* Decode a date. */
static HB_BOOL amf3_decode_epoch( amfContext * context, PHB_ITEM pItem )
{
double epoch_millisecs;
if( ! amfX_decode_double( context, &epoch_millisecs ) )
return HB_FALSE;
hb_itemPutTD( pItem, ( epoch_millisecs + 210866803200000.4 ) / HB_MILLISECS_PER_DAY );
return HB_TRUE;
}
/* Deserialize date. */
static HB_BOOL amf3_deserialize_date( amfContext * context, PHB_ITEM pItem )
{
int header;
int * header_p = &header;
PHB_ITEM pRefItem;
PHB_ITEM pHash = context->obj_ref;
if( ! amf3_decode_int( context, header_p ) )
return HB_FALSE;
/* Check for reference */
pRefItem = amf3_decode_reference( pHash, header );
if( pRefItem )
{
if( HB_IS_LOGICAL( pRefItem ) )
{
/* Logical value means a problem getting reference from the hash */
hb_itemRelease( pRefItem );
return HB_FALSE;
}
/* Copies date from reference hash pRefItem -> pItem */
hb_itemCopy( pItem, pRefItem );
return HB_TRUE;
}
if( ! amf3_decode_epoch( context, pItem ) )
return HB_FALSE;
/* Add reference */
amf3_add_reference( pHash, pItem );
return HB_TRUE;
}
/* Decode a byte array. */
static HB_BOOL amf3_decode_byte_array( amfContext * context, PHB_ITEM pItem, int byte_len )
{
const char * str = readBytes( context, byte_len );
if( ! str )
return HB_FALSE;
hb_itemPutStrLen( pItem, hb_vmCDP(), str, byte_len );
return HB_TRUE;
}
/* Deserialize a byte array. */
static HB_BOOL amf3_deserialize_byte_array( amfContext * context, PHB_ITEM pItem )
{
int header;
int * header_p = &header;
PHB_ITEM pRefItem;
PHB_ITEM pHash = context->obj_ref;
if( ! amf3_decode_int( context, header_p ) )
return HB_FALSE;
/* Check for reference */
pRefItem = amf3_decode_reference( pHash, header );
if( pRefItem )
{
if( HB_IS_LOGICAL( pRefItem ) )
{
/* Logical value means a problem getting reference from the hash */
hb_itemRelease( pRefItem );
return HB_FALSE;
}
/* Copies ByteArray (string) from reference hash pRefItem -> pItem */
hb_itemCopy( pItem, pRefItem );
return HB_TRUE;
}
if( ! amf3_decode_byte_array( context, pItem, header >> 1 ) )
return HB_FALSE;
/* Add reference */
amf3_add_reference( pHash, pItem );
return HB_TRUE;
}
/* Get an object's class def - nearly a copy/paste from decoder */
static PHB_ITEM class_def_from_classname( /* amfContext * context, */ PHB_ITEM pClassName )
{
HB_USHORT uiClass;
PHB_ITEM pClass;
PHB_ITEM pKey;
PHB_ITEM pValue;
char * pszBuffer = hb_itemGetC( pClassName );
HB_SIZE nLen = hb_itemGetCLen( pClassName );
hb_strUpper( pszBuffer, nLen );
/* get Harbour's class id/handle */
uiClass = hb_clsFindClass( pszBuffer, NULL );
hb_strfree( pszBuffer );
#if 0
uiClass = hb_objGetClass( pItem );
#endif
if( ! uiClass )
return NULL;
pClass = hb_hashNew( NULL );
pKey = hb_itemPutC( NULL, "CLASS_DEF" );
pValue = hb_itemNew( NULL );
if( ! hb_hashAdd( pClass, pKey, pValue ) )
{
hb_itemRelease( pKey );
hb_itemRelease( pValue );
hb_itemRelease( pClass );
return NULL;
}
hb_itemRelease( pKey );
hb_itemRelease( pValue );
pKey = hb_itemPutC( NULL, "alias" );
pValue = hb_itemPutC( NULL, hb_clsName( uiClass ) );
if( ! hb_hashAdd( pClass, pKey, pValue ) )
{
hb_itemRelease( pKey );
hb_itemRelease( pValue );
hb_itemRelease( pClass );
return NULL;
}
hb_itemRelease( pKey );
hb_itemRelease( pValue );
if( hbamf_is_cls_externalizable( uiClass ) )
{
pKey = hb_itemPutC( NULL, "EXTERNALIZABLE_CLASS_DEF" );
pValue = hb_itemNew( NULL );
if( ! hb_hashAdd( pClass, pKey, pValue ) )
{
hb_itemRelease( pKey );
hb_itemRelease( pValue );
hb_itemRelease( pClass );
return NULL;
}
hb_itemRelease( pKey );
hb_itemRelease( pValue );
}
return pClass;
}
/*
* Decode a ClassDef.
*
* Header argument is the obj header.
*/
static HB_BOOL amf3_decode_class_def( amfContext * context, PHB_ITEM pClass, int header )
{
PHB_ITEM pStrAlias = hb_itemNew( NULL );
PHB_ITEM pMappedClassDef = NULL;
PHB_ITEM pKey;
PHB_ITEM pValue;
PHB_ITEM pAttrs;
int static_attr_len;
int i;
if( ! amf3_deserialize_string( context, pStrAlias ) )
{
hb_itemRelease( pStrAlias );
return HB_FALSE;
}
/* If this alias string is empty then the object is anonymous */
if( hb_itemGetCLen( pStrAlias ) > 0 )
{
/* Retrieve a ClassDef from a class alias string. */
pMappedClassDef = class_def_from_classname( pStrAlias );
if( ! pMappedClassDef )
{
pMappedClassDef = hb_itemNew( NULL );
hb_hashNew( pMappedClassDef ); /* empty hash emulation for now */
}
/* PyObject_CallMethodObjArgs( context->class_mapper,
context->class_def_name, alias, NULL ); */
}
hb_itemRelease( pStrAlias );
/* Create a dict with class def information
specific to this decode context. */
hb_hashNew( pClass );
if( pMappedClassDef )
{
pKey = hb_itemPutC( NULL, "class_def" ); /* hb_itemNew( NULL ); */
if( ! hb_hashAdd( pClass, pKey, pMappedClassDef ) )
{
hb_itemRelease( pKey );
hb_itemRelease( pMappedClassDef );
return HB_FALSE;
}
hb_itemRelease( pKey );
pKey = hb_itemPutC( NULL, "EXTERNALIZABLE_CLASS_DEF" );
if( hb_hashScan( pMappedClassDef, pKey, NULL ) )
{
/* There is nothing else we need to do
with externalizable ClassDefs */
hb_itemRelease( pKey );
hb_itemRelease( pMappedClassDef );
return HB_TRUE;
}
hb_itemRelease( pKey );
/* this item should be now referenced in the hash */
hb_itemRelease( pMappedClassDef );
}
if( ( header & 0x07FFFFFF ) == EXTERNALIZABLE )
{
/* If the class is externalizable, but the ClassDef isn't,
we have a big problem, because we don't know how to read
the raw bytes. */
/* TODO: introduce similar RTE?
PyErr_SetString( amfast_DecodeError, "Encoded class is externalizable, but ClassDef is not." ); */
return HB_FALSE;
}
/* Set dynamic flag */
pKey = hb_itemPutC( NULL, "dynamic" );
pValue = hb_itemPutL( NULL, ( header & DYNAMIC ) == DYNAMIC );
if( ! hb_hashAdd( pClass, pKey, pValue ) )
{
hb_itemRelease( pKey );
hb_itemRelease( pValue );
return HB_FALSE;
}
hb_itemRelease( pKey );
hb_itemRelease( pValue );
/* Decode static attr names */
static_attr_len = ( int ) ( header >> 4 );
pAttrs = hb_itemNew( NULL );
hb_arrayNew( pAttrs, static_attr_len );
for( i = 0; i < static_attr_len; i++ )
{
pValue = hb_itemNew( NULL );
if( ! amf3_deserialize_string( context, pValue ) )
{
hb_itemRelease( pAttrs );
hb_itemRelease( pValue );
return HB_FALSE;
}
/* steals ref to attr_name */
if( ! hb_arraySet( pAttrs, i + 1, pValue ) )
{
hb_itemRelease( pAttrs );
hb_itemRelease( pValue );
return HB_FALSE;
}
hb_itemRelease( pValue );
}
/* Set decoded attrs onto ClassDef */
pKey = hb_itemPutC( NULL, "static_attrs" );
if( ! hb_hashAdd( pClass, pKey, pAttrs ) )
{
hb_itemRelease( pKey );
hb_itemRelease( pAttrs );
return HB_FALSE;
}
hb_itemRelease( pKey );
hb_itemRelease( pAttrs );
return HB_TRUE;
}
/*
* Deserialize a ClassDef.
*
* header argument is the parsed obj header.
*/
static HB_BOOL amf3_deserialize_class_def( amfContext * context, PHB_ITEM pClass, int header )
{
PHB_ITEM pHash = context->class_ref;
PHB_ITEM pRefItem;
/* Check for reference */
pRefItem = amf3_decode_reference( pHash, header );
if( pRefItem )
{
if( HB_IS_LOGICAL( pRefItem ) )
{
/* Logical value means a problem getting reference from the hash */
hb_itemRelease( pRefItem );
return HB_FALSE;
}
/* Copies ClassDef from reference hash pRefItem -> pClass */
hb_itemCopy( pClass, pRefItem );
return HB_TRUE;
}
if( ! amf3_decode_class_def( context, pClass, header ) )
return HB_FALSE;
/* Add reference to obj */
amf3_add_reference( pHash, pClass );
return HB_TRUE;
}
/* Returns a dict with values from an object. */
static HB_BOOL amf3_decode_obj_attrs( amfContext * context, PHB_ITEM pHash, PHB_ITEM pClass )
{
PHB_ITEM pArray;
PHB_ITEM pValue;
HB_SIZE static_attr_len;
HB_SIZE i;
/* Put decoded attributes into pHash */
/* Decode static attrs */
pArray = hb_hashGetCItemPtr( pClass, "static_attrs" );
if( ! pArray )
return HB_FALSE;
/* maybe hb_arrayGetItemPtr() could be used? */
static_attr_len = hb_arrayLen( pArray );
for( i = 0; i < static_attr_len; i++ )
{
PHB_ITEM pKey;
HB_BOOL result;
pValue = hb_itemNew( NULL );
if( ! amf3_getItem( context, pValue ) )
{
hb_itemRelease( pValue );
return HB_FALSE;
}
pKey = hb_itemNew( NULL );
if( ! hb_arrayGet( pArray, i + 1, pKey ) )
{
hb_itemRelease( pValue );
hb_itemRelease( pKey );
return HB_FALSE;
}
result = hb_hashAdd( pHash, pKey, pValue );
hb_itemRelease( pValue );
hb_itemRelease( pKey );
if( ! result )
return HB_FALSE;
}
/* Decode dynamic attrs */
pValue = hb_hashGetCItemPtr( pClass, "dynamic" );
if( ! pValue )
return HB_FALSE;
if( HB_IS_LOGICAL( pValue ) && hb_itemGetL( pValue ) )
{
if( ! amf3_decode_dynamic_dict( context, pHash ) )
return HB_FALSE;
}
return HB_TRUE;
}
/* Decode an anonymous obj. */
static HB_BOOL amf3_decode_anon_obj( amfContext * context, PHB_ITEM pItem, PHB_ITEM pClass )
{
PHB_ITEM pAnonHash = hb_itemNew( NULL );
HB_BOOL result = HB_FALSE;
/* Original Python comment which I don't understand:
We're using merge instead of populating the dict
directly, because we have to setup a reference to the
object before decoding it. ?????? */
/* we (Harbourers) are supplying already initialized hash to next function */
if( hb_arrayGet( pItem, OBJAMF_VAR_HASH, pAnonHash ) )
result = amf3_decode_obj_attrs( context, pAnonHash, pClass );
hb_itemRelease( pAnonHash );
return result;
}
static HB_BOOL amf3_decode_externalizable( amfContext * context, PHB_ITEM pItem )
{
const char * position;
PHB_ITEM pRetCopy = hb_itemNew( NULL );
PHB_ITEM pStr, pPos;
HB_BOOL result = HB_TRUE;
PHB_ITEM pObject;
if( pItem == hb_stackReturnItem() )
pObject = pRetCopy;
else
pObject = pItem;
hb_itemMove( pRetCopy, hb_stackReturnItem() );
position = context->cBuf + context->position;
pStr = hb_itemNew( NULL );
pStr = hb_itemPutCLConst( pStr, position, context->length - context->position );
pPos = hb_objSendMsg( pObject, "READEXTERNAL", 1, pStr );
if( HB_IS_INTEGER( pPos ) )
{
if( ! readBytes( context, hb_itemGetNI( pPos ) ) )
result = HB_FALSE;
}
else
result = HB_FALSE;
hb_itemMove( hb_stackReturnItem(), pRetCopy );
hb_itemRelease( pRetCopy );
hb_itemRelease( pStr );
return result;
}
/*
* Deserialize an obj.
*
* proxy is flag indicating that the obj being deserialized is within an ObjectProxy
*/
static HB_BOOL amf3_deserialize_obj( amfContext * context, PHB_ITEM pItem, HB_BOOL proxy )
{
int header;
int * header_p = &header;
PHB_ITEM pRefItem;
PHB_ITEM pHash = context->obj_ref;
PHB_ITEM pClass;
PHB_ITEM pMappedClassDef;
PHB_ITEM pValue;
int obj_type; /* 0 = anonymous, 1 == externalizable, 2 == typed */
HB_BOOL result;
if( ! amf3_decode_int( context, header_p ) )
return HB_FALSE;
/* Check for reference */
pRefItem = amf3_decode_reference( pHash, header );
if( pRefItem )
{
if( HB_IS_LOGICAL( pRefItem ) )
{
/* Logical value means a problem getting reference from the hash */
hb_itemRelease( pRefItem );
return HB_FALSE;
}
/* Reference found */
if( proxy )
{
/* Map ObjectProxy idx to ref, since
it points to the same obj. */
amf3_add_reference( pHash, pRefItem );
}
/* Copies ByteArray (string) from reference hash pRefItem -> pItem */
hb_itemCopy( pItem, pRefItem );
return HB_TRUE;
}
pClass = hb_itemNew( NULL );
if( ! amf3_deserialize_class_def( context, pClass, header ) )
{
hb_itemRelease( pClass );
return HB_FALSE;
}
pMappedClassDef = hb_hashGetCItemPtr( pClass, "class_def" );
if( ! pMappedClassDef )
{
/* Anonymous obj. */
obj_type = 0;
}
else if( hb_hashGetCItemPos( pMappedClassDef, "EXTERNALIZABLE_CLASS_DEF" ) != 0 )
{
if( hb_hashGetCItemPos( pMappedClassDef, "ARRAY_COLLECTION_CLASS_DEF" ) != 0 )
{
hb_itemRelease( pClass );
if( ! readByte( context ) ) /* Skip array type marker */
return HB_FALSE;
return amf3_deserialize_array( context, pItem, HB_TRUE );
}
if( hb_hashGetCItemPos( pMappedClassDef, "OBJECT_PROXY_CLASS_DEF" ) != 0 )
{
hb_itemRelease( pClass );
if( ! readByte( context ) ) /* Skip array type marker */
return HB_FALSE;
return amf3_deserialize_obj( context, pItem, HB_TRUE );
}
obj_type = 1;
}
else
obj_type = 2;
/* Instantiate new obj */
if( obj_type == 0 )
{
/* Anonymous obj == OBJAMF */
hb_arrayNew( pItem, OBJAMF_VAR_COUNT );
/* performance FIXME, cache class id (in context maybe)
to not scan all classes by name every time */
hb_objSetClass( pItem, "AMF_OBJ", "AMF_OBJ" );
pValue = hb_itemPutNI( NULL, OBJAMF_VER );
hb_arraySet( pItem, OBJAMF_VAR_VER, pValue );
hb_itemRelease( pValue );
pValue = hb_itemPutC( NULL, "ANONYMOUS" );
hb_arraySet( pItem, OBJAMF_VAR_NAME, pValue );
hb_itemRelease( pValue );
pValue = hb_hashNew( NULL );
hb_arraySet( pItem, OBJAMF_VAR_HASH, pValue );
hb_itemRelease( pValue );
}
else if( obj_type == 1 )
{
/* externalizable object should allow a constructor without parameters,
and the object should not do anything dangerous, because it could
be instantiated on clients request */
pValue = hb_hashGetCItemPtr( pMappedClassDef, "alias" );
if( ! pValue )
{
hb_itemRelease( pClass );
return HB_FALSE;
}
pValue = hbamf_cls_externalizable_instance( pValue );
if( ! pValue )
{
hb_itemRelease( pClass );
return HB_FALSE;
}
hb_itemMove( pItem, pValue );
hb_itemRelease( pValue );
}
else
{
/* Create obj_val for all typed objects. */
#if 0
obj_val = PyObject_CallMethod( class_def, "getInstance", NULL );
#endif
}
if( ! HB_IS_OBJECT( pItem ) )
{
hb_itemRelease( pClass );
return HB_FALSE;
}
/* Reference must be added before children (to allow for recursion). */
amf3_add_reference( pHash, pItem );
if( proxy )
{
/* If this is an ObjectProxy,
we need to add another reference,
so there is one that
points to the obj and one that points
to the proxy. */
amf3_add_reference( pHash, pItem );
}
result = HB_FALSE;
if( obj_type == 0 )
{
result = amf3_decode_anon_obj( context, pItem, pClass );
}
else if( obj_type == 1 )
{
#if 0
result = HB_TRUE;
#endif
result = amf3_decode_externalizable( context, pItem /*, pMappedClassDef */ );
}
else if( obj_type == 2 )
{
#if 0
result = decode_typed_obj_AMF3( context, obj_val, class_def_dict );
#endif
}
hb_itemRelease( pClass );
return result;
}
static void amf3_conversion_in( amfContext * context, PHB_ITEM pItem )
{
PHB_ITEM pRetCopy = hb_itemNew( NULL );
PHB_SYMB pSym = hb_itemGetSymbol( context->conv_function );
if( pItem == hb_stackReturnItem() )
{
hb_vmPushSymbol( pSym );
hb_vmPushNil();
hb_vmPush( pItem );
hb_vmDo( 1 );
}
else
{
hb_itemMove( pRetCopy, hb_stackReturnItem() );
hb_vmPushSymbol( pSym );
hb_vmPushNil();
hb_vmPush( pItem );
hb_vmDo( 1 );
hb_itemMove( pItem, hb_stackReturnItem() );
hb_itemMove( hb_stackReturnItem(), pRetCopy );
}
hb_itemRelease( pRetCopy );
}
/* much of deserialize_* functions are so much similar that we may
generalize them in one, f.e. adding another parameter specifying
pointer of a final decoding function... in case reference checking
returns nothing */
static HB_BOOL amf3_getItem( amfContext * context, PHB_ITEM pItem )
{
char byte;
const char * byte_ref;
HB_BOOL lRet = HB_TRUE;
byte_ref = readByte( context );
if( ! byte_ref )
return HB_FALSE;
byte = byte_ref[ 0 ];
switch( byte )
{
case UNDEFINED_TYPE:
case NULL_TYPE:
lRet = HB_TRUE;
break;
case FALSE_TYPE:
hb_itemPutL( pItem, HB_FALSE );
lRet = HB_TRUE;
break;
case TRUE_TYPE:
hb_itemPutL( pItem, HB_TRUE );
lRet = HB_TRUE;
break;
case INT_TYPE:
{
int iVal;
if( amf3_decode_int( context, &iVal ) )
hb_itemPutNI( pItem, iVal );
else
lRet = HB_FALSE;
}
break;
case DOUBLE_TYPE:
{
double dVal;
if( amfX_decode_double( context, &dVal ) )
hb_itemPutND( pItem, dVal );
else
lRet = HB_FALSE;
}
break;
case STRING_TYPE:
lRet = amf3_deserialize_string( context, pItem );
break;
case XML_DOC_TYPE:
/* don't touch xml encoding right now */
lRet = amf3_deserialize_byte_array( context, pItem );
break;
case DATE_TYPE:
lRet = amf3_deserialize_date( context, pItem );
break;
case ARRAY_TYPE:
lRet = amf3_deserialize_array( context, pItem, HB_FALSE );
break;
case OBJECT_TYPE:
lRet = amf3_deserialize_obj( context, pItem, HB_FALSE );
break;
case XML_TYPE:
/* don't touch xml encoding right now */
lRet = amf3_deserialize_byte_array( context, pItem );
break;
case BYTE_ARRAY_TYPE:
lRet = amf3_deserialize_byte_array( context, pItem );
break;
case AMF3_AMF0:
lRet = amf3_getItem( context, pItem );
break;
default:
lRet = HB_FALSE;
break;
}
if( context->conv_function )
amf3_conversion_in( context, pItem );
return lRet;
}
HB_FUNC( AMF3_DECODE )
{
PHB_ITEM pItem = hb_stackReturnItem();
#if defined( _DEBUG )
PHB_ITEM pDebugBlock = hb_param( 2, HB_IT_BLOCK );
#endif
PHB_ITEM pFuncSym = hb_param( 2, HB_IT_SYMBOL );
amfContext * context;
const char * szBuffer = hb_parc( 1 );
if( ! szBuffer )
return;
context = ( amfContext * ) hb_xgrab( sizeof( amfContext ) );
memset( context, 0, sizeof( amfContext ) );
context->cBuf = szBuffer;
context->position = 0;
context->length = hb_parclen( 1 );
context->obj_ref = hb_hashNew( NULL );
context->str_ref = hb_hashNew( NULL );
context->class_ref = hb_hashNew( NULL );
context->conv_function = pFuncSym;
amf3_getItem( context, pItem );
#if defined( _DEBUG )
if( pDebugBlock )
{
hb_vmPushEvalSym();
hb_vmPush( pDebugBlock );
hb_vmPush( context->obj_ref );
hb_vmPush( context->str_ref );
hb_vmPush( context->class_ref );
hb_vmSend( ( HB_USHORT ) 3 );
}
#endif
hb_itemRelease( context->obj_ref );
hb_itemRelease( context->str_ref );
hb_itemRelease( context->class_ref );
#if 0
if( context->conv_function )
hb_itemRelease( context->conv_function );
#endif
hb_xfree( context );
}