From 390dabb3518f170336eb2795ca3f5b8f5522b5a5 Mon Sep 17 00:00:00 2001
From: Kapil <kgupta@sangoma.com>
Date: Tue, 24 Jul 2012 11:46:50 -0400
Subject: [PATCH] Adding code to    1) build NOTIFY command and send to MGC   
 2) CLI command to send DTMF NOTIFY Megaco command to MGC

---
 .../mod_media_gateway/media_gateway.c         |   5 +
 .../mod_media_gateway/media_gateway_cli.c     |  71 +++++--
 .../media_gateway_cmd_handler.c               | 197 ++++++++++++++++++
 .../mod_media_gateway/media_gateway_stack.h   |  10 +
 .../mod_media_gateway/media_gateway_utils.c   |  36 ++++
 5 files changed, 300 insertions(+), 19 deletions(-)

diff --git a/src/mod/endpoints/mod_media_gateway/media_gateway.c b/src/mod/endpoints/mod_media_gateway/media_gateway.c
index 6c0bfd4092..5d58674a0c 100644
--- a/src/mod/endpoints/mod_media_gateway/media_gateway.c
+++ b/src/mod/endpoints/mod_media_gateway/media_gateway.c
@@ -208,6 +208,11 @@ void megaco_termination_destroy(mg_termination_t *term)
     if (term->type == MG_TERM_RTP && term->u.rtp.local_port != 0) {
         switch_rtp_release_port(term->u.rtp.local_addr, term->u.rtp.local_port);
     }
+
+    if(term->active_events){
+        free(term->active_events);
+        term->active_events = NULL;
+    }
     
     switch_core_hash_delete_wrlock(term->profile->terminations, term->name, term->profile->terminations_rwlock);
     switch_core_destroy_memory_pool(&term->pool);
