/************************************************************************/
/* SISCO SOFTWARE MODULE HEADER *****************************************/
/************************************************************************/
/*   (c) Copyright Systems Integration Specialists Company, Inc.,	*/
/*      	  1986 - 2001, All Rights Reserved.		        */
/*									*/
/*		    PROPRIETARY AND CONFIDENTIAL			*/
/*									*/
/* MODULE NAME : ms_ext.c						*/
/* PRODUCT(S)  : MMSEASE						*/
/*									*/
/* MODULE DESCRIPTION : 						*/
/*									*/
/* GLOBAL FUNCTIONS DEFINED IN THIS MODULE :				*/
/*									*/
/* MODIFICATION LOG :							*/
/*  Date     Who   Rev			Comments			*/
/* --------  ---  ------   -------------------------------------------	*/
/* 03/11/04  GLB     04    Cleaned up "thisFileName"                    */
/* 03/31/03  JRB     03    asn1r_get_bitstr: add max_bits arg.		*/
/* 12/20/01  JRB     02    Converted to use ASN1R (re-entrant ASN1)	*/
/* 09/13/99  MDE     01    Added SD_CONST modifiers			*/
/* 04/02/97  DTL   7.00    MMSEASE 7.0 release. See MODL70.DOC for	*/
/*			   history.					*/
/************************************************************************/

#include "glbtypes.h"
#include "sysincs.h"
#include "glbsem.h"

#include "mmsdefs.h"
#include "asn1defs.h"

/************************************************************************/
/* For debug version, use a static pointer to avoid duplication of 	*/
/* __FILE__ strings.							*/

#if defined(DEBUG_SISCO) && defined(S_MT_SUPPORT)
SD_CONST static ST_CHAR *SD_CONST thisFileName = __FILE__;
#endif

/************************************************************************/
/************************************************************************/

ST_VOID ms_get_extern (ASN1_DEC_CTXT *aCtx, EXTERN_INFO *dest);

static EXTERN_INFO *ext_dest;

static ST_VOID ext_cstr (ASN1_DEC_CTXT *aCtx);
static ST_VOID ext_cstr_done (ASN1_DEC_CTXT *aCtx);

static ST_INT save_method; 	/* storage for decode method		*/

/************************************************************************/
/*			ms_decode_extern				*/
/************************************************************************/

ST_RET ms_decode_extern (ST_UCHAR *asn1ptr, ST_INT asn1len, 
			   EXTERN_INFO *dest)
  {
ST_RET ret;
ASN1_DEC_CTXT localDecCtx;	/* For readability, use "aCtx" to access this.*/
ASN1_DEC_CTXT *aCtx = &localDecCtx;

  memset (aCtx, 0, sizeof (ASN1_DEC_CTXT));	/* CRITICAL: start clean.	*/
  S_LOCK_COMMON_RESOURCES ();
  aCtx->asn1r_decode_method = ASN1_TAG_METHOD;		/* select tag method			*/
  aCtx->asn1r_decode_done_fun = asn1r_done_err;/* not legal to be done at this time	*/
  aCtx->asn1r_err_fun = NULL;

  ext_dest = dest;

  ASN1R_TAG_ADD (aCtx, UNI|CONSTR, EXTERN_CODE, ext_cstr);
  asn1r_decode_asn1 (aCtx, asn1ptr,asn1len);/* decode entire ASN.1 'Data' entity    */

  if (aCtx->asn1r_pdu_dec_err == NO_DECODE_ERR)	/* check for sucess or return error	*/
    ret = SD_SUCCESS;
  else
    ret = MVE_DATA_CONVERT;

  S_UNLOCK_COMMON_RESOURCES ();
  return (ret);
  }

/************************************************************************/
/*			   ext_cstr					*/
/* This function is called when an 'EXTERNAL' data element has been	*/
/* found. It will put info into the selected structure.			*/
/************************************************************************/

static ST_VOID ext_cstr (ASN1_DEC_CTXT *aCtx)
  {
  ms_get_extern (aCtx, ext_dest);
  aCtx->asn1r_c_done_fun[aCtx->asn1r_msg_level] = ext_cstr_done;
  }


