/************************************************************************/
/* SISCO SOFTWARE MODULE HEADER *****************************************/
/************************************************************************/
/*   (c) Copyright Systems Integration Specialists Company, Inc.,	*/
/*      	  1986 - 2001, All Rights Reserved.		        */
/*									*/
/*		    PROPRIETARY AND CONFIDENTIAL			*/
/*									*/
/* MODULE NAME : mmsinit.c						*/
/* PRODUCT(S)  : MMSEASE, MMSEASE-LITE					*/
/*									*/
/* MODULE DESCRIPTION : 						*/
/*	This module contains the common functions to decode and encode	*/
/*	an initiate operation.	Note that the initiate request and the	*/
/*	initiate response are the same. 				*/
/*									*/
/* GLOBAL FUNCTIONS DEFINED IN THIS MODULE :				*/
/*									*/
/* MODIFICATION LOG :							*/
/*  Date     Who   Rev			Comments			*/
/* --------  ---  ------   -------------------------------------------	*/
/* 03/31/03  JRB     06    Support MMS Version2.			*/
/*			   asn1r_get_bitstr: add max_bits arg.		*/
/*			   If received bitstr too long, truncate.	*/
/*			   If received bitstr too short, set missing bits=0*/
/* 12/20/01  JRB     05    Converted to use ASN1R (re-entrant ASN1)	*/
/* 09/13/99  MDE     04    Added SD_CONST modifiers			*/
/* 03/23/99  MDE     03    Changes to decode buffer allocation scheme	*/
/* 12/10/97  MDE     02    Chaned max_nest to ST_INT8			*/
/* 07/03/97  MDE     01    Op-specific info buffer handling changes	*/
/* 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_pcon.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

#define MMS_V0_PARAM_SUPP_BITS	7	/* DIS	*/
#define MMS_V1_PARAM_SUPP_BITS	11
#define MMS_V1_SERV_SUPP_BITS	85
/************************************************************************/

static  INIT_INFO	*info;

#ifdef CS_SUPPORT
static CS_INIT_INFO *cs_info;
static ST_UCHAR *init_detail_start;
#endif

static ST_BOOLEAN  request;	/* req/resp flag			*/
static ST_RET val_err;	   	/* asn1_set_dec_err code for bad value	*/


/************************************************************************/
/* static functions in this module					*/
static ST_VOID get_maxseg (ASN1_DEC_CTXT *aCtx);
static ST_VOID get_maxreq_calling (ASN1_DEC_CTXT *aCtx);
static ST_VOID get_maxreq_called (ASN1_DEC_CTXT *aCtx);
static ST_VOID get_max_nest (ASN1_DEC_CTXT *aCtx);
static ST_VOID get_detail_seq (ASN1_DEC_CTXT *aCtx);
static ST_VOID get_version (ASN1_DEC_CTXT *aCtx);
static ST_VOID get_param_prim (ASN1_DEC_CTXT *aCtx);
static ST_VOID get_param_cstr (ASN1_DEC_CTXT *aCtx);
static ST_VOID get_param_done (ASN1_DEC_CTXT *aCtx);
static ST_VOID get_services_cstr (ASN1_DEC_CTXT *aCtx);
static ST_VOID get_services_prim (ASN1_DEC_CTXT *aCtx);
static ST_VOID get_services_done (ASN1_DEC_CTXT *aCtx);
static ST_VOID mms_detail_cstr_done (ASN1_DEC_CTXT *aCtx);
static ST_VOID init_seq_done (ASN1_DEC_CTXT *aCtx);

/************************************************************************/
/************************************************************************/
/*			_mms_init_dec					*/
/* This function is called from mmsdec.c when an initiate (req or resp)	*/
/* is being decoded. We are in the InitiatePDU sequence.		*/
/* Allocate an operation specific structure and set up to get either	*/
/* the segment size or the max outstanding calling			*/
/************************************************************************/