diff --git a/src/mod/endpoints/mod_media_gateway/media_gateway_cli.c b/src/mod/endpoints/mod_media_gateway/media_gateway_cli.c
index 3e3b034df5..f249d012f4 100644
--- a/src/mod/endpoints/mod_media_gateway/media_gateway_cli.c
+++ b/src/mod/endpoints/mod_media_gateway/media_gateway_cli.c
@@ -98,29 +98,61 @@ switch_status_t mg_process_cli_cmd(const char *cmd, switch_stream_handle_t *stre
 /**********************************************************************************/
 		}else if(!strcmp(argv[2], "send")) {
 /**********************************************************************************/
-			/* mg profile <profile-name> send sc <term-id> <method> <reason>*/
 			printf("count = %d \n",argc);
-			if(argc < 7){
-				goto usage;
-			}
-			if(zstr(argv[3]) || zstr(argv[4]) || zstr(argv[5]) || zstr(argv[6])){
-				goto usage;
-			}
 
-			if (profile) {
-				if(!zstr(argv[7]) && !strcasecmp(argv[7],"wild")){
-					wild = 0x01;
-				}
+            if (profile) {
 
-				printf("Input to Send Service Change command : "
-						"Profile Name[%s], term-id[%s] method[%s] reason[%s] \n",
-						profile->name, argv[4], argv[5], argv[6]);
+                switch(argc)
+                {
+                    case 7:
+                        {
+                            /* mg profile <profile-name> send sc <term-id> <method> <reason>*/
+                            printf("ARGC = 7 \n");
+                            if(zstr(argv[3]) || zstr(argv[4]) || zstr(argv[5]) || zstr(argv[6])){
+                                goto usage;
+                            }
+
+                            if(!zstr(argv[7]) && !strcasecmp(argv[7],"wild")){
+                                wild = 0x01;
+                            }
+
+                            printf("Input to Send Service Change command : "
+                                    "Profile Name[%s], term-id[%s] method[%s] reason[%s] \n",
+                                    profile->name, argv[4], argv[5], argv[6]);
+
+                            megaco_profile_release(profile);
+                            mg_send_service_change(profile->idx, argv[4], atoi(argv[5]), atoi(argv[6]),wild);
+
+                            break;
+                        }
+                    case 6:
+                        {
+                            /* mg profile <profile-name> send notify <term-id> <digits>*/
+                            if(zstr(argv[3]) || zstr(argv[4]) || zstr(argv[5])){
+                                goto usage;
+                            }
+
+                            if(strcasecmp(argv[3],"notify")){
+                                stream->write_function(stream, "-ERR wrong input \n");
+                                goto usage;
+                            }
+
+                            printf("Sending DTMF digits[%s] NOTIFY for termination[%s]\n", argv[5], argv[4]);
+
+                            megaco_profile_release(profile);
+                            mg_send_dtmf_notify(profile, argv[4], (char*)argv[5], (int)strlen(argv[5]));
+
+                            break;
+                        }
+                    default:
+                        {
+                            goto usage;
+                        }
+                }
+            }else{
+                stream->write_function(stream, "-ERR No such profile\n");
+            }
 
-				megaco_profile_release(profile);
-				mg_send_service_change(profile->idx, argv[4], atoi(argv[5]), atoi(argv[6]),wild);
-			} else {
-				stream->write_function(stream, "-ERR No such profile\n");
-			}
 /**********************************************************************************/
 		}else {
 /**********************************************************************************/
@@ -154,6 +186,7 @@ switch_status_t mg_process_cli_cmd(const char *cmd, switch_stream_handle_t *stre
 	goto done;
 
 usage:
+    megaco_profile_release(profile);
 	stream->write_function(stream, "-ERR Usage: \n""\t"MEGACO_CLI_SYNTAX" \n \t"MEGACO_FUNCTION_SYNTAX"\n \t" MEGACO_LOGGING_CLI_SYNTAX "\n");
 
 done:
diff --git a/src/mod/endpoints/mod_media_gateway/media_gateway_cmd_handler.c b/src/mod/endpoints/mod_media_gateway/media_gateway_cmd_handler.c
index bee845a8c1..12174639ae 100644
--- a/src/mod/endpoints/mod_media_gateway/media_gateway_cmd_handler.c
+++ b/src/mod/endpoints/mod_media_gateway/media_gateway_cmd_handler.c
@@ -2135,3 +2135,200 @@ err:
 	return ret;
 }
 /*****************************************************************************************************************************/
+/* API to send DTMF Digits Notification */
+
+switch_status_t  mg_send_dtmf_notify(megaco_profile_t* mg_profile, const char* term_name, char* digits, int num_of_collected_digits )
+{
+    MgMgcoObsEvt *oevt;
+    MgMgcoEvtPar* param;
+    int           ascii = 0x00;
+    int           cnt = 0x00;
+
+	switch_assert(term_name);
+	switch_assert(mg_profile);
+	switch_assert(digits);
+
+    if(0 == num_of_collected_digits ){
+        switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_ERROR,"num_of_collected_digits cannt be ZERO \n");
+        return SWITCH_STATUS_FALSE;
+    }
+
+    mg_stack_alloc_mem((Ptr*)&oevt, sizeof(MgMgcoObsEvt));
+
+    oevt->pres.pres = PRSNT_NODEF;
+
+    mg_get_time_stamp(&oevt->time);
+
+    MG_INIT_TOKEN_VALUE(&(oevt->pkg.pkgType), MGT_PKG_KNOWN);
+
+    MG_INIT_TOKEN_VALUE(&(oevt->pkg.valType), MGT_PKG_KNOWN);
+
+    MG_INIT_TOKEN_VALUE(&(oevt->pkg.u.val), MGT_PKG_EXT_DTMF);
+
+    MG_INIT_TOKEN_VALUE(&(oevt->name.type),MGT_GEN_TYPE_KNOWN);
+
+    MG_INIT_TOKEN_VALUE(&(oevt->name.u.val),(U8)MGT_PKG_ENUM_REQEVT_EXT_DTMF_EXT_CE);
+
+    if (mgUtlGrowList((void ***)&oevt->pl.parms, sizeof(MgMgcoEvtPar), &oevt->pl.num, NULL) != ROK) {
+        switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_ERROR,"Grow List failed\n");
+        return SWITCH_STATUS_FALSE;
+    }
+
+    param = oevt->pl.parms[0];
+
+    MG_INIT_TOKEN_VALUE(&(param->type),(U8)MGT_EVTPAR_OTHER);
+
+    MG_INIT_TOKEN_VALUE(&(param->u.other.name.type),MGT_GEN_TYPE_KNOWN);
+    MG_INIT_TOKEN_VALUE(&(param->u.other.name.u.val),MGT_PKG_ENUM_OBSEVTOTHER_EXT_DTMF_EXT_CE_DGT_STR);
+
+    MG_INIT_TOKEN_VALUE(&(param->u.other.val.type),MGT_VALUE_EQUAL);
+    MG_INIT_TOKEN_VALUE(&(param->u.other.val.u.eq.type),MGT_VALTYPE_OCTSTRXL);
+
+    mg_stack_alloc_mem((Ptr*)&param->u.other.val.u.eq.u.osxl.val, num_of_collected_digits+2);
+
+    param->u.other.val.u.eq.u.osxl.pres = 0x01; 
+    param->u.other.val.u.eq.u.osxl.len = num_of_collected_digits+2;
+
+    param->u.other.val.u.eq.u.osxl.val[0] = '\"';
+
+    /* copy collected DTMF digits */
+    if(ascii)
+    {
+        for(cnt = 1; cnt< num_of_collected_digits; cnt++){
+            /* convert values to ascii */
+            if(digits[cnt-1] <= 9){
+                param->u.other.val.u.eq.u.osxl.val[cnt] = digits[cnt-1] + '0';
+            }else{
+                /* 10 for decimal equivalent of A */
+                param->u.other.val.u.eq.u.osxl.val[cnt] = digits[cnt-1] + 'A' - 10;
+            }
+        }
+    } else {
+        /* If incoming digits are already in ascii format .. simply copy */
+        for(cnt = 1; cnt< num_of_collected_digits; cnt++){
+                param->u.other.val.u.eq.u.osxl.val[cnt] = digits[cnt-1];
+        }
+    }
+
+
+    param->u.other.val.u.eq.u.osxl.val[num_of_collected_digits+1] = '\"';
+
+    if (mgUtlGrowList((void ***)&oevt->pl.parms, sizeof(MgMgcoEvtPar), &oevt->pl.num, NULL) != ROK) {
+        switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_ERROR,"Grow List failed\n");
+        return SWITCH_STATUS_FALSE;
+    }
+
+    param = oevt->pl.parms[1];
+
+
+    /* Set method */
+    MG_INIT_TOKEN_VALUE(&(param->type),MGT_EVTPAR_OTHER);
+    MG_INIT_TOKEN_VALUE(&(param->u.other.name.type),MGT_GEN_TYPE_KNOWN);
+    MG_INIT_TOKEN_VALUE(&(param->u.other.name.u.val),MGT_PKG_ENUM_OBSEVTOTHER_EXT_DTMF_EXT_CE_TERM_METH);
+
+    MG_INIT_TOKEN_VALUE(&(param->u.other.val.type),MGT_VALUE_EQUAL);
+    MG_INIT_TOKEN_VALUE(&(param->u.other.val.u.eq.type),MGT_VALTYPE_ENUM);
+    MG_INIT_TOKEN_VALUE(&(param->u.other.val.u.eq.u.enume),MGT_PKG_ENUM_OBSEVTOTHER_EXT_DTMF_EXT_CE_TERM_METHFM);
+
+    return mg_send_notify(mg_profile, term_name, oevt);
+}
+
+/*****************************************************************************************************************************/
+
+/* Note : API to send NOTIFY Message */
+/* INPUT : 
+*           mg_profile          - MG profile structure pointer
+*	        term_name 			- Termination Name(as specified for MEGACO )
+*	        oevt                - Observed Event structure pointer
+*/
+switch_status_t  mg_send_notify(megaco_profile_t* mg_profile, const char* term_name, MgMgcoObsEvt* oevt)
+{
+    switch_status_t ret;
+    MgMgcoCommand   request;
+    MgMgcoTermId*   termId;
+    mg_termination_t* term = NULL;
+    MgMgcoObsEvtDesc  *obs_desc = NULL;
+    MgMgcoRequestId    reqId;               
+
+    switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_INFO, "Sending Notify Message for termination[%s] !\n", term_name);
+
+    MG_ZERO(&request, sizeof(request));
+    MG_ZERO(&reqId, sizeof(reqId));
+
+    term = megaco_find_termination(mg_profile, (char*)term_name);
+
+    if(!term){
+        switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_INFO, "No termination configured for given name[%s] !\n", term_name);
+        return SWITCH_STATUS_FALSE;
+    }
+
+    if(NULL == term->active_events){
+        switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_INFO, "No Active events observed on given termination[%s] !\n", term_name);
+        /* return SWITCH_STATUS_FALSE; */
+        /*TODO - ideally we should return ...
+         * as of now not returning .. if we dont have active signals then
+         * setting default request id and sending notification to MGC */
+        MG_SET_DEF_REQID(&reqId);
+    }else{
+        MG_MEM_COPY(&reqId, &term->active_events->reqId, sizeof(MgMgcoRequestId));
+    }
+
+
+    if(SWITCH_STATUS_FALSE == (ret = mg_create_mgco_command(&request, CH_CMD_TYPE_REQ, MGT_NTFY))){
+        return SWITCH_STATUS_FALSE;
+    }
+
+    if (mgUtlGrowList((void ***)&request.u.mgCmdReq[0]->cmd.u.ntfy.obs.el.evts, sizeof(MgMgcoObsEvtLst),
+                &request.u.mgCmdReq[0]->cmd.u.ntfy.obs.el.num, &request.u.mgCmdReq[0]->memCp) != ROK) {
+        switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_ERROR,"Grow List failed\n");
+        return SWITCH_STATUS_FALSE;
+    }
+
+    obs_desc = &request.u.mgCmdReq[0]->cmd.u.ntfy.obs;
+
+    MG_INIT_TOKEN_VALUE(&(obs_desc->pres), PRSNT_NODEF);
+
+    /* copy request id */
+    MG_MEM_COPY(&obs_desc->reqId, &reqId, sizeof(MgMgcoRequestId));
+
+    /* copy observe event */
+    /*MG_MEM_COPY(obs_desc->el.evts[0], oevt, sizeof(MgMgcoObsEvt));*/
+
+    obs_desc->el.evts[0] = oevt;
+
+
+    /*fill txn id */
+    request.transId.pres = PRSNT_NODEF;
+    request.transId.val  = get_txn_id();
+
+    request.contextId.type.pres = PRSNT_NODEF;
+    request.contextId.type.val  = MGT_CXTID_NULL;
+
+    request.cmdStatus.pres = PRSNT_NODEF;
+    request.cmdStatus.val = CH_CMD_STATUS_END_OF_TXN;
+
+    request.cmdType.pres = PRSNT_NODEF;
+    request.cmdType.val  = CH_CMD_TYPE_REQ;
+
+    /* fill termination */
+    if (mgUtlGrowList((void ***)&request.u.mgCmdReq[0]->cmd.u.ntfy.termIdLst.terms, sizeof(MgMgcoTermIdLst),
+                &request.u.mgCmdReq[0]->cmd.u.ntfy.termIdLst.num, &request.u.mgCmdReq[0]->memCp) != ROK)
+    {
+        switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_ERROR,"Grow List failed\n");
+        return SWITCH_STATUS_FALSE;
+    }
+
+#ifdef GCP_VER_2_1
+    termId = request.u.mgCmdReq[0]->cmd.u.ntfy.termIdLst.terms[0];
+#else
+    termId = &(request.u.mgCmdReq[0]->cmd.u.ntfy.termId);
+#endif
+
+    mg_fill_mgco_termid(termId, (char*)term_name ,strlen(term_name), &request.u.mgCmdReq[0]->memCp);
+
+    sng_mgco_send_cmd(mg_profile->idx, &request);
+
+    return SWITCH_STATUS_SUCCESS;
+}
+/*****************************************************************************************************************************/
+
diff --git a/src/mod/endpoints/mod_media_gateway/media_gateway_stack.h b/src/mod/endpoints/mod_media_gateway/media_gateway_stack.h
index fc27386d9a..f308496b1e 100644
--- a/src/mod/endpoints/mod_media_gateway/media_gateway_stack.h
+++ b/src/mod/endpoints/mod_media_gateway/media_gateway_stack.h
@@ -120,6 +120,13 @@ typedef enum {
    cmMemcpy((U8*)((_tkn).val), (U8*)(_val), _len);\
 }
 