static ST_VOID ext_cstr_done (ASN1_DEC_CTXT *aCtx)
  {
  aCtx->asn1r_decode_done = SD_TRUE;
  aCtx->asn1r_decode_done_fun = NULL;
  }


/************************************************************************/
/************************************************************************/
/************************************************************************/

static EXTERN_INFO *ext_info_ptr;
static ST_VOID get_dir_ref (ASN1_DEC_CTXT *aCtx);
static ST_VOID get_indir_ref (ASN1_DEC_CTXT *aCtx);
static ST_VOID get_objdesc (ASN1_DEC_CTXT *aCtx);
static ST_VOID get_sngl_asn1 (ASN1_DEC_CTXT *aCtx);
static ST_VOID sngl_parse_done (ASN1_DEC_CTXT *aCtx);
static ST_VOID sngl_done (ASN1_DEC_CTXT *aCtx);
static ST_VOID get_oct_alignd_cstr (ASN1_DEC_CTXT *aCtx);
static ST_VOID get_oct_alignd_prim (ASN1_DEC_CTXT *aCtx);
static ST_VOID oct_alignd_done (ASN1_DEC_CTXT *aCtx);
static ST_VOID get_arb_cstr (ASN1_DEC_CTXT *aCtx);
static ST_VOID get_arb_prim (ASN1_DEC_CTXT *aCtx);
static ST_VOID arb_done (ASN1_DEC_CTXT *aCtx);

/************************************************************************/
/************************************************************************/
/*			   ms_get_extern					*/
/* This function is called when an 'EXTERNAL' data element has been	*/
/* found. It will put info into the selected structure.			*/
/************************************************************************/

ST_VOID ms_get_extern (ASN1_DEC_CTXT *aCtx, EXTERN_INFO *dest)
  {
  save_method = aCtx->asn1r_decode_method;			/* use class method	*/
  aCtx->asn1r_decode_method = ASN1_TAG_METHOD;

  ext_info_ptr = dest;				/* save the destination	*/
  ext_info_ptr -> dir_ref_pres = SD_FALSE;         /* clear pres flag      */
  ext_info_ptr -> indir_ref_pres = SD_FALSE;       /* clear pres flag      */
  ext_info_ptr -> dv_descr_pres = SD_FALSE;	/* clear present flag   */

  ASN1R_TAG_ADD (aCtx, UNI,OBJ_ID_CODE,get_dir_ref);	/* transfer syntax name	*/
  ASN1R_TAG_ADD (aCtx, UNI,INT_CODE,get_indir_ref);		/* PCI			*/
  ASN1R_TAG_ADD (aCtx, UNI,OBJDSCR_CODE,get_objdesc);	/* object descripter    */
						/* Data Types		*/
  ASN1R_TAG_ADD (aCtx, CTX|CONSTR,0,get_sngl_asn1);		/* single asn1 type 	*/
  ASN1R_TAG_ADD (aCtx, CTX|CONSTR,1,get_oct_alignd_cstr);	/* oct alignd, cstr	*/
  ASN1R_TAG_ADD (aCtx, CTX,1,get_oct_alignd_prim);		/* oct alignd, prim	*/
  ASN1R_TAG_ADD (aCtx, CTX|CONSTR,2,get_arb_cstr);		/* arbitrary, cstr	*/
  ASN1R_TAG_ADD (aCtx, CTX,2,get_arb_prim);			/* arbitrary, prim	*/
  }

/************************************************************************/
/*			   get_dir_ref					*/
/* This is the function called when :					*/
/************************************************************************/

