/************************************************************************/
/* SISCO SOFTWARE MODULE HEADER *****************************************/
/************************************************************************/
/*   (c) Copyright Systems Integration Specialists Company, Inc.,	*/
/*      	  1986 - 2008, All Rights Reserved.		        */
/*									*/
/*		    PROPRIETARY AND CONFIDENTIAL			*/
/*									*/
/* MODULE NAME : mmsdec.c						*/
/* PRODUCT(S)  : MMSEASE, MMSEASE-LITE				    	*/
/*									*/
/* MODULE DESCRIPTION : 						*/
/*	This module contains the initial MMS message decode functions.	*/
/*	Decodes the header portion of MMS PDUs. 			*/
/*									*/
/* GLOBAL FUNCTIONS DEFINED IN THIS MODULE :				*/
/*									*/
/* MODIFICATION LOG :							*/
/*  Date     Who   Rev			Comments			*/
/* --------  ---  ------   -------------------------------------------	*/
/* 03/17/08  EJV     10	   Added slog.					*/    
/* 12/19/05  JRB     22    Chg cr to cr_mms, del static to fix warning.*/
/* 09/07/05  JRB     08    Fix backwards client/server error log msgs.	*/
/* 07/24/02  EJV     07    mmsdec_ucreq_opcode: corr err in the check	*/
/*			     opcode due to recent MMSOP_xxx changes.	*/
/* 12/20/01  JRB     06    Converted to use ASN1R (re-entrant ASN1)	*/
/*			   Del mms_err_invoke_fun proto (see mms_dfun.h)*/
/* 09/13/99  MDE     05    Added SD_CONST modifiers			*/
/* 04/07/99  MDE     04    Logging improvements (unsupported service)	*/
/* 03/23/99  MDE     03    Changes to decode buffer allocation scheme	*/
/* 06/15/98  MDE     02    Changes to allow compile under C++		*/
/* 03/18/98  JRB     01    Fix mmsdec_map_err for new error values.	*/
/* 04/02/97  DTL   7.00    MMSEASE 7.0 release. See MODL70.DOC for	*/
/*			   history.					*/
/************************************************************************/

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

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

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

#ifdef DEBUG_SISCO
SD_CONST static ST_CHAR *SD_CONST thisFileName = __FILE__;
#endif

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

static ST_VOID mmsdec_start (ASN1_DEC_CTXT *aCtx, ST_UINT16 id_code);
static ST_VOID mmsdec_invoke_id (ASN1_DEC_CTXT *aCtx, ST_UINT16 id_code);
#ifdef MOD_SUPPORT 		       /* support modifiers 		*/
static ST_VOID mmsdec_modlist (ASN1_DEC_CTXT *aCtx, ST_UINT16 el_id);
static ST_VOID  mmsdec_modlist_done_fun (ASN1_DEC_CTXT *aCtx);
#endif
static ST_VOID  mmsdec_opcode (ASN1_DEC_CTXT *aCtx, ST_UINT16 id_code);
static ST_VOID  mmsdec_ucreq_opcode (ASN1_DEC_CTXT *aCtx, ST_UINT16 id_code);
static ST_RET   mmsdec_chk_req_op (ASN1_DEC_CTXT *aCtx, ST_INT opcode, ST_INT unsupp_code);
static ST_RET   mmsdec_chk_resp_op (ASN1_DEC_CTXT *aCtx, ST_UINT opcode);
static ST_VOID  mmsdec_map_err (ASN1_DEC_CTXT *aCtx);    /* maps ASN.1 tool errors to MMS	*/
#ifndef MMS_LITE 		       /* don't assign op id's 		*/
static ST_VOID  mmsdec_assign_rid (ST_INT opcode);
#endif

/************************************************************************/
const ST_CHAR cr_mms[] =
 "(c) COPYRIGHT SYSTEMS INTEGRATION SPECIALISTS COMPANY INC., 1986 - 2005.  All Rights Reserved.";
/************************************************************************/
/* Operation Specific Decode Buffer Management				*/

ST_VOID *(*m_calloc_os_fun)(ST_UINT num, ST_UINT size);
ST_VOID *(*m_realloc_os_fun)(ST_VOID *old, ST_UINT new_size);
ST_VOID  (*m_free_os_fun)(ST_VOID *buf);

