/************************************************************************/
/* SISCO SOFTWARE MODULE HEADER *****************************************/
/************************************************************************/
/*   (c) Copyright Systems Integration Specialists Company, Inc.,	*/
/*      	1994 - 2005, All Rights Reserved		        */
/*									*/
/* MODULE NAME : mvl_serv.c    						*/
/* PRODUCT(S)  : MMSEASE-LITE						*/
/*									*/
/* MODULE DESCRIPTION : 						*/
/*									*/
/* GLOBAL FUNCTIONS DEFINED IN THIS MODULE :				*/
/*	NONE								*/
/*									*/
/* MODIFICATION LOG :							*/
/*  Date     Who   Rev			Comments			*/
/* --------  ---  ------   -------------------------------------------	*/
/* 06/24/08  MDE     63    Added _mvl_persist_aa_objs			*/
/* 05/20/08  JRB     62    Fix handling of MMSCNCLERR (Conclude ERROR).	*/
/* 04/22/08  JRB     61    Move "active" chk inside semaphore lock.	*/
/*			   Del unused MVLULOG_TIMING.			*/
/* 03/05/08  JRB     60    Call _mvl_clear_aa_objs in ..net_req_done	*/
/* 10/04/07  MDE     59    Tweaked LOGCFG_VALUE_GROUP/LOGCFGX_VALUE_MAP	*/
/* 04/09/07  MDE     58    Enhanced filtered logging 			*/
/* 10/30/06  JRB     57	   Del _mvl_curr_net_info, _mvl_curr_usr_ind_ctrl.*/
/* 08/03/07  NAV     56    mvl_msg_rcvd - return after sending reject   */
/* 01/30/06  GLB     55    Integrated porting changes for VMS           */
/* 09/08/05  JRB     54    _mvl_send_reject: set rej_info->pdu_type,	*/
/*			   send right class/code on request decode err.	*/
/*			   Remove duplicate log msg.			*/
/* 07/13/05  MDE     52    Improved reject handlng, add u_mvl_reject_ind*/
/* 08/08/05  JRB     52    Init mvl_debug_sel = MVLLOG_ERR.     	*/
/* 06/02/05  JRB     51    Fix _mvl_send_reject for INVALID-PDU.	*/
/* 05/24/05  EJV     50    Add mvlLogMaskMapCtrl for parsing logcfg.xml	*/
/* 09/07/04  JRB     49    _mvl_get_req_ctrl: ret NULL if net_info==NULL*/
/* 07/28/03  MDE     48    Yet another tweak to mvl_ureq_bufs_avail	*/
/* 05/05/03  MDE     47    Yet another tweak to mvl_ureq_bufs_avail	*/
/* 05/01/03  MDE     46    Added high water logging to (u)req_bufs_avail*/
/* 04/28/03  JRB     45    Fix computations in mvl_(u)req_bufs_avail.	*/
/* 07/09/02  JRB     44    Add mvl_(u)req_bufs_avail functions.		*/
/* 07/09/02  MDE     43    Add maxpend_ind support			*/
/* 05/21/02  MDE     42    Check PDU len before decoding		*/
/* 05/13/02  MDE     41    Added 'skip' arg to _mvl_set_net_req_done 	*/
/* 05/01/02  MDE     40    Fixed conclude logging			*/
/* 04/05/02  MDE     39    Added MLOG logging				*/
/* 03/25/02  MDE     38	   Moved error/reject elements outside union	*/
/* 11/07/01  JRB     37    mvl_wait_req_done return "req->result".	*/
/* 09/21/01  JRB     36    Alloc global bufs only once at startup.	*/
/* 07/31/01  MDE     35    Added mvl_conn_filtered_logging		*/
/* 02/22/01  GLB     34    Added: MVLULOG_DEBUG, MVLULOG_TIMING         */
/* 11/20/00  MDE     33    Fixed lockup - abort with requests out	*/
/* 10/25/00  JRB     32    Del global link list of MVL_REQ_PEND. Add one*/
/*			   for each conn. Save list ptr in MVL_NET_INFO.*/
/*			   Del _mvl_free_req_pend function.		*/
/* 10/25/00  JRB     31    Del ind & conf funct ptrs. Call directly.	*/
/*			   Mv mvl_ind_rcvd, mvl_conf_rcvd to mvlop_en.c	*/
/*			   so "conditional compilation" can be used.	*/
/* 05/15/00  MDE     30    Added statistics				*/
/* 05/12/00  RKR     29    Worked on mvl_send_reject            	*/
/* 04/14/00  JRB     28    Lint cleanup.				*/
/* 03/08/00  JRB     27    Del #ifdef MVL_REQ_BUF_ENABLED: not used.	*/
/*			   mvl_free_req_ctrl chk result before freeing.	*/
/* 01/21/00  MDE     26    Now use MEM_SMEM for dynamic memory		*/
/* 11/24/99  MDE     25    Del indCtrl from link list for info reports	*/
/* 11/19/99  NAV     24	   Add #ifdef MVL_REQ_BUF_ENABLED		*/
/* 10/20/99  NAV     23    Additions for maxpend_req support		*/
/* 11/03/99  RKR     22    Changed reject and error confirm results	*/
/* 11/02/99  RKR     21    Fixed a leak when a reject is received	*/
/* 10/28/99  RKR     20    Added mvl_clr_ind_que 			*/
/* 10/06/99  GLB     19    Added changes to accomodate file rename      */
/* 09/13/99  MDE     18    Added SD_CONST modifiers			*/
/* 04/01/99  MDE     17    Changes to decode buffer allocation scheme   */
/* 03/03/99  MDE     16    Changed 'mvl_max_req_pend' to 10000		*/
/* 12/08/98  MDE     15    Cleanup on request control handling		*/
/* 11/16/98  MDE     14    Renamed internal functions (prefix '_')	*/
/* 11/16/98  MDE     13    Cleanup, conclude changes, removed simple	*/
/*			   request control, renamed concat_filename	*/
/* 10/08/98  MDE     12    Migrated to updated SLOG interface		*/
/* 10/01/98  DWL     11    Added get domain attributes client		*/
/*			   Added get name list client			*/
/*			   Added get status client			*/
/* 09/21/98  MDE     10    Minor lint cleanup				*/
/* 06/15/98  MDE     09    Changes to allow compile under C++		*/
/* 05/13/98  JRB     08    Set req->result=CNF_REJ_ERR in mvl_reject_rcvd*/
/*			   Add _mplas_err_resp & _mvl_concat_filename.	*/
/* 04/21/98  MDE     07    Added service logging			*/
/* 04/15/98  JRB     06    mvl_conf_rcvd: do all common processing,	*/
/*			     pass req_pend to all "ml_*_conf_proc".	*/
/*			   Add client support for more services.	*/
/*			   Free comm_event, etc. from mvl_free_req_ctrl */
/*			   Init "result" in _mvl_get_req_ctrl.		*/
/*			   Add _mvl_conf_reject, _mvl_ind_reject.		*/
/* 02/10/98  MDE     05    Added get domain attributes server		*/
/* 12/22/97  MDE     04    Changed 'mvl_comm_serve' to report activity	*/
/* 12/12/97  MDE     03    _mvl_send_reject now returns ST_VOID		*/
/* 12/10/97  MDE     02    Added JREAD/JINIT				*/
/* 09/10/97  MDE     01    MMS-LITE V4.0 Release			*/
/************************************************************************/