static ST_VOID get_dir_ref (ASN1_DEC_CTXT *aCtx)
  {
  MLOG_CDEC0 ("get_dir_ref");

  ext_info_ptr->dir_ref_pres = SD_TRUE;		/* mark present		*/

  if (asn1r_get_objid (aCtx, ext_info_ptr->dir_ref.comps,
		 &ext_info_ptr->dir_ref.num_comps))
    {
    asn1r_set_dec_err (aCtx, RESP_BAD_VALUE); 
    return;                          
    }

  ASN1R_TAG_ADD (aCtx, UNI,INT_CODE,get_indir_ref);		/* indir ref maybe	*/
  ASN1R_TAG_ADD (aCtx, UNI,OBJDSCR_CODE,get_objdesc);	/* object descripter    */
						/* Data Types		*/
  ASN1R_TAG_ADD (aCtx, CTX|CONSTR,0,get_sngl_asn1);		/* single asn1 type 	*/
  ASN1R_TAG_ADD (aCtx, CTX|CONSTR,1,get_oct_alignd_cstr);	/* oct alignd, cstr	*/
  ASN1R_TAG_ADD (aCtx, CTX,1,get_oct_alignd_prim);		/* oct alignd, prim	*/
  ASN1R_TAG_ADD (aCtx, CTX|CONSTR,2,get_arb_cstr);		/* arbitrary, cstr	*/
  ASN1R_TAG_ADD (aCtx, CTX,2,get_arb_prim);			/* arbitrary, prim	*/
  }

/************************************************************************/
/*			   get_indir_ref					*/
/* This is the function called when the PCI element of a PDV is found	*/
/************************************************************************/

static ST_VOID get_indir_ref (ASN1_DEC_CTXT *aCtx)
  {
  MLOG_CDEC0 ("get_indir_ref");

  if (asn1r_get_i32 (aCtx, &ext_info_ptr->indir_ref))   /* If out of range for us	*/
    asn1r_set_dec_err (aCtx, RESP_BAD_VALUE);
  else
    {
    ext_info_ptr->indir_ref_pres = SD_TRUE;
    ASN1R_TAG_ADD (aCtx, UNI,OBJDSCR_CODE,get_objdesc);	/* object descripter    */
						/* Data Types		*/
    ASN1R_TAG_ADD (aCtx, CTX|CONSTR,0,get_sngl_asn1);	/* single asn1 type 	*/
    ASN1R_TAG_ADD (aCtx, CTX|CONSTR,1,get_oct_alignd_cstr);	/* oct alignd, cstr	*/
    ASN1R_TAG_ADD (aCtx, CTX,1,get_oct_alignd_prim);	/* oct alignd, prim	*/
    ASN1R_TAG_ADD (aCtx, CTX|CONSTR,2,get_arb_cstr);	/* arbitrary, cstr	*/
    ASN1R_TAG_ADD (aCtx, CTX,2,get_arb_prim);		/* arbitrary, prim	*/
    }
  }

/************************************************************************/
/*			   get_objdesc					*/
/* This is the function called when the PCI element of a PDV is found	*/
/************************************************************************/

static ST_VOID get_objdesc (ASN1_DEC_CTXT *aCtx)
  {
  MLOG_CDEC0 ("get_objdesc");

  ext_info_ptr->dv_descr_pres = SD_TRUE;		/* mark present		*/
  ext_info_ptr->dv_descr_len = aCtx->asn1r_elmnt_len;	/* len of string	*/
  ext_info_ptr->dv_descr = aCtx->asn1r_field_ptr;		/* leave it lie		*/
  aCtx->asn1r_field_ptr += aCtx->asn1r_elmnt_len;			/* skip over		*/

/* set up to get presentation data values - choice of three		*/

  ASN1R_TAG_ADD (aCtx, CTX|CONSTR,0,get_sngl_asn1);	/* single asn1 type 	*/
  ASN1R_TAG_ADD (aCtx, CTX|CONSTR,1,get_oct_alignd_cstr);	/* oct alignd, cstr	*/
  ASN1R_TAG_ADD (aCtx, CTX,1,get_oct_alignd_prim);	/* oct alignd, prim	*/
  ASN1R_TAG_ADD (aCtx, CTX|CONSTR,2,get_arb_cstr);	/* arbitrary, cstr	*/
  ASN1R_TAG_ADD (aCtx, CTX,2,get_arb_prim);		/* arbitrary, prim	*/
  }

/************************************************************************/
/*			   get_sngl_asn1				*/
/* This is the function called when :					*/
/************************************************************************/

