freeswitch/libs/sipcc/core/src-common/kpmlmap.c

2130 lines
60 KiB
C
Executable File

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "cpr_types.h"
#include "cpr_memory.h"
#include "cpr_timers.h"
#include "cpr_locks.h"
#include "phntask.h"
#include "ccsip_subsmanager.h"
#include "singly_link_list.h"
#include "ccapi.h"
#include "subapi.h"
#include "fsm.h"
#include "uiapi.h"
#include "rtp_defs.h"
#include "lsm.h"
#include "lsm_private.h"
#include "kpmlmap.h"
#include "dialplanint.h"
#include "uiapi.h"
#include "debug.h"
#include "phone_debug.h"
#include "kpml_common_util.h"
#include "gsm.h"
cc_int32_t KpmlDebug = 0;
static sll_handle_t s_kpml_list = NULL;
static int s_kpml_config;
static void kpml_generate_notify(kpml_data_t *kpml_data, boolean no_body,
unsigned int resp_code, char *resp_text);
static void kpml_generate_subscribe_response(kpml_data_t *kpml_data,
int resp_code);
static void kpmlmap_show(void);
void kpml_inter_digit_timer_callback(void *kpml_key_p);
void kpml_subscription_timer_callback(void *kpml_key);
static uint32_t g_kpml_id = CC_NO_CALL_ID;
static cprMutex_t kpml_mutex;
/*
* Function: kpml_get_state()
*
* Parameters:
*
* Description: Function called to check if the KPML is configured for signaling.
*
*
* Returns: none
*/
boolean
kpml_get_state (void)
{
config_get_value(CFGID_KPML_ENABLED, &s_kpml_config, sizeof(s_kpml_config));
if ((s_kpml_config == KPML_NONE) || (s_kpml_config == KPML_DTMF_ONLY)) {
return FALSE;
}
return TRUE;
}
/*
* Function: kpml_get_config_value()
*
* Parameters:
*
* Description: Function called to check if the KPML is enabled.
*
*
* Returns: none
*/
kpml_config_e
kpml_get_config_value (void)
{
int kpml_config = KPML_NONE;
config_get_value(CFGID_KPML_ENABLED, &kpml_config, sizeof(kpml_config));
return (kpml_config_e) kpml_config;
}
/*
* Function: kpml_get_new_data()
*
* Parameters: none
*
* Description: Get a new kpml data
*
* Returns: kpml_data_t
*/
static kpml_data_t *
kpml_get_new_data (void)
{
kpml_data_t *kpml_mem;
kpml_mem = (kpml_data_t *) cpr_malloc(sizeof(kpml_data_t));
if (kpml_mem == NULL)
{
return (NULL);
}
memset(kpml_mem, 0, sizeof(kpml_data_t));
kpml_mem->kpml_id = ++g_kpml_id;
return (kpml_mem);
}
/*
* Function: kpml_release_data()
*
* Parameters: kpml_data_t
*
* Description: release kpml data
*
* Returns: None
*/
static void
kpml_release_data (kpml_data_t * kpml_data)
{
cpr_free(kpml_data);
}
/*
* Function: kpml_create_sm_key()
*
* Parameters: kpml_key_t - key information
* line - line number of the call
* call_id - call indentifier
*
* Description: This routine fills line and callid in the key structures.
*
* Returns: None
*/
static void
kpml_create_sm_key (kpml_key_t *key_p, line_t line, callid_t call_id,
void *tmr_ptr)
{
static const char fname[] = "kpml_create_sm_key";
KPML_DEBUG(DEB_L_C_F_PREFIX" timer=0x%0x\n",
DEB_L_C_F_PREFIX_ARGS(KPML_INFO, line, call_id, fname), tmr_ptr);
key_p->line = line;
key_p->call_id = call_id;
key_p->timer = tmr_ptr;
}
/*
* Function: kpml_match_line_call_id()
*
* Parameters: kpml_data - Kpml subscription data
* kpml_key_t - key information
*
* Description: Callback function provided to link list
* to search for a particular line and call_id
*
* Returns: ss_match_e
*/
static sll_match_e
kpml_match_line_call_id (kpml_data_t * kpml_data_p, kpml_key_t * key_p)
{
static const char fname[] = "kpml_match_line_call_id";
if ((kpml_data_p->call_id == key_p->call_id) &&
(kpml_data_p->line == key_p->line)) {
KPML_DEBUG(DEB_L_C_F_PREFIX"Match Found.\n",
DEB_L_C_F_PREFIX_ARGS(KPML_INFO, key_p->line, key_p->call_id, fname));
return SLL_MATCH_FOUND;
}
return SLL_MATCH_NOT_FOUND;
}
/*
* Function: kpml_data_present_for_subid()
*
* Parameters: sub_id_t - sub id sent int he message
*
* Description: Callback function provided to link list
* to see if there kpml_data that matches a sub_id
*
* Returns: kpml_data_t * - Return kpml_data if a match is found
*/
static kpml_data_t *
kpml_data_for_subid(sub_id_t sub_id)
{
kpml_data_t *kpml_data;
kpml_data = (kpml_data_t *) sll_next(s_kpml_list, NULL);
while (kpml_data != NULL) {
if (kpml_data->sub_id == sub_id) {
return kpml_data;
}
kpml_data = (kpml_data_t *) sll_next(s_kpml_list, kpml_data);
}
return NULL;
}
/*
* Function: kpml_start_timer()
*
* Parameters: line_num - line id on which timer to start
* callId - Callid of the call on which timer
* to start
* timer_ptr - User allocated pointer
* duration - in seconds
* timer_callback- callback function once timer
* expires
*
* Description: start any given timer with duration in seconds
*
* Returns: None
*/
static void
kpml_start_timer (line_t line, callid_t callId,
void *timer_ptr, unsigned int duration,
uint32_t kpml_id)
{
if (timer_ptr) {
(void) cprCancelTimer(timer_ptr);
(void) cprStartTimer(timer_ptr, duration, (void *)(long)kpml_id);
}
}
/*
* Function: kpml_stop_timer()
*
* Parameters: timer_ptr - pointer to timer data to stop
*
* Description: stop any specified timer
*
* Returns: None
*/
static void
kpml_stop_timer (void *timer_ptr)
{
if (timer_ptr) {
(void) cprCancelTimer(timer_ptr);
(void) cprDestroyTimer(timer_ptr);
}
}
/*
* Function: kpml_clear_timers()
*
* Parameters: kpml_data - pointer to kpml data
*
* Description: stop and delete all kpml timers
*
* Returns: None
*/
static void
kpml_clear_timers (kpml_data_t *kpml_data)
{
static const char fname[] = "kpml_clear_timers";
KPML_DEBUG(DEB_L_C_F_PREFIX"Release kpml timers.\n",
DEB_L_C_F_PREFIX_ARGS(KPML_INFO, kpml_data->line, kpml_data->call_id, fname));
kpml_stop_timer(kpml_data->inter_digit_timer);
kpml_data->inter_digit_timer = NULL;
kpml_stop_timer(kpml_data->critical_timer);
kpml_data->critical_timer = NULL;
kpml_stop_timer(kpml_data->extra_digit_timer);
kpml_data->extra_digit_timer = NULL;
kpml_stop_timer(kpml_data->sub_timer);
kpml_data->sub_timer = NULL;
}
/*
* Function: kpml_start_timers()
*
* Parameters: kpml_data_t - subscription data
* kpml_data_t - kpml data
*
* Description: start different kpml related timers. Even though all the
* KPML timers are allocated, only interdigit timer will be in use
*
* Returns: None
*/
static void
kpml_start_timers (kpml_data_t *kpml_data)
{
static const char *fname ="kpml_start_timers";
kpml_data->inter_digit_timer =
cprCreateTimer("Interdigit timer", GSM_KPML_INTER_DIGIT_TIMER,
TIMER_EXPIRATION, gsm_msg_queue);
kpml_data->critical_timer =
cprCreateTimer("Criticaldigit timer", GSM_KPML_CRITICAL_DIGIT_TIMER,
TIMER_EXPIRATION, gsm_msg_queue);
kpml_data->extra_digit_timer =
cprCreateTimer("Extradigit timer", GSM_KPML_EXTRA_DIGIT_TIMER,
TIMER_EXPIRATION, gsm_msg_queue);
/* Check if any of the timer cannot be allocated */
if (kpml_data->inter_digit_timer == NULL ||
kpml_data->critical_timer == NULL ||
kpml_data->extra_digit_timer == NULL) {
/* generate error to indicate timer cannot be allocated */
KPML_ERROR(KPML_F_PREFIX"No memory to allocate timer\n",
fname);
return;
}
/* Start interdigit timer */
kpml_start_timer(kpml_data->line, kpml_data->call_id,
kpml_data->inter_digit_timer,
kpml_data->inttimeout, kpml_data->kpml_id);
/* Start critical timer */
kpml_start_timer(kpml_data->line, kpml_data->call_id,
kpml_data->critical_timer,
kpml_data->crittimeout, kpml_data->kpml_id);
/* Start extra digit timer */
kpml_start_timer(kpml_data->line, kpml_data->call_id,
kpml_data->extra_digit_timer,
kpml_data->extratimeout, kpml_data->kpml_id);
}
/*
* Function: kpml_restart_timers()
*
* Parameters: kpml_data_t - subscription data
* kpml_data_t - kpml data
*
* Description: Restart all the KPML related timers
*
* Returns: None
*/
static void
kpml_restart_timers (kpml_data_t * kpml_data)
{
static const char fname[] = "kpml_restart_timers";
KPML_DEBUG(DEB_L_C_F_PREFIX"Restart all timers\n",
DEB_L_C_F_PREFIX_ARGS(KPML_INFO, kpml_data->line, kpml_data->call_id, fname));
kpml_stop_timer(kpml_data->critical_timer);
kpml_stop_timer(kpml_data->inter_digit_timer);
kpml_stop_timer(kpml_data->extra_digit_timer);
kpml_start_timers(kpml_data);
return;
}
/*
* Function: kpml_clear_data()
*
* Parameters: kpml_data - Subscription related information
* kpml_data - KPML realated information
* kpml_sub_type_e - Type of kpml subscription - persistent
* single-notify or one shot
*
* Description: Clear the KPML subscription data depending upon
* KPML request type. If the KPML request type is one shot
* clear the data or else maintain it for the duration of the
* subscription.
*
* Returns: kpml_data next in the list if succesful
* NULL - if not
*/
static boolean
kpml_clear_data (kpml_data_t *kpml_data, kpml_sub_type_e sub_type)
{
static const char fname[] = "kpml_clear_data";
KPML_DEBUG(DEB_L_C_F_PREFIX"sub_type=%d",
DEB_L_C_F_PREFIX_ARGS(KPML_INFO, kpml_data->line, kpml_data->call_id, fname), sub_type);
switch (sub_type) {
case KPML_ONE_SHOT:
KPML_DEBUG(DEB_F_PREFIX"One shot\n", DEB_F_PREFIX_ARGS(KPML_INFO, fname));
kpml_stop_timer(kpml_data->inter_digit_timer);
kpml_data->inter_digit_timer = NULL;
kpml_stop_timer(kpml_data->critical_timer);
kpml_data->critical_timer = NULL;
kpml_stop_timer(kpml_data->extra_digit_timer);
kpml_data->extra_digit_timer = NULL;
kpml_stop_timer(kpml_data->sub_timer);
kpml_data->sub_timer = NULL;
(void) sll_remove(s_kpml_list, kpml_data);
kpml_release_data(kpml_data);
kpmlmap_show();
return (TRUE);
case KPML_PERSISTENT:
KPML_DEBUG(DEB_F_PREFIX"Persistent\n", DEB_F_PREFIX_ARGS(KPML_INFO, fname));
/* FALLTHROUGH */
case KPML_SINGLY_NOTIFY:
KPML_DEBUG(DEB_F_PREFIX"Singly notify\n", DEB_F_PREFIX_ARGS(KPML_INFO, fname));
/*
* persistent KPML request so clear the
* digit buffer and then restart all the timer
*/
kpml_data->kpmlDialed[0] = 00;
kpml_restart_timers(kpml_data);
/*
* if the persistent request is singly-notify
* do not terminate the subscription, but do
* not notify any more digits untill application
* sends out new kpml document. At this time persistent
* subsciption is not supported.
*/
kpmlmap_show();
return (FALSE);
default:
KPML_DEBUG(DEB_F_PREFIX"KPML type not specified\n", DEB_F_PREFIX_ARGS(KPML_INFO, fname));
return (FALSE);
}
}
/*
* Function: kpml_quarantine_digits()
*
* Parameters: line - line number
* call_id - call id of the dialog
* digits - collected difits
* len - Length of collected digits, currently this length
* is 1
*
* Description: Quarantine collected digits in a chain of subscription
* strucutre. if there is no subscription structure creaded
* already then create a new one. The maximum number of digits
* collected per dialog is 256. if there are more digits need to
* be collected then overwrite the oldest digit.
*
* Returns: None
*/
void
kpml_quarantine_digits (line_t line, callid_t call_id, char digit)
{
static const char fname[] = "kpml_quarantine_digits";
kpml_data_t *kpml_data;
kpml_key_t kpml_key;
if (kpml_get_config_value() == KPML_NONE) {
return;
}
KPML_DEBUG(DEB_L_C_F_PREFIX"digit=0x%0x\n",
DEB_L_C_F_PREFIX_ARGS(KPML_INFO, line, call_id, fname), digit);
kpml_create_sm_key(&kpml_key, line, call_id, NULL);
kpml_data = (kpml_data_t *) sll_find(s_kpml_list, &kpml_key);
if (!kpml_data) {
kpml_data = kpml_get_new_data();
if (kpml_data == NULL) {
KPML_ERROR(KPML_F_PREFIX"No memory for subscription data\n",
fname);
return;
}
(void) sll_append(s_kpml_list, kpml_data);
kpml_data->line = line;
kpml_data->call_id = call_id;
kpml_data->pending_sub = FALSE;
kpml_data->dig_head = kpml_data->dig_tail = 0;
}
if (kpml_data->dig_head == (kpml_data->dig_tail + 1) % MAX_DIALSTRING) {
/* buffer is full so discard old collected digits */
kpml_data->dig_head = (kpml_data->dig_head + 1) % MAX_DIALSTRING;
}
kpml_data->q_digits[kpml_data->dig_tail] = digit;
kpml_data->dig_tail = (kpml_data->dig_tail + 1) % MAX_DIALSTRING;
}
/*
* Function: kpml_flush_quarantine_buffer()
*
* Parameters: line - line number
* call_id - call id of the dialog
*
* Description: This function will be called to flush any
* quarantined buffers.
*
* Returns: None
*/
void
kpml_flush_quarantine_buffer (line_t line, callid_t call_id)
{
static const char fname[] = "kpml_flush_quarantine_buffer";
kpml_data_t *kpml_data;
kpml_key_t kpml_key;
if (kpml_get_config_value() == KPML_NONE) {
return;
}
KPML_DEBUG(DEB_L_C_F_PREFIX"Flush buffer\n", DEB_L_C_F_PREFIX_ARGS(KPML_INFO, line, call_id, fname));
kpml_create_sm_key(&kpml_key, line, call_id, NULL);
kpml_data = (kpml_data_t *) sll_find(s_kpml_list, &kpml_key);
if (kpml_data) {
if (!kpml_data->pending_sub) {
kpml_data->dig_head = kpml_data->dig_tail = 0;
(void) kpml_clear_data(kpml_data, KPML_ONE_SHOT);
}
}
}
/*
* Function: kpml_update_quarantined_digits()
*
* Parameters: kpml_data_t : subscription type
*
* Description: This function will be called to update any
* quarantined buffer through KPML patter map.
*
* Returns: None
*/
static void
kpml_update_quarantined_digits (kpml_data_t *kpml_data)
{
static const char fname[] = "kpml_update_quarantined_digits";
KPML_DEBUG(DEB_L_C_F_PREFIX"Update quarantined digits\n",
DEB_L_C_F_PREFIX_ARGS(KPML_INFO, kpml_data->line, kpml_data->call_id, fname));
while (kpml_data->dig_head != kpml_data->dig_tail) {
kpml_update_dialed_digits(kpml_data->line,
kpml_data->call_id,
kpml_data->q_digits[kpml_data->dig_head]);
kpml_data->dig_head = (kpml_data->dig_head + 1) % MAX_DIALSTRING;
}
}
/*
* Function: kpml_digit_timer_callback()
*
* Parameters: timer - pointer to timer
* line - Line number
* call_id - call id
*
* Description: Handle different digit timer events. At this time it
* hanles only inter digit timer.
*
* Returns: None
*/
void
kpml_inter_digit_timer_callback (void *kpml_key_p)
{
(void) app_send_message(&kpml_key_p, sizeof(void *), CC_SRC_GSM,
SUB_MSG_KPML_DIGIT_TIMER);
}
kpml_data_t *kpml_get_kpml_data_from_kpml_id(uint32_t kpml_id)
{
kpml_data_t *kpml_data;
kpml_data = (kpml_data_t *) sll_next(s_kpml_list, NULL);
while (kpml_data != NULL && kpml_data->kpml_id != kpml_id) {
kpml_data = (kpml_data_t *) sll_next(s_kpml_list, kpml_data);
}
return(kpml_data);
}
/*
* Function: digit_timer_callback()
*
* Parameters: timer - pointer to timer
* line - Line number
* call_id - call id
*
* Description: Handle different digit timer events. At this time it
* hanles only inter digit timer.
*
* Returns: None
*/
static void
kpml_inter_digit_timer_event (void **kpml_key_p)
{
const char fname[] = "kpml_inter_digit_timer_callback";
kpml_data_t *kpml_data;
KPML_DEBUG("%s: kpml_id=%d \n ",
fname, (long)*kpml_key_p);
kpml_data = kpml_get_kpml_data_from_kpml_id((long)*kpml_key_p);
if (kpml_data == NULL) {
KPML_ERROR(KPML_F_PREFIX"KPML data not found.\n", fname);
return;
}
KPML_DEBUG(": Interdigit Timer\n");
kpml_generate_notify(kpml_data, FALSE, KPML_TIMER_EXPIRE,
KPML_TIMER_EXPIRE_STR);
/* Timer expired so clear the KPML data */
(void) kpml_clear_data(kpml_data, kpml_data->persistent);
}
/*
* Function: kpml_start_subscription_timer()
*
* Parameters: kpml_data_t - subscription data
*
* Description: Allocate and start subscription timer
*
* Returns: Pointer to timer
*/
static void *
kpml_start_subscription_timer (kpml_data_t * kpml_data, unsigned long duration)
{
static const char fname[] = "kpml_start_subscription_timer";
KPML_DEBUG(DEB_L_C_F_PREFIX"duration=%u\n",
DEB_L_C_F_PREFIX_ARGS(KPML_INFO, kpml_data->line, kpml_data->call_id, fname), duration);
kpml_data->sub_timer = cprCreateTimer("sub timer",
GSM_KPML_SUBSCRIPTION_TIMER,
TIMER_EXPIRATION, gsm_msg_queue);
kpml_data->sub_duration = duration;
kpml_create_sm_key(&(kpml_data->subtimer_key), kpml_data->line,
kpml_data->call_id, kpml_data->sub_timer);
/* Start subscription duration timer */
kpml_start_timer(kpml_data->line,
kpml_data->call_id,
kpml_data->sub_timer,
kpml_data->sub_duration * 1000,
kpml_data->kpml_id);
return (kpml_data->sub_timer);
}
/*
* Function: kpml_subscription_timer_callback()
*
* Parameters: timer - timer pointer
*
* Description: This routine called by timer callback once the subscription
* expires.
*
* Returns: None
*/
void
kpml_subscription_timer_callback (void *kpml_key_p)
{
(void) app_send_message(&kpml_key_p, sizeof(void *), CC_SRC_GSM,
SUB_MSG_KPML_SUBSCRIBE_TIMER);
}
/*
* Function: kpml_subscription_timer_event()
*
* Parameters: timer - timer pointer
*
* Description: This routine called by timer callback once the subscription
* expires.
*
* Returns: None
*/
static void
kpml_subscription_timer_event (void **kpml_key_p)
{
static const char fname[] = "kpml_subscription_timer_event";
kpml_data_t *kpml_data;
KPML_DEBUG("%s: kpml_id=%d \n ",
fname, (long)*kpml_key_p);
kpml_data = kpml_get_kpml_data_from_kpml_id((long)*kpml_key_p);
/* Evaluate if we need to send empty notify */
if (kpml_data) {
kpml_generate_notify(kpml_data, FALSE, KPML_SUB_EXPIRE,
KPML_SUB_EXPIRE_STR);
(void) kpml_clear_data(kpml_data, KPML_ONE_SHOT);
}
}
/*
* Function: kpml_match_requested_digit()
*
* Parameters: input - single char
*
* Description: Determine if the char is 0-9 or *
* # is not matched here since it is
* primarily used as "dial immediately"
*
* Returns: boolean
*/
static boolean
kpml_match_requested_digit (kpml_regex_match_t *regex_match, char input)
{
uint32_t *bitmask;
uint32_t result = 0;
bitmask = (uint32_t *) &(regex_match->u.single_digit_bitmask);
switch (input) {
case '1':
result = (*bitmask & REGEX_1);
break;
case '2':
result = (*bitmask & REGEX_2);
break;
case '3':
result = (*bitmask & REGEX_3);
break;
case '4':
result = (*bitmask & REGEX_4);
break;
case '5':
result = (*bitmask & REGEX_5);
break;
case '6':
result = (*bitmask & REGEX_6);
break;
case '7':
result = (*bitmask & REGEX_7);
break;
case '8':
result = (*bitmask & REGEX_8);
break;
case '9':
result = (*bitmask & REGEX_9);
break;
case '0':
result = (*bitmask & REGEX_0);
break;
case '*':
result = (*bitmask & REGEX_STAR);
break;
case '#':
result = (*bitmask & REGEX_POUND);
break;
case 'A':
result = (*bitmask & REGEX_A);
break;
case 'B':
result = (*bitmask & REGEX_B);
break;
case 'C':
result = (*bitmask & REGEX_C);
break;
case 'D':
result = (*bitmask & REGEX_D);
break;
case '+':
result = (*bitmask & REGEX_PLUS);
break;
default:
result = 0;
break;
}
if (result) {
return (TRUE);
}
return (FALSE);
}
/*
* Function: kpml_match_pattern
*
* Parameters: kpml_data_t - Data strcuture holding digits and KPML
* parameters
*
* Description: At this time only one digit reporting is supported. So
* match the incoming digit to 1st template
*
* Returns: kpml_match_action_e
*/
static kpml_match_action_e
kpml_match_pattern (kpml_data_t *ptempl)
{
char *pinput;
int regex_index = 0;
pinput = &ptempl->kpmlDialed[0];
if (strchr(pinput, '#') && ptempl->enterkey) {
return (KPML_IMMEDIATELY);
}
if (kpml_match_requested_digit
(&(ptempl->regex_match[regex_index]), pinput[0])) {
return (KPML_FULLPATTERN);
}
return (KPML_NOMATCH);
}
/*
* Function: kpml_is_subscribed()
*
* Parameters: line - line number on which SUB created
* call_id - call id of the call
*
* Description: checks if kpml is susbcribed for this call.
*
* Returns: status - TRUE - if there is valid subscription
* FALSE - if there is no valid subscription
*/
boolean kpml_is_subscribed (callid_t call_id, line_t line)
{
static const char fname[] = "kpml_is_subscribed";
kpml_data_t *kpml_data, *kpml_next_data;
KPML_DEBUG(DEB_L_C_F_PREFIX, DEB_L_C_F_PREFIX_ARGS(KPML_INFO, line, call_id, fname));
kpml_data = (kpml_data_t *) sll_next(s_kpml_list, NULL);
while (kpml_data) {
kpml_next_data = (kpml_data_t *) sll_next(s_kpml_list, kpml_data);
if (kpml_data->pending_sub &&
kpml_data->line == line &&
kpml_data->call_id == call_id) {
return TRUE;
}
kpml_data = kpml_next_data;
}
return FALSE;
}
/*
* Function: kpml_update_dialed_digits()
*
* Parameters: line - line number on which SUB created
* call_id - call id of the call
* digits - digit collected (it accepts only one digit now)
* len - length of the digit string =1
*
* Description: Routine called by UI to update any collected digits. At this time
* this routine handles only one digits at a time. Any given pattern will be
* matched with incoming KPML patter. if there is a match notify will be
* sent out. If there is no match digits are buffered for the future use.
*
* Returns: status - TRUE - if there is valid subscription
* FALSE - if there is no valis subscription
*/
kpml_state_e
kpml_update_dialed_digits (line_t line, callid_t call_id, char digit)
{
static const char fname[] = "kpml_update_dialed_digits";
kpml_data_t *kpml_data, *kpml_next_data;
kpml_match_action_e result = KPML_NOMATCH;
int dial_len = 0;
kpml_state_e state = NO_SUB_DATA;
if (kpml_get_config_value() == KPML_NONE) {
return (state);
}
KPML_DEBUG(DEB_L_C_F_PREFIX"digits=0x%x\n",
DEB_L_C_F_PREFIX_ARGS(KPML_INFO, line, call_id, fname), digit);
kpml_data = (kpml_data_t *) sll_next(s_kpml_list, NULL);
while (kpml_data) {
kpml_next_data = (kpml_data_t *) sll_next(s_kpml_list, kpml_data);
if (kpml_data->pending_sub &&
kpml_data->line == line &&
kpml_data->call_id == call_id) {
state = SUB_DATA_FOUND;
/* update the digit string */
dial_len = strlen(kpml_data->kpmlDialed);
if (dial_len >= MAX_DIALSTRING-1)
{ // not enough room
KPML_ERROR(DEB_L_C_F_PREFIX"dial_len = [%d] too large\n", DEB_L_C_F_PREFIX_ARGS(KPML_INFO, line, call_id, fname), dial_len);
return (state);
}
//If DIAL softkey is pressed, just send the NOTIFY with 423
//notfying the CCM that user is done dialing
if (digit == (char)0x82) {
kpml_generate_notify(kpml_data, FALSE, KPML_TIMER_EXPIRE,
KPML_TIMER_EXPIRE_STR);
memset(&kpml_data->kpmlDialed[0], 0, MAX_DIALSTRING);
(void) kpml_clear_data(kpml_data, kpml_data->persistent);
state = NOTIFY_SENT;
kpml_data = kpml_next_data;
continue;
}
if (digit == 0x0F) {
kpml_data->kpmlDialed[dial_len] = '#';
} else if (digit == 0x0E) {
kpml_data->kpmlDialed[dial_len] = '*';
} else {
kpml_data->kpmlDialed[dial_len] = digit;
}
kpml_data->kpmlDialed[dial_len + 1] = 00;
if (digit == BKSPACE_KEY) {
kpml_data->last_dig_bkspace = TRUE;
sstrncpy(kpml_data->kpmlDialed, "bs", sizeof(kpml_data->kpmlDialed));
result = KPML_FULLPATTERN;
} else {
result = kpml_match_pattern(kpml_data);
}
switch (result) {
case KPML_FULLPATTERN:
kpml_generate_notify(kpml_data, FALSE, KPML_SUCCESS,
KPML_SUCCESS_STR);
dp_store_digits(line, call_id,
(unsigned char)((digit == (char) BKSPACE_KEY) ?
BKSPACE_KEY : kpml_data->
kpmlDialed[dial_len]));
/* if not persistent request clear the subscription */
memset(&kpml_data->kpmlDialed[0], 0, MAX_DIALSTRING);
(void) kpml_clear_data(kpml_data, kpml_data->persistent);
state = NOTIFY_SENT;
break;
case KPML_IMMEDIATELY:
/* Do not sent out '#' key remove that from the buffer */
kpml_data->kpmlDialed[dial_len] = 00;
kpml_generate_notify(kpml_data, FALSE, KPML_USER_TERM_NOMATCH,
KPML_USER_TERM_NOMATCH_STR);
dp_store_digits(line, call_id,
(unsigned char)((digit == (char) BKSPACE_KEY) ?
BKSPACE_KEY : kpml_data->
kpmlDialed[dial_len]));
memset(&kpml_data->kpmlDialed[0], 0, MAX_DIALSTRING);
/* if not persistent request clear the subscription */
(void) kpml_clear_data(kpml_data, kpml_data->persistent);
state = NOTIFY_SENT;
break;
default:
memset(&kpml_data->kpmlDialed[0], 0, MAX_DIALSTRING);
/* Restart the digit timers */
kpml_restart_timers(kpml_data);
break;
}
}
/* if there is only one kpml_data then we should stop here.
* Memory for this is already released in kpml_generate_notify function
*/
kpml_data = kpml_next_data;
}
return (state);
}
/*
* Function: kpml_set_subscription_reject()
*
* Parameters: line, call_id to indentify line and call_id
*
* Description:
* Set variable to specify that subscription should be
* rejected on this line. There are error cases wehere INV
* has been sent premeturaly because of error condition, then
* subscription on such a line shoule be rejected.
*
* Returns: none
*/
void
kpml_set_subscription_reject (line_t line, callid_t call_id)
{
static const char fname[] = "kpml_set_subscription_reject";
kpml_data_t *kpml_data;
kpml_key_t kpml_key;
if (kpml_get_config_value() == KPML_NONE) {
return;
}
KPML_DEBUG(DEB_L_C_F_PREFIX"Reject\n", DEB_L_C_F_PREFIX_ARGS(KPML_INFO, line, call_id, fname));
kpml_create_sm_key(&kpml_key, line, call_id, NULL);
kpml_data = (kpml_data_t *) sll_find(s_kpml_list, &kpml_key);
if (kpml_data == NULL) {
kpml_data = kpml_get_new_data();
if (kpml_data == NULL) {
KPML_ERROR(KPML_F_PREFIX"No memory for subscription data\n",
fname);
return;
}
(void) sll_append(s_kpml_list, kpml_data);
kpml_data->line = line;
kpml_data->call_id = call_id;
kpml_data->pending_sub = FALSE;
kpml_data->dig_head = kpml_data->dig_tail = 0;
}
kpml_data->sub_reject = TRUE;
}
/*
* Function: check_subcription_create_error()
*
* Parameters: Kpml - kpml related data
*
* Description: Check subscription state to see if the subscription can be
* created.
*
* Returns: Kpml response code
*
*/
static kpml_resp_code_e
check_subcription_create_error (kpml_data_t *kpml_data)
{
static const char fname[] = "check_subcription_create_error";
lsm_states_t lsm_state;
lsm_state = lsm_get_state(kpml_data->call_id);
if (lsm_state == LSM_S_NONE) {
KPML_ERROR(KPML_L_C_F_PREFIX"NO call with id\n",
kpml_data->line, kpml_data->call_id, fname);
return (KPML_BAD_EVENT);
}
if ((lsm_state < LSM_S_CONNECTED) && kpml_data->sub_reject) {
KPML_ERROR(KPML_L_C_F_PREFIX"Call not in connected state\n",
kpml_data->line, kpml_data->call_id, fname);
return (KPML_BAD_EVENT);
}
/* Call is in connected state. So do not reject the subscription */
kpml_data->sub_reject = FALSE;
return (KPML_SUCCESS);
}
/*
* Function: check_if_kpml_attributes_supported()
*
* Parameters: Kpml - kpml related data
*
* Description: check if the attribute supported in the KPML request
*
* Returns: Kpml response code
*
*/
static kpml_resp_code_e
check_if_kpml_attributes_supported (KPMLRequest *kpml_sub_data)
{
if (kpml_sub_data == NULL) {
return (KPML_BAD_DOC);
}
/* 501 : bad document pre
* header is not supported. If the request carry this reject it
*/
if ((kpml_sub_data->pattern.longhold != 0) ||
(kpml_sub_data->pattern.longrepeat != 0) ||
(kpml_sub_data->pattern.nopartial != 0)) {
return (KPML_BAD_DOC);
}
return (KPML_SUCCESS);
}
/*
* Function: check_attributes_range()
*
* Parameters: Kpml - kpml related data
*
* Description: check attribute range
*
* Returns: kpml response code
*/
static kpml_resp_code_e
check_attributes_range (KPMLRequest * kpml_data)
{
return (KPML_SUCCESS);
}
/*
* Function: check_kpml_config()
*
* Parameters: line and call_id
*
* Description: check kpml config
*
* Returns: kpml response code
*/
static kpml_resp_code_e
check_kpml_config (line_t line, callid_t call_id)
{
static const char fname[] = "check_kpml_config";
lsm_states_t lsm_state;
lsm_state = lsm_get_state(call_id);
if (lsm_state == LSM_S_NONE) {
KPML_ERROR(KPML_L_C_F_PREFIX"NO call\n",
line, call_id, fname);
return (KPML_BAD_EVENT);
}
/* Get kpml configuration */
config_get_value(CFGID_KPML_ENABLED, &s_kpml_config, sizeof(s_kpml_config));
switch (lsm_state) {
case LSM_S_OFFHOOK:
case LSM_S_PROCEED:
if ((s_kpml_config == KPML_SIGNAL_ONLY) || (s_kpml_config == KPML_BOTH)) {
return (KPML_SUCCESS);
}
break;
case LSM_S_RINGOUT:
case LSM_S_CONNECTED:
case LSM_S_HOLDING:
if ((s_kpml_config == KPML_DTMF_ONLY) || (s_kpml_config == KPML_BOTH)) {
return (KPML_SUCCESS);
}
break;
default:
break;
}
KPML_ERROR(KPML_L_C_F_PREFIX"KPML disabled - Check your conifg 0- None, \
1-signaling, 2-dtmf 3-both\n", line, call_id, fname);
return (KPML_BAD_EVENT);
}
/*
* Function: kpml_treat_enterkey()
*
* Parameters: Kpml - kpml related data
*
* Description: Currently we support only '#' in the enter key string.
* If there is anything apart from '#' reject the subscription
*
*
* Returns: kpml_resp_code_e
*/
static kpml_resp_code_e
kpml_treat_enterkey (kpml_data_t *kpml_data, char *enter_str)
{
/* Enterkey can have only 2 values empty or "#" */
if (enter_str[0] == NUL) {
kpml_data->enterkey = FALSE;
} else if (!strcmp(enter_str, KPML_ENTER_STR)) {
kpml_data->enterkey = TRUE;
} else {
return (KPML_BAD_DOC);
}
return (KPML_SUCCESS);
}
/*
* Function: kpml_treat_regex()
*
* Parameters: Kpml - kpml related data
*
* Description: Skip white space, and check content of regex
* Check for backspace presence as well. Regular
* expression can contain only sequence of 'x', space,
* '|' and 'bs' (for backspace).
* Example : x | bs, xxx|bs, x|bs etc.
*
* Returns: kpml_resp_code_e
*/
static kpml_resp_code_e
kpml_treat_regex (kpml_data_t *kpml_data)
{
static const char fname[] = "kpml_treat_regex";
short indx = 0, char_inx, i, regex_idx = 0;
char regex_temp[32];
kpml_data->enable_backspace = FALSE;
KPML_DEBUG(DEB_L_C_F_PREFIX"regex=%u\n",
DEB_L_C_F_PREFIX_ARGS(KPML_INFO, kpml_data->line, kpml_data->call_id, fname),
kpml_data->regex[indx].regexData);
/* skip white space and check for backspace */
while (indx < NUM_OF_REGX) {
char_inx = 0;
i = 0;
regex_idx = 0;
while (kpml_data->regex[indx].regexData[char_inx]) {
switch (kpml_data->regex[indx].regexData[char_inx]) {
case ' ':
break;
case '|':
break;
case 'b':
if (kpml_data->regex[indx].regexData[char_inx + 1] == 's') {
char_inx++;
kpml_data->enable_backspace = TRUE;
} else {
return (KPML_BAD_DOC);
}
break;
case 'x':
default:
regex_temp[i++] = kpml_data->regex[indx].regexData[char_inx];
break;
}
char_inx++;
}
regex_temp[i] = NUL;
/* parse regex string */
if (kpml_parse_regex_str(&(regex_temp[0]),
&(kpml_data->regex_match[indx])) !=
KPML_STATUS_OK) {
KPML_ERROR(KPML_F_PREFIX"Regex parse error.\n",fname);
return (KPML_BAD_DOC);
}
/* create digit map string such as xx to match the digits */
while (regex_idx < kpml_data->regex_match[indx].num_digits) {
kpml_data->regex[indx].regexData[regex_idx++] = 'x';
}
kpml_data->regex[indx].regexData[regex_idx] = NUL;
/* Check for next <regex> string */
indx++;
}
return (KPML_SUCCESS);
}
/*
* Function: kpml_update_data()
*
* Parameters: Kpml - kpml related data from incoming subscribe
*
* Description: Add a dial template to the known list of templates
*
* Returns: kpml_data_t
*/
static kpml_data_t *
kpml_update_data (kpml_data_t *kpml_data, KPMLRequest *kpml_sub_data)
{
static const char fname[] = "kpml_update_data";
if ((kpml_sub_data == NULL) || (kpml_data == NULL)) {
return (kpml_data);
}
memcpy((char *) &(kpml_data->regex),
(char *) &(kpml_sub_data->pattern.regex),
sizeof(Regex) * NUM_OF_REGX);
kpml_data->persistent = kpml_sub_data->pattern.persist;
kpml_data->inttimeout = kpml_sub_data->pattern.interdigittimer;
kpml_data->crittimeout = kpml_sub_data->pattern.criticaldigittimer;
kpml_data->extratimeout = kpml_sub_data->pattern.extradigittimer;
kpml_data->flush = kpml_sub_data->pattern.flush;
/* clear all collected digits, SUB requested with flush */
if (kpml_sub_data->pattern.flush) {
kpml_data->kpmlDialed[0] = 00;
}
kpml_data->longhold = kpml_sub_data->pattern.longhold;
kpml_data->longrepeat = kpml_sub_data->pattern.longrepeat;
kpml_data->nopartial = kpml_sub_data->pattern.nopartial;
KPML_DEBUG(DEB_L_C_F_PREFIX"regex=%u"
"persistent=%d int-timer=%u critic-timer=%u, extra-timer=%u"
"flush=%d longhold=%d longrepeat=%d nopartial=%d\n",
DEB_L_C_F_PREFIX_ARGS(KPML_INFO, kpml_data->line, kpml_data->call_id, fname),
kpml_data->regex, kpml_data->persistent, kpml_data->inttimeout,
kpml_data->crittimeout, kpml_data->extratimeout,
kpml_data->flush, kpml_data->longhold,
kpml_data->longrepeat, kpml_data->nopartial);
return (kpml_data);
}
/*
* Function: kpml_terminate_subscription()
*
* Parameters: ccsip_sub_not_data_t - msg passed from SIP stack
*
* Description: Received terminate event from sip stack, probably
* something wrong and wants to terminate the subscription.
*
* Returns: None
*/
static void
kpml_terminate_subscription (ccsip_sub_not_data_t *msg)
{
static const char fname[] = "kpml_terminate_subscribe";
kpml_data_t *kpml_data = NULL;
boolean normal_terminate;
lsm_lcb_t *lcb;
KPML_DEBUG(DEB_F_PREFIX"entered.\n", DEB_F_PREFIX_ARGS(KPML_INFO, fname));
if (kpml_get_config_value() == KPML_NONE) {
return;
}
if (msg->sub_id == (unsigned int)-1) {
KPML_ERROR(KPML_L_C_F_PREFIX"Invalid sub_id=%d\n", msg->line_id,
msg->gsm_id, fname, msg->sub_id);
return;
}
KPML_DEBUG(DEB_L_C_F_PREFIX"sub_id=%d, reason=%d\n",
DEB_L_C_F_PREFIX_ARGS(KPML_INFO, msg->line_id, msg->gsm_id, fname),
msg->sub_id, msg->reason_code);
/*
* If the terminate reason is caused by local action,
* then there is no need to send any notify to the network.
*/
switch (msg->reason_code) {
case SM_REASON_CODE_SHUTDOWN:
case SM_REASON_CODE_ROLLOVER:
case SM_REASON_CODE_RESET_REG:
/*
* These errors are caused by failure or system being shutting down.
* Subscription manager will automatically clean up the subscription
* The application just needs to clean up the associated data
* strutures.
*/
normal_terminate = FALSE;
break;
default:
normal_terminate = TRUE;
break;
}
//Acquire kpml lock
cprGetMutex(kpml_mutex);
kpml_data = kpml_data_for_subid(msg->sub_id);
if (kpml_data) {
kpml_data->persistent = KPML_ONE_SHOT;
/*
* For ignore generating notify and release in the case
* of non normal terminate as determined above.
*/
if (normal_terminate) {
kpml_generate_notify(kpml_data, FALSE,
KPML_SUB_EXPIRE,
KPML_SUB_EXPIRE_STR);
/* Tell call control to generate reorder */
lcb = lsm_get_lcb_by_call_id(kpml_data->call_id);
if (lcb && lcb->state < LSM_S_RINGOUT) {
cc_release(CC_SRC_GSM, kpml_data->call_id, kpml_data->line,
CC_CAUSE_CONGESTION, NULL, NULL);
}
}
(void) kpml_clear_data(kpml_data, KPML_ONE_SHOT);
}
//Release kpml lock
cprReleaseMutex(kpml_mutex);
/*
* For ignore generating notify and release in the case
* of non normal terminate as determined above.
*/
if (normal_terminate) {
(void) sub_int_subscribe_term(msg->sub_id, TRUE,
msg->request_id, msg->event);
}
KPML_DEBUG(DEB_F_PREFIX"exit.\n", DEB_F_PREFIX_ARGS(KPML_INFO, fname));
}
/*
* Function: kpml_receive_subscribe()
*
* Parameters: ccsip_sub_not_data_t - msg passed from SIP stack
*
* Description: Responsible for handling incoming SUBSCRIBE with KPML
* Checks if the subscription expired or it is a re-subscribe
* sents out intial notification, starts all the timers
*
* Returns: None
*/
static void
kpml_receive_subscribe (ccsip_sub_not_data_t *msg)
{
static const char fname[] = "kpml_receive_subscribe";
kpml_data_t *kpml_data;
kpml_key_t kpml_key;
kpml_resp_code_e resp_code = KPML_SUCCESS;
KPMLRequest *kpml_sub_data = NULL;
lsm_states_t lsm_state;
char *regx_prnt = NULL;
boolean is_empty_resubscribe = FALSE;
if (kpml_get_config_value() == KPML_NONE) {
KPML_DEBUG(DEB_L_C_F_PREFIX"KPML disabled in config.\n",
DEB_L_C_F_PREFIX_ARGS(KPML_INFO, msg->line_id, msg->gsm_id, fname));
return;
}
if (msg->line_id == 0 || msg->gsm_id == 0) {
KPML_ERROR(KPML_L_C_F_PREFIX"Line or call_id not correct\n",
msg->line_id, msg->gsm_id, fname);
(void) sub_int_subscribe_ack(CC_SRC_GSM, CC_SRC_SIP, msg->sub_id,
KPML_BAD_EVENT, msg->sub_duration);
return;
}
kpml_create_sm_key(&kpml_key, (line_t) msg->line_id, (callid_t) msg->gsm_id,
NULL);
kpml_data = (kpml_data_t *) sll_find(s_kpml_list, &kpml_key);
if (msg->u.subs_ind_data.eventData) {
kpml_sub_data = &(msg->u.subs_ind_data.eventData->u.kpml_request);
}
/* There is active KPML subscription on this dialog
* so check if the existing subscription need to be
* terminated or refreshed
*/
if (kpml_data) {
if (kpml_data->pending_sub == TRUE) {
kpml_data->sub_duration = msg->sub_duration;
/* generate SUBSCRIBE response */
kpml_generate_subscribe_response(kpml_data, KPML_SUCCESS);
/* KPML receives a new subscription for the same dialog.
* Terminate the current subscription if the new subscription
* is not related
*/
if (kpml_data->sub_id != msg->sub_id) {
KPML_DEBUG(DEB_L_C_F_PREFIX"Terminate previous subscription \
sub_id = %x\n",
DEB_L_C_F_PREFIX_ARGS(KPML_INFO, kpml_data->line, kpml_data->call_id, fname),
kpml_data->sub_id);
kpml_generate_notify(kpml_data, FALSE,
KPML_SUB_EXPIRE,
KPML_SUB_EXPIRE_STR);
(void) sub_int_subscribe_term(kpml_data->sub_id, TRUE,
msg->request_id, msg->event);
}
KPML_DEBUG(DEB_L_C_F_PREFIX"Refresh Subscription\n",
DEB_L_C_F_PREFIX_ARGS(KPML_INFO, kpml_data->line, kpml_data->call_id, fname));
/* Refresh subscription without kpml body. This is CCM current behavior
* which seems not to follow rfc4730. We support this for compatibility
* purpose, and intepret the body to be exactly the same as previous
* subscription. Only new subscription time changes.
*/
if (kpml_sub_data == NULL) {
kpml_clear_timers(kpml_data);
is_empty_resubscribe = TRUE;
} else if (kpml_clear_data(kpml_data, KPML_ONE_SHOT)) {
kpml_data = NULL;
}
} else {
/* Already has subscription data created to quaratining
* digits, but there is no active SIP subscription.
*/
kpml_data = kpml_update_data(kpml_data, kpml_sub_data);
KPML_DEBUG(DEB_L_C_F_PREFIX"Activate Subscription\n",
DEB_L_C_F_PREFIX_ARGS(KPML_INFO, kpml_data->line, kpml_data->call_id, fname));
}
}
if (kpml_sub_data) {
regx_prnt = kpml_sub_data->pattern.regex.regexData;
}
DEF_DEBUG(DEB_L_C_F_PREFIX"Regex=%s\n",
DEB_L_C_F_PREFIX_ARGS(KPML_INFO, msg->line_id, msg->gsm_id, fname), regx_prnt);
/* Above block can terminate existing subscription */
if (!kpml_data) {
/* New subscription - allocate data */
kpml_data = kpml_get_new_data();
if (kpml_data == NULL) {
KPML_ERROR(KPML_L_C_F_PREFIX"No memory for subscription data\n",
msg->line_id, msg->gsm_id, fname);
return;
}
(void) kpml_update_data(kpml_data, kpml_sub_data);
(void) sll_append(s_kpml_list, kpml_data);
}
kpml_data->call_id = msg->gsm_id;
kpml_data->line = msg->line_id;
kpml_data->sub_id = msg->sub_id;
/* Terminate the subcription */
if (msg->sub_duration == 0) {
/* Update KPML document in the record */
kpml_data = kpml_update_data(kpml_data, kpml_sub_data);
KPML_DEBUG(DEB_L_C_F_PREFIX"Terminate Subscription.\n",
DEB_L_C_F_PREFIX_ARGS(KPML_INFO, kpml_data->line, kpml_data->call_id, fname));
/* check the regular expression and backspace */
(void) kpml_treat_regex(kpml_data);
/* Remove backspace softkey */
ui_control_featurekey_bksp(msg->line_id, msg->gsm_id,
kpml_data->last_dig_bkspace);
/* Set call to proceeding state */
cc_proceeding(CC_SRC_SIP, kpml_data->call_id, kpml_data->line, NULL);
kpml_data->persistent = KPML_ONE_SHOT;
kpml_generate_notify(kpml_data, FALSE,
KPML_SUB_EXPIRE,
KPML_SUB_EXPIRE_STR);
/* Empty all the digits present in the list */
kpml_data->dig_head = kpml_data->dig_tail = 0;
(void) kpml_clear_data(kpml_data, KPML_ONE_SHOT);
/* free event data allocated by SIP stack */
if (msg->u.subs_ind_data.eventData) {
cpr_free(msg->u.subs_ind_data.eventData);
}
return;
}
kpml_data->pending_sub = TRUE;
(void) kpml_start_subscription_timer(kpml_data, msg->sub_duration);
kpml_start_timers(kpml_data);
/* check config to send appropriate response. Check subscription reject
* status.
*/
if (((resp_code = check_kpml_config(msg->line_id, msg->gsm_id)) != KPML_SUCCESS) ||
((resp_code = check_subcription_create_error(kpml_data)) != KPML_SUCCESS)) {
kpml_generate_subscribe_response(kpml_data, resp_code);
if (kpml_clear_data(kpml_data, KPML_ONE_SHOT)) {
kpml_data = NULL;
}
if (msg->u.subs_ind_data.eventData) {
cpr_free(msg->u.subs_ind_data.eventData);
}
return;
} else {
/* Accept the subscription with a 200 OK response */
kpml_generate_subscribe_response(kpml_data, SIP_SUCCESS_SETUP);
}
/* Error checking for incoming KPML data. If there are attributes
* which are not supported or if the values are not in the range
* or KPML document is not present then generate error
*/
if (!is_empty_resubscribe && ((kpml_sub_data == NULL) ||
((resp_code = check_if_kpml_attributes_supported(kpml_sub_data)) != KPML_SUCCESS) ||
((resp_code = check_attributes_range(kpml_sub_data)) != KPML_SUCCESS) ||
((resp_code = kpml_treat_regex(kpml_data)) != KPML_SUCCESS) ||
((resp_code = kpml_treat_enterkey(kpml_data,
kpml_sub_data->pattern.enterkey)) != KPML_SUCCESS))) {
KPML_ERROR(KPML_F_PREFIX"Error Resp code = %d\n", fname, resp_code);
kpml_generate_notify(kpml_data, FALSE, resp_code,
KPML_ATTR_NOT_SUPPORTED_STR);
if (kpml_clear_data(kpml_data, KPML_ONE_SHOT)) {
kpml_data = NULL;
}
cpr_free(msg->u.subs_ind_data.eventData);
return;
} else {
lsm_state = lsm_get_state(kpml_data->call_id);
/* When GSM receives FEATURE event transitions its state. To avoid
* transition during DTMF phase do not send out the event if the
* lsm state is > RINGOUT state. GSM has to know about the subscription
* only to extend the collect info (KPML_COLLECT_INFO)
*/
if ((lsm_state != LSM_S_NONE) && (lsm_state < LSM_S_RINGOUT)) {
cc_feature(CC_SRC_GSM, kpml_data->call_id, kpml_data->line,
CC_FEATURE_SUBSCRIBE, NULL);
}
kpml_generate_notify(kpml_data, TRUE, KPML_SUCCESS, KPML_TRYING_STR);
kpml_update_quarantined_digits(kpml_data);
}
/* If the backspace key request is set then enable backspace key */
ui_control_featurekey_bksp(msg->line_id, msg->gsm_id,
kpml_data->enable_backspace);
/* free event data allocated by SIP stack */
if (msg->u.subs_ind_data.eventData) {
cpr_free(msg->u.subs_ind_data.eventData);
}
}
/*
* Function: kpml_generate_subscribe_response()
*
* Parameters: ccsip_sub_not_data_t - msg data from SIP
*
* Description: This routine is called to generate 200 Ok for incoming
* KPML SUBSCRIBE.
*
* Returns: None
*/
static void
kpml_generate_subscribe_response (kpml_data_t * kpml_data, int resp_code)
{
static const char fname[] = "kpml_generate_subscribe_response";
KPML_DEBUG(DEB_L_C_F_PREFIX"SUB response\n",
DEB_L_C_F_PREFIX_ARGS(KPML_INFO, kpml_data->line, kpml_data->call_id, fname));
(void) sub_int_subscribe_ack(CC_SRC_GSM, CC_SRC_SIP, kpml_data->sub_id,
(uint16_t) resp_code, kpml_data->sub_duration);
}
/*
* Function: kpml_receive_notify_response()
*
* Parameters: ccsip_sub_not_data_t - msg data from SIP
*
* Description: This routine is called when there is a NOTIFY
* response from subscription manager.
*
* Returns: None
*/
void
kpml_receive_notify_response (ccsip_sub_not_data_t *msg)
{
static const char fname[] = "kpml_receive_notify_response";
kpml_data_t *kpml_data;
kpml_key_t kpml_key;
KPML_DEBUG(DEB_L_C_F_PREFIX"Notify response\n",
DEB_L_C_F_PREFIX_ARGS(KPML_INFO, msg->line_id, msg->gsm_id, fname));
kpml_create_sm_key(&kpml_key, (line_t) msg->line_id, (callid_t) msg->gsm_id,
NULL);
kpml_data = (kpml_data_t *) sll_find(s_kpml_list, &kpml_key);
/* Do not terminate subscription if the subscription is
* persistent or result is not error
*/
if (kpml_data) {
if (kpml_data->last_dig_bkspace &&
msg->u.notify_result_data.status_code == SIP_SUCCESS_SETUP) {
/* remove last digit which is backspace */
dp_delete_last_digit(msg->line_id, msg->gsm_id);
kpml_data->last_dig_bkspace = FALSE;
} else if (msg->u.notify_result_data.status_code == REQUEST_TIMEOUT) {
(void) kpml_clear_data(kpml_data, KPML_ONE_SHOT);
(void) sub_int_subscribe_term(msg->sub_id, TRUE, msg->request_id,
msg->event);
return;
}
/* See if there are digits collected before the subscription
* if so then update the kpml_digit buffer and match the pattern
*/
kpml_update_quarantined_digits(kpml_data);
} else {
(void) sub_int_subscribe_term(msg->sub_id, TRUE, msg->request_id,
msg->event);
}
}
/*
* Function: kpml_generate_notify()
*
* Parameters: kpml_data - subscription data
* resp_code - response code for the NOTIFY
* resp_text - response text
*
* Description: Notify KPML response data. This can be sucessful or error.
*
* Returns: None
*/
static void
kpml_generate_notify (kpml_data_t *kpml_data, boolean no_body,
unsigned int resp_code, char *resp_text)
{
static const char fname[] = "kpml_generate_notify";
char resp_str[10];
ccsip_event_data_t *peventData = NULL;
DEF_DEBUG(DEB_L_C_F_PREFIX"RESP %u: \n",
DEB_L_C_F_PREFIX_ARGS(KPML_INFO, kpml_data->line, kpml_data->call_id, fname), resp_code);
if (no_body == FALSE) {
// allocate memory to hold events and that will be freed in the stack
peventData = (ccsip_event_data_t *)
cpr_malloc(sizeof(ccsip_event_data_t));
if (peventData == NULL) {
KPML_ERROR(KPML_L_C_F_PREFIX"No memory for eventdata\n",
kpml_data->line, kpml_data->call_id, fname);
return;
}
memset(peventData, 0, sizeof(ccsip_event_data_t));
sstrncpy(peventData->u.kpml_response.version, KPML_VER_STR, sizeof(peventData->u.kpml_response.version));
snprintf(resp_str, 10, "%d", resp_code);
sstrncpy(peventData->u.kpml_response.code, resp_str, sizeof(peventData->u.kpml_response.code));
if (resp_code == KPML_SUCCESS) {
sstrncpy(&(peventData->u.kpml_response.digits[0]),
&(kpml_data->kpmlDialed[0]), sizeof(peventData->u.kpml_response.digits));
}
if (kpml_data->flush == FALSE) {
sstrncpy(peventData->u.kpml_response.forced_flush, "false",
sizeof(peventData->u.kpml_response.forced_flush));
} else {
sstrncpy(peventData->u.kpml_response.forced_flush, "true",
sizeof(peventData->u.kpml_response.forced_flush));
}
sstrncpy(peventData->u.kpml_response.tag,
&(kpml_data->regex->tag[0]), sizeof(peventData->u.kpml_response.tag));
sstrncpy(peventData->u.kpml_response.text,
resp_text, sizeof(peventData->u.kpml_response.text));
peventData->type = EVENT_DATA_KPML_RESPONSE;
peventData->next = NULL;
}
(void) sub_int_notify(CC_SRC_GSM, CC_SRC_SIP, kpml_data->sub_id,
/* kpml_receive_notify_response */ NULL,
SUB_MSG_KPML_NOTIFY_ACK, peventData,
(kpml_data->persistent ==
KPML_ONE_SHOT ? SUBSCRIPTION_TERMINATE :
SUBSCRIPTION_NULL));
}
/**
*
* Function to return the message command name for KPML module
*
* @param uint32_t command
*
* @return char * pointer to command name
*
* @pre (none)
*/
char *
kpml_get_msg_string (uint32_t cmd)
{
switch (cmd) {
case SUB_MSG_KPML_SUBSCRIBE:
return("KPML_SUB");
case SUB_MSG_KPML_TERMINATE:
return("KPML_TERMINATE");
case SUB_MSG_KPML_NOTIFY_ACK:
return("KPML_NOT_ACK");
case SUB_MSG_KPML_SUBSCRIBE_TIMER:
return("KPML_SUB_TIMER");
case SUB_MSG_KPML_DIGIT_TIMER:
return("KPML_DIGIT_TIMER");
default:
return ("KPML_UNKNOWN_CMD");
}
}
void
kpml_process_msg (uint32_t cmd, void *msg)
{
static const char fname[] = "kpml_process_msg";
KPML_DEBUG(DEB_F_PREFIX"cmd= %s\n", DEB_F_PREFIX_ARGS(KPML_INFO, fname), kpml_get_msg_string(cmd));
if (s_kpml_list == NULL) {
/* KPML is down do not process any message */
KPML_DEBUG(DEB_F_PREFIX"KPML is down.\n", DEB_F_PREFIX_ARGS(KPML_INFO, fname));
return;
}
switch (cmd) {
case SUB_MSG_KPML_SUBSCRIBE:
kpml_receive_subscribe((ccsip_sub_not_data_t *) msg);
break;
case SUB_MSG_KPML_TERMINATE:
kpml_terminate_subscription((ccsip_sub_not_data_t *) msg);
break;
case SUB_MSG_KPML_NOTIFY_ACK:
kpml_receive_notify_response((ccsip_sub_not_data_t *) msg);
break;
case SUB_MSG_KPML_SUBSCRIBE_TIMER:
kpml_subscription_timer_event((void **) msg);
break;
case SUB_MSG_KPML_DIGIT_TIMER:
kpml_inter_digit_timer_event((void **) msg);
break;
default:
KPML_ERROR(KPML_F_PREFIX"Bad Cmd received: 0x%x.\n", fname, cmd);
break;
}
}
/*
* Function: kpmlmap_show
*
* Parameters: standard args
*
* Description: Display the current dialplan (if any)
*
* Returns:
*
*/
static void
kpmlmap_show (void)
{
static const char *fname="kpmlmap_show";
kpml_data_t *kpml_data;
int counter;
kpml_data = (kpml_data_t *) sll_next(s_kpml_list, NULL);
while (kpml_data != NULL) {
KPML_DEBUG(DEB_L_C_F_PREFIX"Pending sub duration=%-8d",
DEB_L_C_F_PREFIX_ARGS(KPML_INFO, kpml_data->line, kpml_data->call_id, fname),
kpml_data->sub_duration);
for (counter = 0; counter < NUM_OF_REGX; counter++) {
KPML_DEBUG(DEB_F_PREFIX"%-4s %-10s %-5s\n", DEB_F_PREFIX_ARGS(KPML_INFO, fname),
kpml_data->regex[counter].regexData,
kpml_data->regex->tag, kpml_data->kpmlDialed);
}
kpml_data = (kpml_data_t *) sll_next(s_kpml_list, kpml_data);
}
}
/*
* Function: show_kpmlmap_cmd
*
* Parameters: standard args
*
* Description: Display the current kpml subscription details (if any)
*
* Returns:
*
*/
cc_int32_t
show_kpmlmap_cmd (cc_int32_t argc, const char *argv[])
{
kpml_data_t *kpml_data;
int counter;
debugif_printf("Pending KPML requests are....\n");
kpml_data = (kpml_data_t *) sll_next(s_kpml_list, NULL);
debugif_printf("\n--------------- KPML SUBSCRIPTIONS-------------------");
debugif_printf("\nLine Call_Id Expire Regx Tag Digits ");
debugif_printf
("\n------------------------------------------------------\n");
while (kpml_data != NULL) {
debugif_printf("%-4d %-5d %-8lu ",
kpml_data->line, kpml_data->call_id,
kpml_data->sub_duration);
for (counter = 0; counter < NUM_OF_REGX; counter++) {
debugif_printf("%-4s %-10s %-5s\n",
kpml_data->regex[counter].regexData,
kpml_data->regex->tag, kpml_data->kpmlDialed);
}
kpml_data = (kpml_data_t *) sll_next(s_kpml_list, kpml_data);
}
return (0);
}
/*
* Function: kpml_init()
*
* Parameters: none
*
* Description: Register with Subscription manager for any imcoming
* KPML subscribe messages
*
* Returns: None
*/
void
kpml_init (void)
{
KPML_DEBUG(DEB_F_PREFIX"entered.\n", DEB_F_PREFIX_ARGS(KPML_INFO, "kpml_init"));
if (!kpml_mutex) {
kpml_mutex = cprCreateMutex("kpml lock");
if (!kpml_mutex) {
KPML_ERROR(DEB_F_PREFIX"unable to create kpml lock \n", "kpml_init");
}
}
(void) sub_int_subnot_register(CC_SRC_GSM, CC_SRC_SIP,
CC_SUBSCRIPTIONS_KPML,
/*kpml_receive_subscribe */ NULL, CC_SRC_GSM,
SUB_MSG_KPML_SUBSCRIBE,
NULL, SUB_MSG_KPML_TERMINATE, 0, 0);
/* allocate and initialize kpml list */
s_kpml_list = sll_create((sll_match_e(*)(void *, void *))
kpml_match_line_call_id);
}
/*
* Function: kpml_shutdown()
*
* Parameters: none
*
* Description: The function shutdowns KPML and cleans up data.
*
* NOTE:
*
* The assumption is existing subscriptions with SUB/NOT are
* terminated and released elsewhere i.e. kpml shutdown does not
* support shuting down alone while the rest of the SIP/GSM
* components are up. Therefore the shutdown function will not
* send any subscription termination to SUB/NOT which it will
* be droped by SIP stack since it is also being shutdown.
*
* Returns: None
*/
void
kpml_shutdown (void)
{
kpml_data_t *kpml_data;
KPML_DEBUG(DEB_F_PREFIX"entered.\n", DEB_F_PREFIX_ARGS(KPML_INFO, "kpml_shutdown"));
//acquire kpml lock
(void) cprGetMutex(kpml_mutex);
kpml_data = (kpml_data_t *) sll_next(s_kpml_list, NULL);
while (kpml_data != NULL) {
/*
* Clean up, remove from the list and deallocate the kpml_data
*/
(void) kpml_clear_data(kpml_data, KPML_ONE_SHOT);
/*
* The kpml_data is already freed above, get the next one
* from the list's head
*/
kpml_data = (kpml_data_t *) sll_next(s_kpml_list, NULL);
}
sll_destroy(s_kpml_list);
s_kpml_list = NULL;
(void) cprReleaseMutex(kpml_mutex);
KPML_DEBUG(DEB_F_PREFIX"exit.\n", DEB_F_PREFIX_ARGS(KPML_INFO, "kpml_shutdown"));
}