#include "glbtypes.h"
#include "sysincs.h"
#include "glbsem.h"
#include "mmsdefs.h"
#include "mms_err.h"
#include "mvl_defs.h"
#include "mvl_log.h"
#include "mloguser.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

ST_UINT mvl_debug_sel = MVLLOG_ERR;
#ifdef DEBUG_SISCO
SD_CONST ST_CHAR *SD_CONST _mvllog_err_logstr = "MVLLOG_ERR";
SD_CONST ST_CHAR *SD_CONST _mvllog_nerr_logstr = "MVLLOG_NERR";
SD_CONST ST_CHAR *SD_CONST _mvllog_acse_logstr = "MVLLOG_ACSE";
SD_CONST ST_CHAR *SD_CONST _mvllog_acsedata_logstr = "MVLLOG_ACSEDATA";
SD_CONST ST_CHAR *SD_CONST _mvlulog_flow_logstr = "MVLULOG_FLOW";
SD_CONST ST_CHAR *SD_CONST _mvlulog_debug_logstr = "MVLULOG_DEBUG";

LOGCFGX_VALUE_MAP mvlLogMaskMaps[] =
  {
    {"MVLLOG_ERR",	MVLLOG_ERR,	  &mvl_debug_sel, _LOGCFG_DATATYPE_UINT_MASK, NULL, "Error"},
    {"MVLLOG_NERR",	MVLLOG_NERR,	  &mvl_debug_sel, _LOGCFG_DATATYPE_UINT_MASK, NULL, "Notice"},
    {"MVLLOG_ACSE",	MVLLOG_ACSE,	  &mvl_debug_sel, _LOGCFG_DATATYPE_UINT_MASK, NULL, "ACSE"},
    {"MVLLOG_ACSEDATA",	MVLLOG_ACSEDATA,  &mvl_debug_sel, _LOGCFG_DATATYPE_UINT_MASK, NULL, "ACSEData"},
    {"MVLULOG_FLOW",	MVLULOG_FLOW,	  &mvl_debug_sel, _LOGCFG_DATATYPE_UINT_MASK, NULL, "Flow"},
    {"MVLULOG_DEBUG",	MVLULOG_DEBUG,	  &mvl_debug_sel, _LOGCFG_DATATYPE_UINT_MASK, NULL, "Debug"}
  };

LOGCFG_VALUE_GROUP mvlLogMaskMapCtrl =
  {
  {NULL,NULL},
  "MvlLogMasks",	  /* Parent Tag */
  sizeof(mvlLogMaskMaps)/sizeof(LOGCFGX_VALUE_MAP),
  mvlLogMaskMaps
  };
#endif /* DEBUG_SISCO */

ST_BOOLEAN mvl_conn_filtered_logging;

ST_BOOLEAN _mvl_persist_aa_objs;

/************************************************************************/
/* User defined function, called repetitively by MVL client functions 	*/
/* while waiting for confirm. If this function returns SD_TRUE, the	*/
/* request will timeout and return SD_FAILURE.				*/

ST_BOOLEAN (*u_mvl_check_timeout)(ST_VOID);

/************************************************************************/
/* These functions are used internally by mvl_comm_serve ()		*/

static ST_VOID mvl_msg_rcvd (MVL_COMM_EVENT *event);
static ST_VOID mvl_error_rcvd (MVL_COMM_EVENT *event);
static ST_VOID mvl_reject_rcvd (MVL_COMM_EVENT *event);

static ST_VOID _mvl_send_reject_net_info (MVL_NET_INFO *net_info, 
				          REJECT_RESP_INFO *rej_info);