static ST_VOID get_sngl_asn1 (ASN1_DEC_CTXT *aCtx)
  {
  MLOG_CDEC0 ("get_sngl_asn1");

/* aCtx->asn1r_field_ptr points to 'any', elmnt is length of data			*/
  ext_info_ptr->encoding_tag = 0;		/* write tag		*/
  ext_info_ptr->data_len = aCtx->asn1r_elmnt_len;		/* len of ANY		*/
  ext_info_ptr->data_ptr = aCtx->asn1r_field_ptr;		/* leave it lie		*/

  asn1r_parse_next (aCtx, sngl_parse_done);
  }


/************************************************************************/
/*			   sngl_parse_done				*/
/*									*/
/************************************************************************/

static ST_VOID sngl_parse_done (ASN1_DEC_CTXT *aCtx)
  {
  MLOG_CDEC0 ("sngl_parse_done");

  aCtx->asn1r_c_done_fun[aCtx->asn1r_msg_level] = sngl_done;
  }

/************************************************************************/
/*			   sngl_done 					*/
/* This is the cstr done function called when :				*/
/************************************************************************/

static ST_VOID sngl_done (ASN1_DEC_CTXT *aCtx)
  {
  MLOG_CDEC0 ("sngl_done");

  aCtx->asn1r_decode_method = save_method;		/* this extern is now done	*/
  }


/************************************************************************/
/*			   get_oct_alignd_prim				*/
/* This is the function called when :					*/
/************************************************************************/

static ST_VOID get_oct_alignd_prim (ASN1_DEC_CTXT *aCtx)
  {
  MLOG_CDEC0 ("get_oct_alignd_prim");

/* aCtx->asn1r_elmnt_len == # octects */

  ext_info_ptr->encoding_tag = 1;		/* write tag		*/
  ext_info_ptr->data_len = aCtx->asn1r_elmnt_len;		/* len of ANY		*/
  ext_info_ptr->data_ptr = aCtx->asn1r_field_ptr;		/* leave it lie		*/

  aCtx->asn1r_field_ptr += aCtx->asn1r_elmnt_len;			/* skip over data	*/

  aCtx->asn1r_decode_method = save_method;		/* this extern is now done	*/
  }

/************************************************************************/
/*			   get_oct_alignd_cstr				*/
/* This is the function called when :					*/
/************************************************************************/

static ST_VOID get_oct_alignd_cstr (ASN1_DEC_CTXT *aCtx)
  {
  MLOG_CDEC0 ("get_oct_alignd_cstr");

  ext_info_ptr->encoding_tag = 1;		/* write tag		*/
  ext_info_ptr->data_ptr = aCtx->asn1r_field_ptr;		/* leave it lie		*/
  asn1r_get_octstr_cstr (aCtx, ASN1_MAX_PDU, aCtx->asn1r_field_ptr);
  aCtx->asn1r_c_done_fun[aCtx->asn1r_msg_level] = oct_alignd_done;
  }

/************************************************************************/
/*			   oct_alignd_done 				*/
/* This is the cstr done function called when :				*/
/************************************************************************/

static ST_VOID oct_alignd_done (ASN1_DEC_CTXT *aCtx)
  {
  MLOG_CDEC0 ("oct_alignd_done");

  ext_info_ptr->data_len = aCtx->asn1r_octetcount;		/* len of octet string  */
  aCtx->asn1r_decode_method = save_method;		/* this extern is now done	*/
  }

/************************************************************************/
/*			   get_arb_prim					*/
/* This is the function called when :					*/
/************************************************************************/

static ST_VOID get_arb_prim (ASN1_DEC_CTXT *aCtx)
  {
  MLOG_CDEC0 ("get_arb_prim");

/* aCtx->asn1r_bitcount = bits */

  ext_info_ptr->encoding_tag = 2;		/* write tag		*/
  ext_info_ptr->data_len = aCtx->asn1r_elmnt_len-1;	/* len of ANY		*/
  ext_info_ptr->data_ptr = aCtx->asn1r_field_ptr;		/* leave it lie		*/
  asn1r_get_bitstr (aCtx, aCtx->asn1r_field_ptr,0);	/* max_bits=0 (no limit)*/
  ext_info_ptr->num_bits = aCtx->asn1r_bitcount;
  aCtx->asn1r_decode_method = save_method;		/* this extern is now done	*/
  }

/************************************************************************/
/*			   get_arb_cstr					*/
/* This is the function called when :					*/
/************************************************************************/