/************************************************************************/
/* The array below is used to check the constructed state of a PDU	*/

SD_CONST static ST_UINT16 mmspdu_cstr_chk[] =
   {
   CONSTR,		/* CONFIRMED REQUEST IS CONSTRUCTOR		*/
   CONSTR,		/* CONFIRMED RESPONSE				*/
   CONSTR,		/* CONFIRMED ERROR				*/
   CONSTR,		/* UNCONFIRMED PDU				*/
   CONSTR,		/* REJECT					*/
   0,			/* CANCEL REQUEST				*/
   0,			/* CANCEL RESPONSE				*/
   CONSTR,		/* CANCEL ERROR					*/
   CONSTR,		/* INITIATE REQUEST				*/
   CONSTR,		/* INITIATE RESPONSE				*/
   CONSTR,		/* INITIATE ERROR				*/
   0,			/* CONCLUDE REQUEST IS PRIMITIVE		*/
   0,			/* CONCLUDE RESPONSE				*/
   CONSTR		/* CONCLUDE ERROR IS CONSTRUCTOR		*/
   };


/************************************************************************/
/************************************************************************/
/*			_ms_mms_decode					*/
/* Function to setup and initiate MMS PDU decode			*/
/* Only fourteen context specific codes are legal at start		*/
/* Input parameters : pointer to message start, length of message,	*/
/* pointer to result buffer.						*/
/************************************************************************/

ST_RET _ms_mms_decode (ST_UCHAR *msg_ptr, ST_INT len, MMSDEC_INFO *rslt_ptr)
  {
ASN1_DEC_CTXT localDecCtx;	/* For readability, use "aCtx" to access this.*/
ASN1_DEC_CTXT *aCtx = &localDecCtx;

  MLOG_DEC0 ("Decoding MMS PDU");
  memset (aCtx, 0, sizeof (ASN1_DEC_CTXT));	/* CRITICAL: start clean.	*/

  aCtx->asn1r_decode_method = ASN1_CLASS_METHOD; /* select the class method	*/
  aCtx->asn1r_c_id_fun = mmsdec_start;	/* point to mms decode initial function */
  aCtx->asn1r_u_id_fun = asn1r_class_err; 	/* no universals to start		*/
  aCtx->asn1r_a_id_fun = asn1r_class_err; 	/* no applications to start		*/
  aCtx->asn1r_p_id_fun = asn1r_class_err; 	/* no privates to start 		*/

  aCtx->asn1r_decode_done_fun = asn1r_done_err; /* done function = error fun 	*/
  aCtx->asn1r_err_fun = NULL;		   /* set up error detected fun 	*/

  _mmsdec_msglen = len;			/* save decode message length	*/
  _mmsdec_msgptr = msg_ptr; 		/* save decode message ptr	*/
  _mmsdec_rslt	= rslt_ptr;		/* save the result pointer	*/
  rslt_ptr->err_code = NO_DECODE_ERR;	/* start with no error		*/
  rslt_ptr->dec_level = 0;		/* start with no usable info	*/
  rslt_ptr->data_pres = SD_FALSE;
  rslt_ptr->mods.info_pres = SD_FALSE;
  rslt_ptr->cs.cs_pres = SD_FALSE;
  _mms_dec_info_pres = SD_FALSE;

/* ASN.1 decode machine is set up for initial MMS decode.		*/
/* Call 'asn1r_decode_asn1' to parse the message.                             */

  asn1r_decode_asn1 (aCtx, msg_ptr,len);		/* do message decode		*/
  mmsdec_map_err (aCtx);			/* write the error code 	*/
  if (aCtx->asn1r_pdu_dec_err == NO_DECODE_ERR)
    return (SD_SUCCESS); 			/* return 'no error' code       */
#ifndef MMS_LITE
  else
    {
    if (_mmsdec_rslt->mods.info_pres)	/* see if there were any mods	*/
      chk_free (_mmsdec_rslt->mods.mod_list_ptr);

/* Note that op specific buffer are free'd in _mms_dec_buf_free on error	*/

    }
#endif
  return (MVE_ASN1_DECODE_ERR);		/* return error code		*/
  }

/************************************************************************/
/************************************************************************/
/*			mmsdec_start					*/
/* Initial MMS decode context-specific code service function.  This	*/
/* function is called when the first data element of the message is a	*/
/* context-specific class.						*/
/************************************************************************/