/************************************************************************/
/************************************************************************/
/*			mvl_comm_serve					*/
/* General MMS communication service					*/
/************************************************************************/

ST_BOOLEAN _mvl_comm_serve_active;

ST_BOOLEAN mvl_comm_serve ()
  {
MVL_COMM_EVENT *event;
ST_BOOLEAN activityFlag;


  S_LOCK_COMMON_RESOURCES ();
  /* Check for re-entrancy. This must be inside the lock so only one	*/
  /* thread is accessing the global "_mvl_comm_serve_active".		*/
  if (_mvl_comm_serve_active)
    {
    MVL_LOG_ERR0 ("Error: mvl_comm_serve re-entered");
    S_UNLOCK_COMMON_RESOURCES ();	/* unlock sem before return	*/
    return (SD_FALSE);
    }
  _mvl_comm_serve_active = SD_TRUE;
/* Call the network service routine, see if it has anything for us	*/
/* to deal with								*/
  activityFlag = _mvl_net_service (&event);
  if (event)
    {
    switch (event->event_type)
      {
      case MMS_MSG_RCVD :
        mvl_msg_rcvd (event);
      break;

      default :
        _mvl_free_comm_event (event);	/* All done with this event	*/
      break;
      }
    }
  _mvl_comm_serve_active = SD_FALSE;
  S_UNLOCK_COMMON_RESOURCES ();
  return (activityFlag);
  }

/************************************************************************/
/*			mvl_msg_rcvd					*/
/* This function is called by mvl_comm_serve when a MMS message	        */
/* has been received.							*/
/************************************************************************/