+#define MG_SET_DEF_REQID(_reqId)                \
+   (_reqId)->type.pres = PRSNT_NODEF;               \
+   (_reqId)->type.val = MGT_REQID_OTHER;            \
+   (_reqId)->id.pres = PRSNT_NODEF;                 \
+   (_reqId)->id.val = 0xFFFFFFFF;
+
+
 switch_status_t mg_prc_descriptors(megaco_profile_t* mg_profile, MgMgcoCommand *inc_cmd, mg_termination_t* term);
 void handle_sng_log(uint8_t level, char *fmt, ...);
 void handle_mgco_sta_ind(Pst *pst, SuId suId, MgMgtSta* msg);
@@ -174,6 +181,9 @@ switch_status_t  mg_create_mgco_command(MgMgcoCommand  *cmd, uint8_t apiType, ui
 
 switch_status_t mg_send_oos_service_change(megaco_profile_t* mg_profile, const char* term_name, int wild);
 switch_status_t mg_send_ins_service_change(megaco_profile_t* mg_profile, const char* term_name, int wild);
+switch_status_t  mg_send_notify(megaco_profile_t* mg_profile, const char* term_name, MgMgcoObsEvt* oevt);
+switch_status_t  mg_send_dtmf_notify(megaco_profile_t* mg_profile, const char* term_name, char* digits, int num_of_collected_digits);
+switch_status_t  mg_util_build_obs_evt_desc (MgMgcoObsEvt *obs_event, MgMgcoRequestId *request_id, MgMgcoObsEvtDesc **ptr_obs_desc);
 
 
 
diff --git a/src/mod/endpoints/mod_media_gateway/media_gateway_utils.c b/src/mod/endpoints/mod_media_gateway/media_gateway_utils.c
index 73d467f8de..b6751c6eea 100644
--- a/src/mod/endpoints/mod_media_gateway/media_gateway_utils.c
+++ b/src/mod/endpoints/mod_media_gateway/media_gateway_utils.c
@@ -1353,3 +1353,39 @@ void mg_fill_null_context(MgMgcoContextId* ctxt)
 	MG_SET_TKN_VAL_PRES(&ctxt->type, MGT_CXTID_NULL, PRSNT_NODEF);
 }	
 /*****************************************************************************************************************************/