#define MAX_MMS_PDU_TAG		13

static ST_VOID mmsdec_start (ASN1_DEC_CTXT *aCtx, ST_UINT16 id_code)
  {
  MLOG_CDEC0 ("mmsdec_start");

/* check for valid MMS PDU type, such as request, response, error, etc. */
/* if valid tag and constructed	state OK				*/
  if (id_code <= MAX_MMS_PDU_TAG &&
			 !(mmspdu_cstr_chk[id_code] ^ aCtx->asn1r_constr_elmnt))
    {				
    _mmsdec_rslt->dec_level = 1;  /* set flag that PDU type is OK	*/
    _mmsdec_rslt->type = id_code; /* save the type for the user	*/
    switch (id_code)
      {
      case MMSREQ :			/* Confirmed Request PDU	*/
	aCtx->asn1r_u_id_fun = mmsdec_invoke_id;	/* next must be uni, integer 	*/
					/* invoke ID			*/
	aCtx->asn1r_c_id_fun = asn1r_class_err;		/* can not context specific	*/
	break;

      case MMSRESP :			/* Confirmed Response PDU	*/
	aCtx->asn1r_u_id_fun = mmsdec_invoke_id;	/* next must be uni, integer 	*/
					/* invoke ID			*/
	aCtx->asn1r_c_id_fun = asn1r_class_err;		/* cannot be context specific	*/
	break;

      case MMSERROR :			/* Confirmed Error PDU		*/
	aCtx->asn1r_c_id_fun = mms_err_invoke_fun;	/* must be context specific	*/
	break;		        	/* integer (invoke ID)		*/

      case MMSUNREQ :			/* Unconfirmed PDU		*/
	aCtx->asn1r_c_id_fun = mmsdec_ucreq_opcode;	/* expect context-specific tag	*/
	break;

      case MMSREJECT :			/* Reject PDU			*/
	aCtx->asn1r_c_id_fun = mms_reject;		/* expect context-specific	*/
	break;

      case MMSCANREQ :			/* Cancel Request PDU		*/
        if (!(mmsop_en[MMSOP_CANCEL] & RESP_EN))	/* protocol error if	*/
          asn1r_set_dec_err (aCtx, CANREQ_UNSPECIFIED);	/* unsupported service	*/
        else
          {
          if (asn1r_get_u32 (aCtx, &_mmsdec_rslt->id))
	    asn1r_set_dec_err (aCtx, CANREQ_INVAL_INVOKE);
          else
            {
	    _mmsdec_rslt->dec_level = 2;
            (*mms_req_decode_fun [MMSOP_CANCEL]) (aCtx);
            }
          }
	break;
		     
      case MMSCANRESP : 		/* Cancel Response PDU		*/
	if (!(mmsop_en[MMSOP_CANCEL] & REQ_EN))	/* protocol error if	*/
	  asn1r_set_dec_err (aCtx, CANRESP_UNSPECIFIED);	/* not supported	*/
        else
	  {
	  if (asn1r_get_u32 (aCtx, &_mmsdec_rslt->id))
	    asn1r_set_dec_err (aCtx, CANRESP_INVAL_INVOKE);
          else
            {
	    _mmsdec_rslt->dec_level = 2;		/* Save inv. ID in rslt */
            (*mms_rsp_decode_fun [MMSOP_CANCEL]) (aCtx);
            }
          }
	break;

      case MMSCANERR :			/* Cancel Error PDU		*/
	if (!(mmsop_en[MMSOP_CANCEL] & REQ_EN))	/* protocol error if	*/
	  asn1r_set_dec_err (aCtx, CANERR_INVAL_SERV);	/* is not supported	*/
        else					/* expect context-spec	*/
	  aCtx->asn1r_c_id_fun = mms_err_invoke_fun;
	break;			      
 
      case MMSINITREQ : 		/* Initiate Request PDU		*/
    	if (!(mmsop_en[MMSOP_INITIATE] & RESP_EN))	/* if not enabled	*/
          asn1r_set_dec_err (aCtx, REQ_UNREC_SERV);		/* as responder		*/
        else
          {				  
#ifndef MMS_LITE
          mmsdec_assign_rid (MMSOP_INITIATE);	/* Assign opcode and	*/
						/* reserved Initiate ID	*/
#endif
	  (*mms_req_decode_fun [MMSOP_INITIATE]) (aCtx);
          }
        break;

      case MMSINITRESP :	       /* Initiate Response PDU		*/
    	if (!(mmsop_en[MMSOP_INITIATE] & REQ_EN))	/* if not enabled	*/
          asn1r_set_dec_err (aCtx, RESP_UNREC_SERV);		/* as responder		*/
        else
          {	     
#ifndef MMS_LITE
          mmsdec_assign_rid (MMSOP_INITIATE);	/* Assign opcode and	*/
					        /* reserved Initiate ID	*/
#endif
	  (*mms_rsp_decode_fun [MMSOP_INITIATE]) (aCtx);
          }
        break;

      case MMSINITERR : 		/* Initiate Error PDU		*/
	if (!(mmsop_en[MMSOP_INITIATE] & REQ_EN))	/* protocol error if	*/
	  asn1r_set_dec_err (aCtx, ERR_UNREC_SERV);		/* is not supported	*/
        else
	  {
#ifndef MMS_LITE
	  mmsdec_assign_rid (MMSOP_INITIATE);	/* Assign opcode and	*/
						/* reserved Initiate ID	*/
#endif
  	  mms_err_rsp (aCtx); 			/* general err function	*/
          }
	break;

      case MMSCNCLREQ : 		/* Conclude Requset PDU 	*/
	if (!(mmsop_en[MMSOP_CONCLUDE] & RESP_EN))	/* protocol error if	*/
	  asn1r_set_dec_err (aCtx, CONREQ_UNSPECIFIED);	/* not supported	*/
        else
	  {
	  if (aCtx->asn1r_elmnt_len)
	    asn1r_set_dec_err (aCtx, CONREQ_INVAL_ARG); 	
          else
	    {
#ifndef MMS_LITE
	    mmsdec_assign_rid (MMSOP_CONCLUDE);/* Assign opcode and	*/
						/* reserved Conclude ID */
#endif
            (*mms_req_decode_fun [MMSOP_CONCLUDE]) (aCtx);
   	    }
          }
	break;

      case MMSCNCLRESP :		/* Conclude Response PDU	*/
	if (!(mmsop_en[MMSOP_CONCLUDE] & REQ_EN))	/* protocol error if	*/
	  asn1r_set_dec_err (aCtx, CONRESP_UNSPECIFIED);	/* not supported	*/
        else
	  {
	  if (aCtx->asn1r_elmnt_len)
	     asn1r_set_dec_err (aCtx, CONRESP_INVAL_RSLT);	/* if constructor	*/
          else
	    {
#ifndef MMS_LITE
	    mmsdec_assign_rid (MMSOP_CONCLUDE); /* Assign opcode and	*/
		 				  /* reserved Concl ID	*/
#endif
            (*mms_rsp_decode_fun [MMSOP_CONCLUDE]) (aCtx);
 	    }
 	  }
	break;

      case MMSCNCLERR : 		/* Conclude Error PDU		*/
	if (!(mmsop_en[MMSOP_CONCLUDE] & REQ_EN))	/* protocol error if	*/
	  asn1r_set_dec_err (aCtx, CONERR_INVAL_SERV);	/* is not supported	*/
	else
	  {
#ifndef MMS_LITE
	  mmsdec_assign_rid (MMSOP_CONCLUDE); /* Assign opcode and	*/
						/* reserved Conclude ID */
#endif
	  mms_err_rsp (aCtx); 			/* general err function	*/
	  }
	break;

      default:
	asn1r_set_dec_err (aCtx, PDU_UNKNOWN_TYPE);	/* Not a legal MMS PDU type.	*/
      }
    }
  else
    asn1r_set_dec_err (aCtx, PDU_UNKNOWN_TYPE);	/* Not a legal MMS PDU type.	*/
  }					/* or constructor error		*/