static ST_VOID mvl_msg_rcvd (MVL_COMM_EVENT *event)
  {
MMSDEC_INFO *rslt;
MVL_REQ_PEND *req_pend;
ST_INT pdu_len;

  rslt = &event->u.mms.dec_rslt;
  pdu_len = event->u.mms.pdu_len;


/* Verify that the message is less than or equal to negiotiated		*/
  if (pdu_len > event->net_info->max_pdu_size)
    {
    MVL_LOG_NERR2 ("MMS PDU too long: %d rxd, %d max", 
    				pdu_len, event->net_info->max_pdu_size);
	printf("MMS PDU too long: %d rxd, %d max", 
		pdu_len, event->net_info->max_pdu_size);
    _mvl_send_reject (event);
    return;
    }

/* some kind of message was received, need to decode and act on it	*/
  _ms_mms_decode (event->u.mms.pdu, pdu_len, rslt);
  rslt->data_ptr = _mms_dec_info;

  if (rslt->err_code == NO_DECODE_ERR)
    {
    switch (rslt->type)
      {
      case MMSREQ :
      /* We can log here if so desired */
        if ((mms_debug_sel & MMS_LOG_IND) && ml_log_ind_info_fun != NULL)
	  {
          if (mvl_conn_filtered_logging == SD_FALSE || event->net_info->log_enable)
            (*ml_log_ind_info_fun) (rslt->id, rslt->op, rslt->data_ptr);
	  }
        mvl_ind_rcvd (event);
      break;
      case MMSUNREQ :
      /* We can log here if so desired */
        if ((mms_debug_sel & MMS_LOG_IND) && ml_log_unsol_ind_info_fun != NULL)
	  {
          if (mvl_conn_filtered_logging == SD_FALSE || event->net_info->log_enable)
            (*ml_log_unsol_ind_info_fun) (rslt->op, rslt->data_ptr);
	  }
        mvl_ind_rcvd (event);
      break;
    
      case MMSRESP :
      /* We can log here if so desired */
        if ((mms_debug_sel & MMS_LOG_CONF) && ml_log_conf_info_fun != NULL)
	  {
          if (mvl_conn_filtered_logging == SD_FALSE || event->net_info->log_enable)
            (*ml_log_conf_info_fun) (rslt->id, rslt->op, rslt->data_ptr);
	  }
        mvl_conf_rcvd (event);
      break;

      
      case MMSREJECT :
      /* We can log here if so desired */
        if ((mms_debug_sel & MMS_LOG_CONF) && ml_log_reject_recv_fun != NULL)
	  {
          if (mvl_conn_filtered_logging == SD_FALSE || event->net_info->log_enable)
            (*ml_log_reject_recv_fun) (rslt->data_ptr);
	  }
        mvl_reject_rcvd (event);
      break;

      case MMSERROR : 
        if ((mms_debug_sel & MMS_LOG_CONF) && ml_log_error_conf_fun != NULL)
	  {
          if (mvl_conn_filtered_logging == SD_FALSE || event->net_info->log_enable)
            (*ml_log_error_conf_fun) (rslt->id, rslt->data_ptr);
	  }
        mvl_error_rcvd (event);
      break;

      case MMSCNCLERR :
        if ((mms_debug_sel & MMS_LOG_CONF) && ml_log_error_conf_fun != NULL)
	  {
          if (mvl_conn_filtered_logging == SD_FALSE || event->net_info->log_enable)
            (*ml_log_error_conf_fun) (rslt->id, rslt->data_ptr);
	  }
        /* Find Conclude request sent on this connection & mark it as failed.*/
        req_pend = _mvl_find_req_ctrl (event->net_info,
                                       MMSOP_CONCLUDE, (ST_UINT32) -1);
        if (!req_pend)
          {	/* this should almost never fail.	*/
          MVL_LOG_ERR0 ("Negative Conclude Confirm received: Error finding request control");
          _mvl_free_comm_event (event);	/* All done with this event	*/
          return;
          }
        req_pend->done = SD_TRUE;
        req_pend->result = SD_FAILURE;
      break;

      case MMSCNCLRESP :
      /* We can log here if so desired */
        if ((mms_debug_sel & MMS_LOG_CONF) && ml_log_conf_info_fun != NULL)
	  {
          if (mvl_conn_filtered_logging == SD_FALSE || event->net_info->log_enable)
            (*ml_log_conf_info_fun) (rslt->id, MMSOP_CONCLUDE, NULL);
          } 
        req_pend = _mvl_find_req_ctrl (event->net_info,
                                       MMSOP_CONCLUDE, (ST_UINT32) -1);
        if (!req_pend)
          {
          MVL_LOG_NERR1 ("%s Confirmation : mvl_find_req_ctrl failed",
              mms_op_string [event->u.mms.dec_rslt.op]);
          _mvl_free_comm_event (event);	/* All done with this event	*/
          return;
          }
        req_pend->event = event;
        mvl_concl_conf_rcvd (req_pend);
      break;

      case MMSCNCLREQ	:
      /* We can log here if so desired */
        if ((mms_debug_sel & MMS_LOG_IND) && ml_log_ind_info_fun != NULL)
	  {
          if (mvl_conn_filtered_logging == SD_FALSE || event->net_info->log_enable)
            (*ml_log_ind_info_fun) (rslt->id, MMSOP_CONCLUDE, NULL);
	  }
        mvl_concl_ind_rcvd (event);
      break;
      
/* Unexpected PDU types */
      case MMSCANREQ :
      /* We can log here if so desired */
        if ((mms_debug_sel & MMS_LOG_IND) && ml_log_ind_info_fun != NULL)
          (*ml_log_ind_info_fun) (rslt->id, rslt->op, rslt->data_ptr);
        mvl_cancel_ind_rcvd (event);
      break;

      case MMSCANRESP	:
        MVL_LOG_NERR0 ("Unexpected MMS PDU type received: Cancel Response");
        _mvl_send_reject (event);
      break;

      case MMSCANERR :
        MVL_LOG_NERR0 ("Unexpected MMS PDU type received: Cancel Error");
        _mvl_send_reject (event);
      break;

      case MMSINITREQ	:
        MVL_LOG_NERR0 ("Unexpected MMS PDU type received: Initiate Request");
        _mvl_send_reject (event);
      break;

      case MMSINITRESP :
        MVL_LOG_NERR0 ("Unexpected MMS PDU type received: Initiate Response");
        _mvl_send_reject (event);
      break;

      case MMSINITERR	:
        MVL_LOG_NERR0 ("Unexpected MMS PDU type received: Initiate Error");
        _mvl_send_reject (event);
      break;

      case UNKNOWNTYPE :
        MVL_LOG_NERR0 ("Unexpected MMS PDU type received: Unknown");
        _mvl_send_reject (event);
      break;

      default :
        MVL_LOG_NERR1 ("Unexpected MMS PDU type received: %d", rslt->type);
        _mvl_send_reject (event);
      break;
      }   
    }
  else  /* Error in decoding, need to reject the message		*/
    {
    MVL_LOG_NERR0 ("MMS PDU decode error, sending reject");

  /* If this was a confirmed response, try to match the request */
    req_pend = NULL;
    if (rslt->dec_level >= 2 && rslt->type == MMSRESP)
      {
      req_pend = _mvl_find_req_ctrl (event->net_info, -1, rslt->id);
      if (req_pend != NULL)
        {
        req_pend->event = event; 
        req_pend->reject_info = event->u.mms.dec_rslt.data_ptr = 
            (REJECT_RESP_INFO *) chk_calloc (1, sizeof (REJECT_RESP_INFO));
        event->u.mms.dec_rslt.data_pres = SD_TRUE;

        req_pend->reject_info->detected_here = SD_TRUE;
        req_pend->reject_info->invoke_known = SD_TRUE;
        req_pend->reject_info->invoke = rslt->id;
        req_pend->reject_info->pdu_type = MMSRESP;
        req_pend->reject_info->rej_class = MMS_REJ_CLASS_CONFIRMED_RESPONSE_PDU;
        req_pend->reject_info->rej_code  = MMS_REJ_CODE_CONFIRMED_RESPONSE_PDU_OTHER; 

        _mvl_send_reject_net_info (event->net_info, req_pend->reject_info);

        _mvl_set_req_done (req_pend, MVL_ERR_CNF_REJ_ERR);

	}
      else
        {
        MVL_LOG_CNERR0 ("Could not match confirmed request");
        }
      }

    if (req_pend == NULL)
      _mvl_send_reject (event);
    }
  }

/************************************************************************/
ST_VOID mvl_clr_ind_que (MVL_NET_INFO *net_info)
  {
MVL_IND_PEND	 *pend_ind;
MVL_IND_PEND	 *next_ind;

/* look at all the pending indications attached to this MVL_NET_INFO	*/
/* unlink them, and deallocate them.					*/
  
  pend_ind = net_info->pend_ind;
  while(pend_ind!=NULL)
    {
    next_ind = (MVL_IND_PEND *) list_get_next(net_info->pend_ind,pend_ind);
    list_unlink(&(net_info->pend_ind),pend_ind);
    --net_info->numpend_ind;

    _mvl_free_comm_event (pend_ind->event);

    M_FREE (MSMEM_IND_CTRL, pend_ind);
    pend_ind = next_ind;
    }
  }