ST_VOID _mms_init_dec (ASN1_DEC_CTXT *aCtx, ST_BOOLEAN req)
  {
  request = req;			/* save the request flag	*/
  if (request)
    val_err = REQ_BAD_VALUE;
  else
    val_err = RESP_BAD_VALUE;

/* Create an init_info structure data area to put the decoded info into.*/


  /* CRITICAL: "info" struct must be calloc'd, so if received bitstrings
   *           are too short, the missing bits=0.
   */
#ifdef CS_SUPPORT
  if (_mmsdec_ctxt == MMS_PCI)
    info = (INIT_INFO *) _m_get_dec_buf (aCtx, sizeof (INIT_INFO));
  else
    cs_info = (CS_INIT_INFO *) _m_get_dec_buf (aCtx, sizeof (CS_INIT_INFO));
#else
    info = (INIT_INFO *) _m_get_dec_buf (aCtx, sizeof (INIT_INFO));
#endif

  ASN1R_TAG_ADD (aCtx, CTX,0,get_maxseg);			/* max seg size (opt)	*/
  ASN1R_TAG_ADD (aCtx, CTX,1,get_maxreq_calling);		/* max outstdg calling	*/
  }


/************************************************************************/
/*			get_maxseg					*/
/* The max_msgsegsize parameter has been encountered.  Read its value.	*/
/************************************************************************/

static ST_VOID get_maxseg (ASN1_DEC_CTXT *aCtx)
  {
ST_INT32 local_detail;

  MLOG_CDEC0 ("get_maxseg");

  if (asn1r_get_i32 (aCtx, &local_detail) || local_detail < 0)
    {
    if (request)
      asn1r_set_dec_err (aCtx, REQ_INVAL_ARG);
    else
      asn1r_set_dec_err (aCtx, RESP_INVAL_RSLT);
    }
  else
    {
#ifdef CS_SUPPORT
    if (_mmsdec_ctxt == MMS_PCI)
      {
      info->max_segsize_pres = SD_TRUE;  	/* set present flag for segsize	*/
      info->max_segsize = local_detail;
      }
    else
      {
      cs_info->local_detail_pres = SD_TRUE;  	/* set present flag	*/
      cs_info->local_detail = local_detail;
      }
#else
    info->max_segsize_pres = SD_TRUE;  	/* set present flag for segsize	*/
    info->max_segsize = local_detail;
#endif
    ASN1R_TAG_ADD (aCtx, CTX,1,get_maxreq_calling);
    }   
  }

/************************************************************************/
/*			get_maxreq_calling				*/
/* proposed/negiotiated max outstanding calling encountered		*/
/************************************************************************/

static ST_VOID get_maxreq_calling (ASN1_DEC_CTXT *aCtx)
  {
ST_INT16 maxreq_calling;

  MLOG_CDEC0 ("get_maxreq_calling");

/* Get the value of the maxreq_calling parameter.			*/

  if (asn1r_get_i16 (aCtx, &maxreq_calling))
    asn1r_set_dec_err (aCtx, val_err);

#ifdef CS_SUPPORT
  if (_mmsdec_ctxt == MMS_PCI)
    info->maxreq_calling = maxreq_calling;
  else
    cs_info->maxreq_calling = maxreq_calling;
#else
  info->maxreq_calling = maxreq_calling;
#endif

/* The next data element is the maxreq_called parameter.		*/

  ASN1R_TAG_ADD (aCtx, CTX,2,get_maxreq_called);
  }

/************************************************************************/
/*			get_maxreq_called				*/
/* proposed/negiotiated max outstanding called encountered		*/
/************************************************************************/

static ST_VOID get_maxreq_called (ASN1_DEC_CTXT *aCtx)
  {
ST_INT16 maxreq_called;

  MLOG_CDEC0 ("get_maxreq_called");

/* Get the value of the maxreq_called parameter.				*/

  if (asn1r_get_i16 (aCtx, &maxreq_called))
    asn1r_set_dec_err (aCtx, val_err);

#ifdef CS_SUPPORT
  if (_mmsdec_ctxt == MMS_PCI)
    info->maxreq_called = maxreq_called;
  else
    cs_info->maxreq_called = maxreq_called;
#else
  info->maxreq_called = maxreq_called;
#endif

/* The next data element is either the max_nest parameter (optional) or */
/* the initiate detail sequence	or the CS detail.			*/
/* All are optional, so we could be done				*/

  aCtx->asn1r_c_done_fun[aCtx->asn1r_msg_level] = init_seq_done;
  ASN1R_TAG_ADD (aCtx, CTX,3,get_max_nest);
  ASN1R_TAG_ADD (aCtx, CTX|CONSTR,4,get_detail_seq);
  }

/************************************************************************/
/*			get_max_nest					*/
/* The max_nest parameter has been encountered.  Read its value.	*/
/************************************************************************/