/************************************************************************/
/************************************************************************/
/*			mmsdec_invoke_id   				*/
/* Universal class service function called when invoke ID is expected	*/
/* (after first data element in most MMS confirmed services has been	*/
/* decoded, and this data element is a universal).			*/
/************************************************************************/

static ST_VOID mmsdec_invoke_id (ASN1_DEC_CTXT *aCtx, ST_UINT16 id_code)
  {
  if (aCtx->asn1r_constr_elmnt || id_code != INT_CODE)
    asn1r_set_dec_err (aCtx, PDU_INVALID);	/* not a constr or INTEGER type */
  else
    {
    if (asn1r_get_u32 (aCtx, &_mmsdec_rslt->id))
      {
      if (_mmsdec_rslt->type == MMSREQ)
	asn1r_set_dec_err (aCtx, REQ_BAD_VALUE);    /* not a valid 4-byte integer	*/
      else
	asn1r_set_dec_err (aCtx, RESP_BAD_VALUE);   /* not a valid 4-byte integer	*/
      return;
      }
    }

  MLOG_CDEC1 ("Invoke ID = %lu",_mmsdec_rslt->id);

  _mmsdec_rslt->dec_level = 2;   /* set flag that Invoke ID is OK	*/
				/* setup function pointers for next	*/
  aCtx->asn1r_c_id_fun = mmsdec_opcode;	/* next is explicit context tag 	*/
#ifdef MOD_SUPPORT
  if (_mmsdec_rslt->type == MMSREQ)/* if MMSREQ a modifier could be next */
    aCtx->asn1r_u_id_fun = mmsdec_modlist; 	/* modifiers are tagged universal	*/
  else
#endif
    aCtx->asn1r_u_id_fun = asn1r_class_err; 	/* no more universals			*/
  }

