+ contrib/hbamf/hbamfobj.prg
* contrib/hbamf/amfdec.c
* contrib/hbamf/amfenc.c
* contrib/hbamf/hbamf.hbp
* contrib/hbamf/hbamf.hbx
+ added missing parts as per Aleksander's instructions.
with some renames/formatting. OBJAMF renamed to AMF_OBJ,
please review and test.
https://groups.google.com/d/msg/harbour-devel/EPdeo6zbFt8/FTd7mkyTPawJ
* utils/hbmk2/hbmk2.prg
* minor formatting
1285 lines
32 KiB
C
1285 lines
32 KiB
C
/*
|
|
* $Id$
|
|
*/
|
|
|
|
/*******
|
|
*
|
|
* 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;
|
|
int position;
|
|
int 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 char * readByte( amfContext * context )
|
|
{
|
|
int new_position = context->position + 1;
|
|
char * byte_ref;
|
|
|
|
if( new_position < 0 || new_position > context->length )
|
|
return NULL;
|
|
|
|
byte_ref = ( char * ) ( context->cBuf + context->position );
|
|
context->position = new_position;
|
|
return byte_ref;
|
|
}
|
|
|
|
static char * readBytes( amfContext * context, int len )
|
|
{
|
|
int new_position = context->position + len;
|
|
char * result;
|
|
|
|
if( new_position < 0 || new_position > context->length )
|
|
return NULL;
|
|
|
|
result = ( char * ) ( 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
|
|
* TOFIX: does this aligment 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 )
|
|
{
|
|
int result = 0;
|
|
int byte_cnt = 0;
|
|
char * byte_ref;
|
|
char byte;
|
|
|
|
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 29bit->32bit */
|
|
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_itemPutNI( 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 )
|
|
{
|
|
PHB_ITEM pKey;
|
|
PHB_ITEM pValue;
|
|
HB_BOOL result;
|
|
|
|
while( 1 )
|
|
{
|
|
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 vals 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 */
|
|
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:
|
|
Can't 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_hashSetFlags( pHash, HB_HASH_KEEPORDER );
|
|
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 );
|
|
|
|
/* return HB_TRUE; */
|
|
}
|
|
|
|
/* Decode a date. */
|
|
static HB_BOOL amf3_decode_epoch( amfContext * context, PHB_ITEM pItem )
|
|
{
|
|
double dJulian, dTime;
|
|
double epoch_millisecs;
|
|
double * epoch_p = &epoch_millisecs;
|
|
|
|
if( ! amfX_decode_double( context, epoch_p ) )
|
|
return HB_FALSE;
|
|
|
|
/* 210866760000000 unix_epoch milliseconds base in JD */
|
|
dTime = modf( ( 210866760000000 + epoch_millisecs + 0.5 ) / HB_MILLISECS_PER_DAY, &dJulian );
|
|
|
|
hb_itemPutTDT( pItem, ( long ) dJulian, ( long ) ( dTime * HB_MILLISECS_PER_DAY ) );
|
|
|
|
/* TOFIX: why following Harbour function doesn't work out of the box?
|
|
C compiler was MSVC 7.1 (Visual Studio 2003)
|
|
hb_itemPutTD( pItem, epoch_millisecs + 210866803200000 );
|
|
*/
|
|
|
|
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 );
|
|
|
|
/* uiClass = hb_objGetClass( pItem ); */
|
|
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 vals from an obj. */
|
|
static HB_BOOL amf3_decode_obj_attrs( amfContext * context, PHB_ITEM pHash, PHB_ITEM pClass )
|
|
{
|
|
PHB_ITEM pArray;
|
|
PHB_ITEM pKey;
|
|
PHB_ITEM pValue;
|
|
HB_BOOL result;
|
|
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++ )
|
|
{
|
|
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 func */
|
|
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 TOFIX, cache class id (in context maybe)
|
|
to not scan all classes by name everytime */
|
|
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 objs. */
|
|
/* obj_val = PyObject_CallMethod(class_def, "getInstance", NULL); */
|
|
}
|
|
|
|
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 )
|
|
{
|
|
/* result = HB_TRUE; */
|
|
result = amf3_decode_externalizable( context, pItem /*, pMappedClassDef */ );
|
|
}
|
|
else if( obj_type == 2 )
|
|
{
|
|
/* result = decode_typed_obj_AMF3(context, obj_val, class_def_dict); */
|
|
}
|
|
|
|
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(context->conv_function)
|
|
hb_itemRelease(context->conv_function);*/
|
|
|
|
hb_xfree( context );
|
|
}
|