static ST_VOID get_max_nest (ASN1_DEC_CTXT *aCtx)
  {
ST_INT8 max_nest;

  MLOG_CDEC0 ("get_max_nest");

/* Get the value of the max_nest parameter.				*/

  if (asn1r_get_i8 (aCtx, &max_nest))
    asn1r_set_dec_err (aCtx, val_err);
  else
    {
#ifdef CS_SUPPORT
    if (_mmsdec_ctxt == MMS_PCI)
      {
      info->max_nest_pres = SD_TRUE;
      info->max_nest = max_nest;
      }
    else
      {
      cs_info->max_nest_pres = SD_TRUE;
      cs_info->max_nest = max_nest;
		/* Initiate detail sequence may be next, or CS detail	*/
      init_detail_start = aCtx->asn1r_field_ptr;
      }
#else
    info->max_nest_pres = SD_TRUE;
    info->max_nest = max_nest;
#endif
    }

  ASN1R_TAG_ADD (aCtx, CTX|CONSTR,4,get_detail_seq);
  }

/************************************************************************/
/*			get_detail_seq					*/
/* The mms init detail sequence is now starting, first is version #	*/
/************************************************************************/

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

/* Check if decoding an MMS CORE Initiate or a CS Initiate		*/

#ifdef CS_SUPPORT
  if (_mmsdec_ctxt == MMS_PCI)
    {
    info->mms_detail_pres = SD_TRUE;	/* set detail present flag	*/
    ASN1R_TAG_ADD (aCtx, CTX,0,get_version);	/* Next comes version		*/
    }
  else		/* Companion Standard Initiate, accept sequence		*/
    asn1r_parse_cstr_contents (aCtx, mms_detail_cstr_done);
#else
  info->mms_detail_pres = SD_TRUE;		/* set detail present flag	*/
  ASN1R_TAG_ADD (aCtx, CTX,0,get_version);		/* Next comes version		*/
#endif
  }

/************************************************************************/
/*			get_version					*/
/* The mms init detail sequence is now starting, this is version #	*/
/************************************************************************/

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

/* Get the value of the max_nest parameter.				*/

  if (asn1r_get_i16 (aCtx, &info->version) || info->version <0)
    asn1r_set_dec_err (aCtx, val_err);

/* Next is parameter support options, a 6 bit bitstring (prim or cstr)	*/

  ASN1R_TAG_ADD (aCtx, CTX,1,get_param_prim);
  ASN1R_TAG_ADD (aCtx, CTX|CONSTR,1,get_param_cstr);
  }


/************************************************************************/
/*			get_param_prim					*/
/* The parameter support BS has been encountered as a primitive data	*/
/* element.  Read its value.						*/
/************************************************************************/

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

  /* NOTE: decode only the bits we recognize.
   * For version0 (DIS) should get 7 bits.
   * For version1 (IS) should get 11 bits.
   * For version2 and higher, may get more than 11, but we ignore the 
   * higher bits because we don't support them.
   */
  if (!asn1r_get_bitstr (aCtx, info->param_supp, MMS_V1_PARAM_SUPP_BITS))
    get_param_done (aCtx);	  	/* set up for the next data element	*/
  else
    asn1r_set_dec_err (aCtx, val_err);
  }

/************************************************************************/
/*			get_param_cstr					*/
/* The parameter suport has been encountered as a constructor data	*/
/* element.  Read its value.						*/
/************************************************************************/

static ST_VOID get_param_cstr (ASN1_DEC_CTXT *aCtx)
  {

  MLOG_CDEC0 ("get_param_cstr");

/* Set up to call this function when done with constructor bitstring.	*/

  aCtx->asn1r_c_done_fun[aCtx->asn1r_msg_level] = get_param_done;

/* Get the value of the parameter support.  For constructor		*/
/* bitstrings, this requires returning and letting the ASN.1 tools	*/
/* parse it to any nesting level.					*/

  asn1r_get_bitstr_cstr (aCtx, MMS_V1_PARAM_SUPP_BITS,info->param_supp);
  }

/************************************************************************/
/*			get_param_done					*/
/* Function called after the supported parameter has been parsed.	*/
/************************************************************************/

static ST_VOID get_param_done (ASN1_DEC_CTXT *aCtx)
  {

  MLOG_CDEC0 ("get_param_done");

  ASN1R_TAG_ADD (aCtx, CTX,2,get_services_prim);
  ASN1R_TAG_ADD (aCtx, CTX|CONSTR,2,get_services_cstr);
  }