#ifdef MOD_SUPPORT
/************************************************************************/
/************************************************************************/
/*			mmsdec_modlist					*/
/* An MMS request has been received with a modifier list present.  We	*/
/* change the asn1_decode_method to TAG so _ms_get_mms_modlist can decode it.	*/
/* During the decode the modifier list will be attached to _mmsdec_rslt.	*/
/* Upon completion of the decode we see if we can give back any unused	*/
/* memory and change the decod_method back to CLASS.			*/
/************************************************************************/

static ST_VOID mmsdec_modlist (ASN1_DEC_CTXT *aCtx, ST_UINT16 el_id)
  {
#ifdef MMS_LITE
extern ST_CHAR mms_dec_modbuf [];
#endif

  aCtx->asn1r_decode_method = ASN1_TAG_METHOD;		/* select TAG method for get_mms_modifer*/
  
  _mmsdec_rslt->mods.num_of_mods = 0;
#ifndef MMS_LITE
  _mmsdec_rslt->mods.mod_list_ptr = (MODIFIER *)
                                   chk_calloc (m_max_mods,
                                               sizeof (MODIFIER));
#else
  _mmsdec_rslt->mods.mod_list_ptr = (MODIFIER *) mms_dec_modbuf;
#endif
  _ms_get_mms_modlist (aCtx, _mmsdec_rslt->mods.mod_list_ptr,
                   &(_mmsdec_rslt->mods.num_of_mods),
                   m_max_mods,
		   mmsdec_modlist_done_fun);
  }

/************************************************************************/
/************************************************************************/
/*			mmsdec_modlist_done_fun				*/
/* This function is called when the modifier list has been decoded.	*/
/************************************************************************/

static ST_VOID mmsdec_modlist_done_fun (ASN1_DEC_CTXT *aCtx)
  {

  aCtx->asn1r_decode_method = ASN1_CLASS_METHOD; /* restore the class method	*/
  _mmsdec_rslt->mods.info_pres = SD_TRUE;
  
  }
#endif  

/************************************************************************/
/************************************************************************/
/*			mmsdec_opcode					*/
/* An MMS request with good invoke ID has been detected. Next field	*/
/* should be an implicit context tag which represents the opcode.	*/
/* This function is called if a context class is decoded.		*/
/************************************************************************/

