/* 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 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")); }