/************************************************************************/
/*			mvl_error_rcvd					*/
/************************************************************************/

static ST_VOID mvl_error_rcvd (MVL_COMM_EVENT *event)
  {
MVL_REQ_PEND *req;
ST_UINT32 id;
ST_INT op;

  ++mvl_mms_statistics.clientReqErr;
  event->net_info->numpend_req--;

  id = event->u.mms.dec_rslt.id;
  op = event->u.mms.dec_rslt.op;

  req = _mvl_find_req_ctrl (event->net_info, -1, id);
  if (!req)
    {
    MVL_LOG_NERR0 ("Error Received : mvl_find_req_ctrl failed");
    _mvl_free_comm_event (event);	/* All done with this event	*/
    return;
    }
  req->event = event;
  req->error_info = (ERR_INFO *) req->event->u.mms.dec_rslt.data_ptr;
   _mvl_set_req_done (req, MVL_ERR_CNF_ERR_OK);
  }

/************************************************************************/
/*			mvl_reject_rcvd					*/
/************************************************************************/

static ST_VOID mvl_reject_rcvd (MVL_COMM_EVENT *event)
  {
REJECT_RESP_INFO *info;
MVL_REQ_PEND *req;
ST_BOOLEAN freeCommEvent;
MVL_NET_INFO *netInfo;

  ++mvl_mms_statistics.rejectsRcvd;
  netInfo = event->net_info;
  netInfo->numpend_req--;
  freeCommEvent = SD_TRUE;

  info = (REJECT_RESP_INFO *) event->u.mms.dec_rslt.data_ptr;
  MVL_LOG_NERR2 ("Reject Received: class %d, code %d", 
  					info->rej_class, info->rej_code);

/* If the other node rejected our confirmed request, need to match it	*/
  if (info->rej_class == MMS_REJ_CLASS_CONFIRMED_REQUEST_PDU)
    {
    if (info->invoke_known)
      {
      req = _mvl_find_req_ctrl (netInfo, -1, info->invoke);
      if (!req)
        {
        MVL_LOG_CNERR0 ("Could not match confirmed request");
        return;
        }
      req->event = event; 
      req->reject_info = 
                 (REJECT_RESP_INFO *) req->event->u.mms.dec_rslt.data_ptr;
      _mvl_set_req_done (req, MVL_ERR_CNF_REJ_ERR);
      freeCommEvent = SD_FALSE;
      } 
    }

/* Let the user know about this */
  u_mvl_reject_ind (netInfo, info);

  if (freeCommEvent)
    _mvl_free_comm_event (event);	/* All done with this event	*/
  }

/************************************************************************/
/************************************************************************/
/* This table is used to select reject class given MMS pdu type		*/
/* PDU type is the index into this table    				*/
/************************************************************************/

SD_CONST ST_INT16 pdutype_to_reject_class[14] =
  {
  1,    /* confirmed request						*/
  2,    /* confirmed response						*/
  3,    /* confirmed error						*/
  4,    /* unconfirmed							*/
  5,    /* reject							*/
  6,    /* cancel request						*/
  7,    /* cancel response						*/
  8,    /* cancel error							*/
  0,    /* initiate req   - not used					*/
  0,    /* initiate resp  - not used					*/
  0,    /* initiate error - not used					*/
  9,    /* conclude request						*/
  10,   /* conclude response						*/
  11    /* conclude error						*/
  };

/************************************************************************/
/*			_mvl_ind_reject					*/
/* Send reject for Indication.						*/
/************************************************************************/

ST_VOID _mvl_ind_reject (MVL_IND_PEND *ind_pend)
  {
MVL_NET_INFO *net_info; 

  net_info = ind_pend->event->net_info;
  _mvl_send_reject (ind_pend->event);
  list_unlink(&(net_info->pend_ind),ind_pend);
  --net_info->numpend_ind;

  M_FREE (MSMEM_IND_CTRL, ind_pend);
  }

/************************************************************************/
/*			_mvl_conf_reject					*/
/* Send reject for Confirmation.					*/
/************************************************************************/

ST_VOID _mvl_conf_reject (MVL_REQ_PEND *req_pend)
  {
  _mvl_send_reject (req_pend->event);
  req_pend->event = NULL;	/* so mvl_free_req_ctrl doesn't try to	*/
				/* free it again.			*/
  req_pend->result = SD_FAILURE;/* Let user know it failed.		*/ 
  }

/************************************************************************/
/*			_mvl_send_reject					*/
/* This is a simple minded reject send function. If the PDU type isd	*/
/* known, send a reject of the appropriate class with code == OTHER.	*/
/* This is not always exactly right per the MMS service document, but	*/
/* will be OK for now.							*/
/************************************************************************/