static ST_VOID mmsdec_opcode (ASN1_DEC_CTXT *aCtx, ST_UINT16 id_code)
  {
ST_INT operr;

#ifndef MMS_LITE
  if (id_code > MAX_MMS_OPCODE)
    {				/* Must be a legal MMS opcode		*/
    if (_mmsdec_rslt->type == MMSREQ)
	asn1r_set_dec_err (aCtx, REQ_UNREC_SERV);
    else
	asn1r_set_dec_err (aCtx, RESP_UNREC_SERV);
    return;
    }
#endif

  _mmsdec_rslt->dec_level = 3;    /* set flag that opcode is read OK	*/
  _mmsdec_rslt->op = id_code;     /* save the opcode for the user	*/

  MLOG_CDEC1 ("MMS Opcode = %d",_mmsdec_rslt->op);

/* Opcode has been saved.  Check that the service corresponding to this */
/* opcode is supported and there are no parse errors.			*/

  if (_mmsdec_rslt->type == MMSREQ)
    {
    						/* pass err code	*/
    operr = mmsdec_chk_req_op (aCtx, _mmsdec_rslt->op,REQ_UNREC_SERV);
    if (!operr)
      (*mms_req_decode_fun [_mmsdec_rslt->op]) (aCtx);
    }
  else
    {
    operr = mmsdec_chk_resp_op (aCtx, _mmsdec_rslt->op);
    if (!operr)
      (*mms_rsp_decode_fun [_mmsdec_rslt->op]) (aCtx);
    }
  }


/************************************************************************/
/************************************************************************/
/*			mmsdec_ucreq_opcode				*/
/* An MMS unconfirmed request has been detected.  Next field should be	*/
/* an implicit context tag which represents the opcode.  This function	*/
/* is called if a context class is decoded.				*/
/************************************************************************/

static ST_VOID mmsdec_ucreq_opcode (ASN1_DEC_CTXT *aCtx, ST_UINT16 id_code)
  {
  if (id_code == 0)
    _mmsdec_rslt->op = MMSOP_INFO_RPT;
  else if (id_code == 1)
    _mmsdec_rslt->op = MMSOP_USTATUS;
  else if (id_code == 2)
    _mmsdec_rslt->op = MMSOP_EVENT_NOT;
  else
    {				/* Must be a legal MMS opcode		*/
    asn1r_set_dec_err (aCtx, PDU_INVALID);
    return;
    }

  MLOG_CDEC1 ("Unconfirmed request opcode = %d",_mmsdec_rslt->op);

  _mmsdec_rslt->dec_level = 3;    /* set flag that opcode is read OK	*/
  _mmsdec_rslt->id = UNREQ_INVOKE;

/* Opcode has been saved.  Check that the service corresponding to this */
/* opcode is supported and there are no parse errors.			*/

  if (!mmsdec_chk_req_op (aCtx, _mmsdec_rslt->op,UNCONF_UNREC_SERV))
    (*mms_req_decode_fun [_mmsdec_rslt->op]) (aCtx);	/* continue decode	*/
  }


/************************************************************************/
/*			     mmsdec_chk_req_op				*/
/*			     mmsdec_chk_resp_op				*/
/* This function checks whether the given opcode is supported as	*/
/* request or response (or both), if the data element containing the	*/
/* opcode is of the proper form (either primitive or constructor),	*/
/* and if it is a NULL when it is supposed to be.  The mmsop_en table	*/
/* contains the opcode support information.				*/
/* Returns 0 for success, else non-zero error code			*/
/************************************************************************/

static ST_RET mmsdec_chk_req_op (ASN1_DEC_CTXT *aCtx, ST_INT opcode, ST_INT unsupp_code)
  {
ST_UCHAR op_en;

/* First check that opcode range, then whether it is enabled		*/

  if ((opcode > MAX_MMSOP_DIS) || ! ((op_en = mmsop_en[opcode]) & RESP_EN))
    {
    if (opcode <= MAX_MMSOP_DIS)
      {
      MLOG_NERR1 ("MMS service %s not supported as server (cannot decode request)", 
					mms_op_string[opcode]);
      }
    else
      {
      MLOG_NERR1 ("MMS service opcode %d out of range", opcode);
      }
    asn1r_set_dec_err (aCtx, unsupp_code);			/* req not supported	*/
    return (MVE_REQ_NOT_SUPP);
    }
  else
    {
    if ((aCtx->asn1r_constr_elmnt && !(op_en & 0x04)) ||
	 (!aCtx->asn1r_constr_elmnt && (op_en & 0x04)) ||
         (aCtx->asn1r_elmnt_len > 0 && (op_en & 0x10)))
      {
      asn1r_set_dec_err (aCtx, PDU_INVALID);
      return (MVE_INVALID_PDU);
      }
    return (SD_SUCCESS);			/* return success code			*/
    }
  }