/************************************************************************/
/*			get_services_prim				*/
/* The services support BS has been encountered as a primitive data	*/
/* element.  Read its value.						*/
/************************************************************************/

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

  /* NOTE: decode only the bits we recognize.
   * For version0 (DIS) and version1 (IS) should get 85 bits.
   * For version2 and higher, may get more than 85, but we ignore the 
   * higher bits because we don't support them.
   */
  if (!asn1r_get_bitstr (aCtx, info->serv_supp, MMS_V1_SERV_SUPP_BITS))
    get_services_done (aCtx);	 /* set up for the next data element	*/
  else
    asn1r_set_dec_err (aCtx, val_err);
  }

/************************************************************************/
/*			get_services_cstr				*/
/* The service suport BS has been encountered as a constructor data	*/
/* element.  Read its value.						*/
/************************************************************************/

static ST_VOID get_services_cstr (ASN1_DEC_CTXT *aCtx)
  {

  MLOG_CDEC0 ("get_services_cstr");

/* Set up to call this function when done with constructor bitstring.	*/

  aCtx->asn1r_c_done_fun[aCtx->asn1r_msg_level] = get_services_done;

/* Get the value of the service support.  For constructor		*/
/* bitstrings, this requires returning and letting the ASN.1 tools	*/
/* parse it to any nesting level.					*/

  asn1r_get_bitstr_cstr (aCtx, MMS_V1_SERV_SUPP_BITS,info->serv_supp);
  }

/************************************************************************/
/*			get_services_done				*/
/* Function called when entire mms_detail constructor is done		*/
/************************************************************************/

static ST_VOID get_services_done (ASN1_DEC_CTXT *aCtx)
  {

  MLOG_CDEC0 ("get_services_done");

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

/************************************************************************/
/*			mms_detail_cstr_done				*/
/* Function called when entire mms_detail constructor is done		*/
/************************************************************************/

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

#ifdef CS_SUPPORT
  if (_mmsdec_ctxt != MMS_PCI)
    {
    cs_info->init_detail_len = aCtx->asn1r_field_ptr - init_detail_start;
    cs_info->init_detail = init_detail_start;
    }
#endif
  }

/************************************************************************/
/*			init_seq_done					*/
/* The parse of the Initiate PDU is complete, either with or without	*/
/* CS detail. Just set decode done flag.				*/
/************************************************************************/

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

  aCtx->asn1r_decode_done = SD_TRUE;
  aCtx->asn1r_decode_done_fun = _mms_dec_done_ok;
  }


/************************************************************************/
/************************************************************************/
/*			_ms_mk_init 					*/
/* Function to construct an Initiate request or response PDU in the	*/
/* selected buffer.  Pass a pointer to the end of the build buffer, and */
/* a pointer to a structure of type init_info that contains the build	*/
/* control and data information.  Returns a pointer to the first byte	*/
/* of the encoded message.						*/
/************************************************************************/