ST_VOID _mvl_send_reject (MVL_COMM_EVENT *event)
  {
ST_RET rc;
REJECT_RESP_INFO rej_info;
MMSDEC_INFO *rslt;

  S_LOCK_COMMON_RESOURCES ();
  rej_info.detected_here = SD_TRUE;
  rej_info.invoke_known = SD_FALSE;
  rslt = &event->u.mms.dec_rslt;
  if (rslt->dec_level > 0)      /* If we at least know the type of PDU	*/
    {
    rej_info.pdu_type = rslt->type;
    /* If this was unrecognized confirmed request, set appropriate reject class/code.	*/
    if (rslt->dec_level >= 2 && rslt->type == MMSREQ && rslt->err_code==REQ_UNREC_SERV)
      {
      rej_info.rej_class = MMS_REJ_CLASS_CONFIRMED_REQUEST_PDU;
      rej_info.rej_code  = MMS_REJ_CODE_CONFIRMED_REQUEST_PDU_UNRECOGNIZED_SERVICE;
      }
    else
      {		/* set correct class, but just set code to "OTHER" (0).	*/
      rej_info.rej_class = pdutype_to_reject_class[rslt->type];
      rej_info.rej_code = 0;
      }

    if ((rslt->dec_level >= 2) &&  /* if invoke ID known, use it    */
        (rslt->type == MMSREQ || rslt->type == MMSRESP))
      {						
      rej_info.invoke_known = SD_TRUE;
      rej_info.invoke = rslt->id;
      }
    }
  else
    {
    rej_info.pdu_type = UNKNOWNTYPE;
    rej_info.rej_class = MMS_REJ_CLASS_PDU_ERROR;
    rej_info.rej_code = MMS_REJ_CODE_PDU_ERROR_INVALID_PDU;
    }

  rc = mpl_reject_resp (&rej_info);
  if (rc != SD_SUCCESS)
    {
    MVL_LOG_NERR1 ("Sending Reject : mpl_reject_resp failed, ret = %x", rc);
    }	
  else
    {			    	/* build was OK, send it		*/
    rc = _mvl_send_msg (mmsl_msg_start,mmsl_msg_len, event->net_info);
    if (rc != SD_SUCCESS)
      {
      MVL_LOG_NERR1 ("Sending Reject : _mvl_send_msg failed, ret = %x", rc);
      }
    else
      ++mvl_mms_statistics.rejectsSent;
    }

/* Tell the user all about it ... */
  u_mvl_reject_ind (event->net_info, &rej_info);

  _mvl_free_comm_event (event);	/* All done with this event		*/
  S_UNLOCK_COMMON_RESOURCES ();
  }


/************************************************************************/
/*			_mvl_send_reject_net_info			*/
/************************************************************************/

static ST_VOID _mvl_send_reject_net_info (MVL_NET_INFO *net_info, 
				          REJECT_RESP_INFO *rej_info)
				   
  {
ST_RET rc;

  S_LOCK_COMMON_RESOURCES ();
  rc = mpl_reject_resp (rej_info);
  if (rc != SD_SUCCESS)
    {
    MVL_LOG_NERR1 ("Sending Reject : mpl_reject_resp failed, ret = %x", rc);
    }	
  else
    {			    	/* build was OK, send it		*/
    rc = _mvl_send_msg (mmsl_msg_start,mmsl_msg_len, net_info);
    if (rc != SD_SUCCESS)
      {
      MVL_LOG_NERR1 ("Sending Reject : _mvl_send_msg failed, ret = %x", rc);
      }
    else
      ++mvl_mms_statistics.rejectsSent;
    }

/* Tell the user all about it ... */
  u_mvl_reject_ind (net_info, rej_info);

  S_UNLOCK_COMMON_RESOURCES ();
  }


/************************************************************************/
/*			_mplas_err_resp					*/
/* This function sends an error response and frees the MVL_IND_PEND	*/
/* and the MVL_COMM_EVENT.						*/
/************************************************************************/

ST_RET _mplas_err_resp (MVL_IND_PEND *indCtrl, ST_INT16 eClass, ST_INT16 code)
  {
ST_RET rc;
MVL_NET_INFO *net_info; 

  net_info = indCtrl->event->net_info;
  rc = _mvl_send_err_f (indCtrl->event, eClass, code);
  list_unlink(&(net_info->pend_ind),indCtrl);
  --net_info->numpend_ind;

  M_FREE (MSMEM_IND_CTRL, indCtrl);
  ++mvl_mms_statistics.serverRespErr;
  return (rc);
  }

/************************************************************************/
/*			_mvl_send_err_f					*/
/* This function sends an error response and frees the MVL_COMM_EVENT.	*/
/************************************************************************/

ST_RET _mvl_send_err_f (MVL_COMM_EVENT *event, ST_INT16 eClass, ST_INT16 code)
  {
ST_RET rc;

  rc = _mvl_send_error (event, eClass, code);
  _mvl_free_comm_event (event);	/* All done with this event		*/
  return (rc);
  }

/************************************************************************/
/*			_mvl_send_error					*/
/* This function is used to send an error response to a confirmed	*/
/* request PDU.								*/
/************************************************************************/

ST_RET _mvl_send_error (MVL_COMM_EVENT *event, ST_INT16 eClass, ST_INT16 code)
  {
ST_RET rc;

  S_LOCK_COMMON_RESOURCES ();
  rc = mpl_err_resp (event->u.mms.dec_rslt.op, 
                     event->u.mms.dec_rslt.id,
                     eClass,code);
  if (rc != SD_SUCCESS)
    {
    MVL_LOG_NERR1 ("Sending Error : mpl_err_resp failed, ret = %x", rc);
    }
  else
    {    			/* build was OK, send it		*/
    rc = _mvl_send_msg (mmsl_msg_start,mmsl_msg_len,event->net_info);
    if (rc != SD_SUCCESS)
      {
      MVL_LOG_NERR1 ("Sending Error : _mvl_send_msg failed, ret = %x", rc);
      }
    else
      ++mvl_mms_statistics.serverRespErr;
    }
  S_UNLOCK_COMMON_RESOURCES ();
  return (rc);
  }

/************************************************************************/
/*			mvl_wait_req_done				*/
/* RETURNS: SD_SUCCESS or MVL error code				*/
/************************************************************************/