/************************************************************************/
static ST_RET mmsdec_chk_resp_op (ASN1_DEC_CTXT *aCtx, ST_UINT opcode)
  {
ST_UCHAR op_en;

/* First check that opcode range, then whether it is enabled		*/

  if ((opcode > MAX_MMSOP_DIS) || ! ((op_en = mmsop_en[opcode]) & REQ_EN))
    {
    if (opcode <= MAX_MMSOP_DIS)
      {
      MLOG_NERR1 ("MMS service %s not supported as client (cannot decode response)", 
					mms_op_string[opcode]);
      }
    else
      {
      MLOG_NERR1 ("MMS service opcode %d out of range", opcode);
      }

    asn1r_set_dec_err (aCtx, RESP_UNREC_SERV);		/* resp not supported	*/
    return (MVE_RESP_NOT_SUPP);
    }
  else
    {
    if (opcode == MMSOP_INIT_DOWNLOAD)
      {
#ifndef MMS_LITE
      if (mms_chan_info[_mmsdechan].version)	/* IS 	*/
#else
      if (mmsl_version)				/* IS 	*/
#endif
        {
        if (aCtx->asn1r_constr_elmnt || aCtx->asn1r_elmnt_len)
          {
          asn1r_set_dec_err (aCtx, PDU_INVALID);
          return (MVE_INVALID_PDU);
          }
        }
      else					/* DIS	*/
        {
        if (!aCtx->asn1r_constr_elmnt || !aCtx->asn1r_elmnt_len)
          {
          asn1r_set_dec_err (aCtx, PDU_INVALID);
          return (MVE_INVALID_PDU);
          }
        }
      }
    else
      {
      if ((aCtx->asn1r_constr_elmnt && !(op_en & 0x08)) ||
  	  (!aCtx->asn1r_constr_elmnt && (op_en & 0x08)) ||
          (aCtx->asn1r_elmnt_len > 0 && (op_en & 0x20)))
        {
        asn1r_set_dec_err (aCtx, PDU_INVALID);
        return (MVE_INVALID_PDU);
        }
      }

    return (SD_SUCCESS);			/* return success code 		*/
    }
  }