static ST_VOID get_arb_cstr (ASN1_DEC_CTXT *aCtx)
  {
  MLOG_CDEC0 ("get_arb_cstr");

  ext_info_ptr->encoding_tag = 2;		/* write tag		*/
  ext_info_ptr->data_ptr = aCtx->asn1r_field_ptr;		/* leave it lie		*/

  asn1r_get_bitstr_cstr (aCtx, ASN1_MAX_PDU, aCtx->asn1r_field_ptr);

  aCtx->asn1r_c_done_fun[aCtx->asn1r_msg_level] = arb_done;
  }


/************************************************************************/
/*			   arb_done 					*/
/* This is the cstr done function called when :				*/
/************************************************************************/

static ST_VOID arb_done (ASN1_DEC_CTXT *aCtx)
  {
  MLOG_CDEC0 ("arb_done");

  ext_info_ptr->num_bits = aCtx->asn1r_bitcount;
  ext_info_ptr->data_len = CALC_BIT_LEN (aCtx->asn1r_bitcount);
  aCtx->asn1r_decode_method = save_method;		/* this extern is now done	*/
  }

/************************************************************************/
/************************************************************************/
/************************************************************************/
/*			 ms_encode_extern 				*/
/************************************************************************/

ST_RET ms_encode_extern (EXTERN_INFO *ext, ST_UCHAR *dest, ST_INT dest_len,
	ST_INT *ext_len, ST_UCHAR **ext_ptr)
  {
ST_RET ret;
ASN1_ENC_CTXT localEncCtx;	/* For readability, use "aCtx" to access this.*/
ASN1_ENC_CTXT *aCtx = &localEncCtx;

  S_LOCK_COMMON_RESOURCES ();
  asn1r_strt_asn1_bld (aCtx, dest,dest_len);	/* init the builder     */
  wr_external (aCtx, ext);

  *ext_ptr = aCtx->asn1r_field_ptr + 1;
  *ext_len = (dest+dest_len) - *ext_ptr;

  if (!aCtx->asn1r_encode_overrun)		/* Check for encode overrun 	*/
    ret = SD_SUCCESS;
  else
    ret = ME_ASN1_ENCODE_OVERRUN;

  S_UNLOCK_COMMON_RESOURCES ();
  return (ret);
  }

/************************************************************************/
/*			 wr_external 					*/
/************************************************************************/

ST_VOID wr_external (ASN1_ENC_CTXT *aCtx, EXTERN_INFO *ext)
  {
  asn1r_strt_constr (aCtx);		/* start EXTERNAL			*/

  if (ext->encoding_tag == 0)	/* ANY */
    {
    asn1r_wr_octstr (aCtx, ext->data_ptr,ext->data_len);
    asn1r_fin_constr (aCtx, 0,CTX,DEF);   
    }
  else if (ext->encoding_tag == 1)	/* Octet Aligned */
    {
    asn1r_wr_octstr (aCtx, ext->data_ptr,ext->data_len);
    asn1r_fin_prim (aCtx, 1,CTX);		/* finish octet-aligned string		*/
    }
  else if (ext->encoding_tag == 2)	/* Arbitrary			*/
    {
    asn1r_wr_bitstr (aCtx, ext->data_ptr,ext->num_bits);
    asn1r_fin_prim (aCtx, 2,CTX);		
    }

  if (ext->dv_descr_pres)
    {
    asn1r_wr_octstr (aCtx, ext->dv_descr,ext->dv_descr_len);
    asn1r_fin_prim (aCtx, OBJDSCR_CODE,UNI);	/* object descripter    */
    }

  if (ext->indir_ref_pres)
    {
    asn1r_wr_i32 (aCtx, ext->indir_ref);
    asn1r_fin_prim (aCtx, INT_CODE,UNI);
    }

  if (ext->dir_ref_pres)
    {
    asn1r_wr_objid (aCtx, ext->dir_ref.comps,ext->dir_ref.num_comps);
    asn1r_fin_prim (aCtx, OBJ_ID_CODE,UNI);
    }

  asn1r_fin_constr (aCtx, EXTERN_CODE,UNI,DEF);		/* finish EXTERNAL	*/
  }