ST_RET mvl_wait_req_done (MVL_REQ_PEND *req)
  {
  while (req->done == SD_FALSE)    /* wait forever until done		*/
    {
    if (u_mvl_check_timeout)
      {
      if ((*u_mvl_check_timeout)())
        {
        req->result = MVL_ERR_USR_TIMEOUT;
        MVL_LOG_NERR0 ("Wait Request Done : u_mvl_check_timeout timed out"); 
        break;
        }
      }
    mvl_comm_serve ();        	/* service communications		*/
    }    
  return (req->result);
  }

/************************************************************************/
/************************************************************************/
/* Outstanding MMS request control handling functions			*/
/************************************************************************/


/************************************************************************/
/*			_mvl_get_req_ctrl				*/
/************************************************************************/

MVL_REQ_PEND *_mvl_get_req_ctrl (MVL_NET_INFO *net_info, ST_INT16 op)
  {
MVL_REQ_PEND *req;

  if (net_info == NULL)
    {
    MVL_LOG_NERR0 ("_mvl_get_req_ctrl error: connection invalid");
    return (NULL);
    }
  req = (MVL_REQ_PEND *) M_CALLOC (MSMEM_REQ_CTRL, 1, sizeof (MVL_REQ_PEND));
  S_LOCK_COMMON_RESOURCES ();
  list_add_last(&net_info->req_pend_list, req);

  req->invoke_id = mmsl_invoke_id;
  req->op = op;
  req->done = SD_FALSE;
  req->result = SD_SUCCESS;
  req->net_info = net_info;
  req->u_req_done = NULL;
  req->event = NULL;
  S_UNLOCK_COMMON_RESOURCES ();
  return (req);
  }

/************************************************************************/
/*			_mvl_find_req_ctrl 				*/
/************************************************************************/

MVL_REQ_PEND *_mvl_find_req_ctrl (MVL_NET_INFO *net_info, 
				  ST_INT op, ST_UINT32 id)
  {
MVL_REQ_PEND *req;

  S_LOCK_COMMON_RESOURCES ();
  req = net_info->req_pend_list;	/* start at head of linked list	*/
  while (req != NULL)			/* look through whole list	*/
    {
    if (req->net_info == net_info && 
	req->invoke_id == id)
      {
      if (req->op == op || op == -1)
        break;				/* return a pointer to it	*/
      }
    req = (MVL_REQ_PEND *) list_get_next (net_info->req_pend_list, req);
    }

  S_UNLOCK_COMMON_RESOURCES ();
  return (req);
  }

/************************************************************************/
/*			_mvl_set_net_req_done				*/
/************************************************************************/

ST_VOID _mvl_set_net_req_done (MVL_REQ_PEND *skip, 
			       MVL_NET_INFO *net_info, ST_RET result)
  {
MVL_REQ_PEND *req;
MVL_REQ_PEND *next;

  req = net_info->req_pend_list;	/* start at head of linked list	*/
  while (req != NULL)			/* look through whole list	*/
    {
  /* CRITICAL: get NEXT entry BEFORE calling _mvl_set_req_done		*/
  /*           because it may remove THIS entry from the linked list.	*/
    next = (MVL_REQ_PEND *) list_get_next (net_info->req_pend_list, req);
    if (req->done != SD_TRUE)
      {
      if (req != skip)
        _mvl_set_req_done (req, result);
      }
    req = next;
    }

  /* CRITICAL: Destroy all AA_SPEC objects on this connection.		*/
  /*    This is a good place to do this because this function is	*/
  /*    called on Abort.req, Abort.ind, Release.ind, or Release.cnf	*/

  if (!_mvl_persist_aa_objs)
    _mvl_clear_aa_objs (net_info->aa_objs);
  }


/************************************************************************/
/* 			_mvl_set_req_done 				*/
/************************************************************************/

ST_VOID _mvl_set_req_done (MVL_REQ_PEND *req, ST_RET result)
  {
  req->result = result;
  req->done = SD_TRUE;

  if (req->u_req_done)		/* Call the user done function		*/
    (*req->u_req_done)(req);
  }

/************************************************************************/
/*			mvl_free_req_ctrl				*/
/************************************************************************/

ST_VOID mvl_free_req_ctrl (MVL_REQ_PEND *pend_req)
  {


  if (pend_req)
    {
    S_LOCK_COMMON_RESOURCES ();



    if (pend_req->event)
      _mvl_free_comm_event (pend_req->event);


    /* Free up extra stuff for some services.				*/
    /* NOTE: on error or reject the union (pend_req->u) is overwritten	*/
    /*       so "normal" resp_info is not valid. DON'T try to free it.	*/
    if (pend_req->op == MMSOP_FILE_DIR  &&
        pend_req->result != MVL_ERR_CNF_REJ_ERR  &&	/* !reject	*/
        pend_req->result != MVL_ERR_CNF_ERR_OK  &&	/* !error	*/
        pend_req->u.fdir.resp_info != NULL)
      {	/* Only do this if it was allocated (see confirm function)	*/
      M_FREE (MSMEM_GEN, pend_req->u.fdir.resp_info);
      }
    if (pend_req->op == MMSOP_READ_JOURNAL  &&
        pend_req->result != MVL_ERR_CNF_REJ_ERR  &&	/* !reject	*/
        pend_req->result != MVL_ERR_CNF_ERR_OK  &&	/* !error	*/
        pend_req->u.jread.resp_info != NULL)
      {	/* Only do this if it was allocated (see confirm function)	*/
      M_FREE (MSMEM_GEN, pend_req->u.jread.resp_info);
      }

    list_unlink (&pend_req->net_info->req_pend_list, pend_req);
    M_FREE (MSMEM_REQ_CTRL, pend_req);
    S_UNLOCK_COMMON_RESOURCES ();
    }

  }