/************************************************************************/
/*			mmsdec_map_err					*/
/* Function to map the error codes supplied by the generic ASN.1 tools	*/
/* to the MMS error codes specified for the Reject service.  Inputs are */
/* the global variables:						*/
/*			ST_UINT16 aCtx->asn1r_pdu_dec_err;	*/
/*			MMSDEC_INFO *_mmsdec_rslt;		*/
/*									*/
/* (A aCtx->asn1r_pdu_dec_err value of 0 should not occur and maps to UNSPECIFIED.) */
/************************************************************************/
static ST_VOID mmsdec_map_err (ASN1_DEC_CTXT *aCtx)
  {
ST_INT level;

  level = 0;

/* asn1_pdu_dec_err codes of NO_DECODE_ERR are OK, and codes of > 15 have been	*/
/* set by the MMS decode logic and so are OK.  15 is a special number	*/
/* because the lowest reject code is a 0x10(REQ_UNSPECIFIED)		*/

  if (aCtx->asn1r_pdu_dec_err == NO_DECODE_ERR || aCtx->asn1r_pdu_dec_err >= REQ_UNSPECIFIED)
    {
    _mmsdec_rslt->err_code = aCtx->asn1r_pdu_dec_err;
    }

/* We started to decode some type of PDU and it wasn't valid.  Here we	*/
/* try to determine how much of the PDU we parsed and generate a 	*/
/* pertainent error code						*/

  else
    {
    level = _mmsdec_rslt -> dec_level;
    if (level == 0)			/* did we determine it's type ?	*/
      _mmsdec_rslt->err_code = PDU_INVALID;
    else				/* if we knew it's type we 	*/
      {					/* switch to figure out more	*/
      switch (_mmsdec_rslt->type)
        {

/* About the only time we get here and have to do this is when the MMS	*/
/* decoder temporarily turned the decode over the the ASN.1 tools (via	*/
/* parse_next, parse_cstr_contents, etc) and an error was discovered.	*/
/* The error the ASN.1 tools will report is <= 15.  Some, most or all	*/
/* of the PDU was decoded.  It's not fair to just say we couldn't tell	*/
/* anything at all about the PDU.					*/
/* These are the rules for determining what reject class and code get	*/
/* returned for the rejected PDU.  The protocol for the begining of	*/
/* each MMSpdu is different so almost each type has to dealt with on an	*/
/* individual basis.							*/

        case MMSREQ :		/* request for most confirmed services	*/
          if (level >= 2)
            _mmsdec_rslt->err_code = REQ_INVAL_ARG;
          else
            _mmsdec_rslt->err_code = REQ_UNSPECIFIED;
 	  break;

        case MMSRESP :		/* response for most confirmed services */
          if (level >= 2)
            _mmsdec_rslt->err_code = RESP_INVAL_RSLT;
          else
            _mmsdec_rslt->err_code = RESP_UNSPECIFIED;
	  break;

        case MMSERROR :		/* error for most confirmed services	*/
          _mmsdec_rslt->err_code = ERR_UNSPECIFIED;
          break;

        case MMSUNREQ :		/* request for unconfirmed services	*/
          _mmsdec_rslt->err_code = UNCONF_UNSPECIFIED;
          break;

        case MMSREJECT :	/* reject				*/
          _mmsdec_rslt->err_code = PDU_UNKNOWN_TYPE;
          break;

        case MMSCANREQ :	/* request for the Cancel service	*/
          _mmsdec_rslt->err_code = CANREQ_UNSPECIFIED;
          break;

        case MMSCANRESP : 	/* response for the Cancel service	*/
          _mmsdec_rslt->err_code = CANRESP_UNSPECIFIED;
          break;

        case MMSCANERR :	/* error for Cancel service	*/
          _mmsdec_rslt->err_code = CANERR_UNSPECIFIED;
          break;

        case MMSINITREQ : 	/* request for Initiate service */
          _mmsdec_rslt->err_code = REQ_BAD_VALUE;
          break;

        case MMSINITRESP :	/* response for Initiate service */
          _mmsdec_rslt->err_code = RESP_BAD_VALUE;
          break;

        case MMSINITERR : 	/* error for Initiate service	*/
          _mmsdec_rslt->err_code = ERR_BAD_VALUE;
          break;

        case MMSCNCLREQ : 	/* request for Conclude service 	*/
          _mmsdec_rslt->err_code = CONREQ_UNSPECIFIED;
          break;

        case MMSCNCLRESP :	/* response for Conclude service	*/
          _mmsdec_rslt->err_code = CONRESP_UNSPECIFIED;
          break;

        case MMSCNCLERR : 	/* error for Conclude service	*/
          _mmsdec_rslt->err_code = CONERR_UNSPECIFIED;
          break;

        default:
          _mmsdec_rslt->err_code = PDU_INVALID;
          break;
        }
      }
    }
  }

/************************************************************************/
/*			mmsdec_assign_rid 				*/
/* Assign a reserved invoke ID for the given service on this channel	*/
/* and store it in the _mmsdec_rslt structure.  Also, set the opcode for */
/* the specified service, which must be either Initiate or Conclude	*/
/* (or their error responses).						*/
/************************************************************************/

#ifndef MMS_LITE

static ST_VOID mmsdec_assign_rid (ST_INT opcode)
  {
  					/* assign reserved invoke id	*/
  if (opcode == MMSOP_INITIATE)		
    _mmsdec_rslt->id = INIT_INVOKE_ID + _mmsdechan;	/* for Initiate	*/
  else					
    _mmsdec_rslt->id = CONCL_INVOKE_ID + _mmsdechan;	/* for Conclude	*/

  _mmsdec_rslt->op = opcode;		/* save the opcode  		*/
  _mmsdec_rslt->dec_level = 3;		/* opcode & invoke ID are valid	*/
  }

#endif

/************************************************************************/
/************************************************************************/
/*			_m_get_dec_buf 					*/
/************************************************************************/

ST_VOID *_m_get_dec_buf (ASN1_DEC_CTXT *aCtx, ST_UINT size)
  {
  _mms_dec_info = (*m_calloc_os_fun)(1, size);
  aCtx->asn1r_err_fun = _mms_dec_buf_free;
  _mms_dec_info_pres = SD_TRUE;
  aCtx->asn1r_decode_method = ASN1_TAG_METHOD;		/* select TAG method for get_mms_modifer*/
  return (_mms_dec_info);
  }