+switch_status_t  mg_util_build_obs_evt_desc (MgMgcoObsEvt *obs_event, MgMgcoRequestId *request_id, MgMgcoObsEvtDesc **ptr_obs_desc)
+{
+   MgMgcoObsEvtDesc *obs_desc = NULL;
+ 
+   /* Check for valid request Id, if not then fill default value */
+   if (NOTPRSNT == request_id->type.pres)
+   {
+      MG_SET_DEF_REQID(request_id);
+   }
+ 
+   mg_stack_alloc_mem((Ptr*)&obs_desc, sizeof(MgMgcoObsEvtDesc));
+   if (NULL == obs_desc)
+   {
+       switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_ERROR, "Failed to allocate MgMgcoObsEvtDesc!\n");
+       return SWITCH_STATUS_FALSE;
+   }
+
+   obs_desc->pres.pres = PRSNT_NODEF;
+   MG_MEM_COPY(&obs_desc->reqId, request_id, sizeof(MgMgcoRequestId));
+   obs_desc->el.num.pres = PRSNT_NODEF;
+   obs_desc->el.num.val = 1;
+
+   mg_stack_alloc_mem((Ptr*)&obs_desc->el.evts, sizeof(MgMgcoObsEvt*));
+   if (NULL == obs_desc->el.evts)
+   {
+       switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_ERROR, "Failed to allocate MgMgcoObsEvt!\n");
+       return SWITCH_STATUS_FALSE;
+   }
+
+   MG_MEM_COPY(obs_desc->el.evts[0], obs_event, sizeof(obs_event));
+
+   *ptr_obs_desc = obs_desc;
+
+   return SWITCH_STATUS_SUCCESS;
+}
+/*****************************************************************************************************************************/