/************************************************************************/
/************************************************************************/
/*			   mvl_init_glb_vars				*/
/************************************************************************/
/* For use when global variables cannot be initialized at compile time	*/

#if defined(NO_GLB_VAR_INIT)

ST_VOID mvl_init_glb_vars (ST_VOID)
  {
  m_init_glb_vars ();
  _mvlop_en_init ()
  }
#endif

/************************************************************************/
/*			_mvl_concat_filename					*/
/************************************************************************/
ST_RET _mvl_concat_filename (ST_CHAR *good_name, 
	ST_INT num_of_fname,
	FILE_NAME *fname_arr,
	ST_INT max_len)	/* size of array to fill in (i.e. good_name)	*/
  {
ST_INT j;
ST_INT total_len;

  for (j = 0, total_len = 0;  j < num_of_fname;  j++)
    total_len += fname_arr[j].fn_len;

  if (total_len + 1 > max_len)	/* Must leave room for NULL	*/
    return (SD_FAILURE);
  
  for (j = 0, total_len = 0;  j < num_of_fname;  j++)
    {
    memcpy (&good_name [total_len], fname_arr[j].fname, fname_arr[j].fn_len);
    total_len += fname_arr[j].fn_len;
    }
  good_name [total_len] = '\x00';		/* add NULL terminator	*/
  return (SD_SUCCESS);
  }

/************************************************************************/
/************************************************************************/
/************************************************************************/
/*			_mvl_calloc_os 					*/
/************************************************************************/

ST_VOID *_mvl_calloc_os (ST_UINT num, ST_UINT size)
  {
  return (M_CALLOC (MSMEM_DEC_OS_INFO, 1, size));
  }

/************************************************************************/
/*			_mvl_realloc_os 				*/
/************************************************************************/

ST_VOID *_mvl_realloc_os (ST_VOID *old, ST_UINT new_size)
  {
  return (M_REALLOC (MSMEM_DEC_OS_INFO, old,new_size));
  }

/************************************************************************/
/*			_mvl_free_os 					*/
/************************************************************************/

ST_VOID  _mvl_free_os (ST_VOID *buf)
  {
  M_FREE (MSMEM_DEC_OS_INFO, buf);
  }

/************************************************************************/
/*			mvl_req_bufs_avail				*/
/* Return number of buffers available for sending "confirmed" requests.	*/
/************************************************************************/

ST_INT mvlLowWaterAvail = 50;
ST_INT mvlLowWaterReqAvail = 50;
ST_INT mvlLowWaterUReqAvail = 50;

ST_INT mvl_req_bufs_avail (MVL_NET_INFO *net_info)
  {
ST_INT avail;

  avail = a_buffers_avail (net_info->acse_conn_id);
  if (avail < mvlLowWaterAvail)
    {
    mvlLowWaterAvail = avail;
    MVLU_LOG_DEBUG1 ("Low water 'buffers available': %d", mvlLowWaterAvail);
    }

  /* Must have enough buffers avail to respond to max num of indications.*/
  /* Doesn't matter how many indications are currently pending because	*/
  /* at any momemt could get a bunch of indications and must ALWAYS	*/
  /* have buffers available to respond to the maximum.			*/

  avail -= net_info->maxpend_ind;

  if (avail < mvlLowWaterReqAvail)
    {
    mvlLowWaterReqAvail = avail;
    MVLU_LOG_DEBUG1 ("Low water 'Req buffers available': %d", mvlLowWaterReqAvail);
    }
  return (avail);
  }

/************************************************************************/
/*			mvl_ureq_bufs_avail				*/
/* Return number of buffers available for sending "unconfirmed" reqs	*/
/************************************************************************/

ST_INT mvl_ureq_bufs_avail (MVL_NET_INFO *net_info)
  {
ST_INT avail;

  avail = a_buffers_avail (net_info->acse_conn_id);
  if (avail < mvlLowWaterAvail)
    {
    mvlLowWaterAvail = avail;
    MVLU_LOG_DEBUG1 ("Low water 'buffers available': %d", mvlLowWaterAvail);
    }

  /* Must have enough buffers avail to respond to max num of indications.*/
  /* Doesn't matter how many indications are currently pending because	*/
  /* at any momemt could get a bunch of indications and must ALWAYS	*/
  /* have buffers available to respond to the maximum.			*/

  /* Because we don't know when response buffers are release by SUIC, 	*/
  /* we always assume that we must be able to send the max resp at any	*/
  /* time. This means that when there really ARE max resp pend in SUIC	*/
  /* we reserve twice as many as we need ... 				*/

  avail -= (net_info->maxpend_ind * 2);	/* reserve maximum		*/

  /* Must have enough buffers avail to send max num of "confirmed" reqs.*/
  /* Doesn't matter how many requests are currently pending because	*/
  /* at any momemt could get a bunch of confirms and may want to send	*/
  /* new requests. So, we need to have buffers reserved for the max. 	*/
  avail -= (net_info->maxpend_req);

  if (avail < mvlLowWaterUReqAvail)
    {
    mvlLowWaterUReqAvail = avail;
    MVLU_LOG_DEBUG1 ("Low water 'UReq buffers' available': %d", mvlLowWaterUReqAvail);
    }
  return (avail);
  }