ST_UCHAR *_ms_mk_init (ASN1_ENC_CTXT *aCtx, ST_UCHAR *buf_ptr, ST_INT buf_len, 
		INIT_INFO *info_ptr, ST_INT pdutype,
		ST_BOOLEAN cs_init)
  {
#ifdef DEBUG_SISCO
ST_UCHAR  *msg_start;			/* These variables used for	*/
ST_INT	msg_len;			/* debug print only.		*/
#endif
#ifdef CS_SUPPORT
CS_INIT_INFO *cs_init_ptr; 	   
#endif

  asn1r_strt_asn1_bld (aCtx, buf_ptr,buf_len);	/* Initialize the ASN.1 tools	*/

#ifdef CS_SUPPORT
  if (cs_init)		/* COMPANION STANDARD INITIATE PDU		*/
    {
    cs_init_ptr = (CS_INIT_INFO *) info_ptr;

/* write the CS init detail constructor					*/
    asn1r_strt_constr (aCtx);			/* start the CTX 5 cstr		*/  
    asn1r_wr_octstr (aCtx, cs_init_ptr->init_detail,cs_init_ptr->init_detail_len);
    asn1r_fin_constr (aCtx, 4,CTX,DEF);		/* close the CS detail cstr	*/

/* maximum data nesting (optional)					*/
    if (cs_init_ptr->max_nest_pres)     	/* if desired to send - 	*/
      {
      asn1r_wr_i8 (aCtx, (ST_CHAR)cs_init_ptr->max_nest);
      asn1r_fin_prim (aCtx, 3,CTX);			/* context-specific, tag 2	*/
      }

/* max outstanding requests from called MMS-user			*/
    asn1r_wr_i16 (aCtx, cs_init_ptr->maxreq_called);
    asn1r_fin_prim (aCtx, 2,CTX);			/* context-specific, tag 2	*/

/* max outstanding requests from calling MMS-user			*/
    asn1r_wr_i16 (aCtx, cs_init_ptr->maxreq_calling);
    asn1r_fin_prim (aCtx, 1,CTX);			/* context-specific, tag 1	*/

/* max segment size (optional)						*/
    if (cs_init_ptr->local_detail_pres) 	/* if enabled		 	*/
      {
      asn1r_wr_i32 (aCtx, cs_init_ptr->local_detail);
      asn1r_fin_prim (aCtx, 0,CTX);			/* context-specific, tag 0	*/
      }
    }
  else		/* MMS CORE INITIATE PDU				*/
    {
#endif
/* Now begin writing primitive data elements from back of PDU to front. */
  
/* write the mms init detail constructor, if enabled			*/
    if (info_ptr->mms_detail_pres)	   /* if we have MMS detail	*/
      {
      asn1r_strt_constr (aCtx);
      asn1r_wr_bitstr (aCtx, info_ptr->serv_supp,MMS_V1_SERV_SUPP_BITS);  /* services supported BS	*/
      asn1r_fin_prim (aCtx, 2,CTX);			   /* context-specific, tag 2	*/
  
/* parameters supported BS, write either 7 (DIS) or 11 (IS) bits	*/
      if (info_ptr->version == 0)	/* If DIS is being proposed	*/
        {
        asn1r_wr_bitstr (aCtx, info_ptr->param_supp,MMS_V0_PARAM_SUPP_BITS);
        asn1r_fin_prim (aCtx, 1,CTX);			
        }
      else				/* IS is being proposed		*/
        {
        asn1r_wr_bitstr (aCtx, info_ptr->param_supp,MMS_V1_PARAM_SUPP_BITS);
        asn1r_fin_prim (aCtx, 1,CTX);		/* context-specific, tag 1	*/
        }
  
      asn1r_wr_i16 (aCtx, info_ptr->version);	/* version number		*/
      asn1r_fin_prim (aCtx, 0,CTX);			/* context-specific, tag 0	*/

      asn1r_fin_constr (aCtx, 4,CTX,DEF);		/* close the MMS detail cstr	*/
      }    

/* Now write the core initiate parameters				*/

/* maximum data nesting (optional)					*/
    if (info_ptr->max_nest_pres)		/* if desired to send - 	*/
      {
      asn1r_wr_i8 (aCtx, (ST_CHAR)info_ptr->max_nest);
      asn1r_fin_prim (aCtx, 3,CTX);			/* context-specific, tag 2	*/
      }

/* max outstanding requests from called MMS-user			*/
    asn1r_wr_i16 (aCtx, info_ptr->maxreq_called);
    asn1r_fin_prim (aCtx, 2,CTX);			/* context-specific, tag 2	*/

/* max outstanding requests from calling MMS-user			*/
    asn1r_wr_i16 (aCtx, info_ptr->maxreq_calling);
    asn1r_fin_prim (aCtx, 1,CTX);			/* context-specific, tag 1	*/

/* max segment size (optional)						*/
    if (info_ptr->max_segsize_pres)		/* if enabled		 	*/
      {
      asn1r_wr_i32 (aCtx, info_ptr->max_segsize);
      asn1r_fin_prim (aCtx, 0,CTX);			/* context-specific, tag 0	*/
      }
#ifdef CS_SUPPORT
    }
#endif

/* Close the PDU.  Building of either the request or response message	*/
/* is complete. 							*/

#ifdef DEBUG_SISCO
  asn1r_fin_constr (aCtx, (ST_UINT16) pdutype,CTX,DEF);
  msg_start = aCtx->asn1r_field_ptr + 1;
  msg_len =  (buf_ptr + buf_len) - msg_start;

  MLOG_ENC2 ("INITIATE %s built, len = %d",
             pdutype == MMSINITREQ ? "request" : "response",
             msg_len);
  MLOG_ENCH (msg_len,msg_start);
  MLOG_PAUSEENC (NULL);

  return (msg_start);
#else
  asn1r_fin_constr (aCtx, (ST_UINT16) pdutype,CTX,DEF);
  return (aCtx->asn1r_field_ptr + 1);
#endif
  }
