From 7b4838035a8f9ab7eef200df7b62ce874c77d789 Mon Sep 17 00:00:00 2001 From: Moises Silva Date: Fri, 8 Aug 2014 02:38:04 -0400 Subject: [PATCH] OPENZAP-238: [freetdm] Enable GSM immediate forwarding logic Use the new parameter immediate-forwarding-numbers to configure immediate forwarding logic that emulates hunt groups The parameter syntax is: [:] Multiple elements can be specified separated by commas If the is specified, the span will be checked for availability, if available, its number will be selected for forwarding, otherwise next number will be checked Forwarding is enabled as soon as a channel is answered and its disabled when the channel is hung up --- libs/freetdm/conf/freetdm.conf.xml | 54 +++ libs/freetdm/src/ftmod/ftmod_gsm/ftmod_gsm.c | 350 ++++++++++++------- 2 files changed, 270 insertions(+), 134 deletions(-) diff --git a/libs/freetdm/conf/freetdm.conf.xml b/libs/freetdm/conf/freetdm.conf.xml index dcbaafe2db..9b9912006d 100644 --- a/libs/freetdm/conf/freetdm.conf.xml +++ b/libs/freetdm/conf/freetdm.conf.xml @@ -571,5 +571,59 @@ with the signaling protocols that you can run on top of your I/O interfaces. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/freetdm/src/ftmod/ftmod_gsm/ftmod_gsm.c b/libs/freetdm/src/ftmod/ftmod_gsm/ftmod_gsm.c index 974ef2c516..9e136313df 100755 --- a/libs/freetdm/src/ftmod/ftmod_gsm/ftmod_gsm.c +++ b/libs/freetdm/src/ftmod/ftmod_gsm/ftmod_gsm.c @@ -106,10 +106,19 @@ typedef struct ftdm_gsm_span_data_s { ftdm_channel_t *bchan; int32_t call_id; uint32_t sms_id; - char conditional_forward_number[255]; + char conditional_forward_prefix[10]; + char conditional_forward_number[50]; + char immediate_forward_prefix[10]; + struct { + char number[50]; + char span[50]; + } immediate_forward_numbers[10]; + char disable_forward_number[50]; ftdm_sched_t *sched; ftdm_timer_id_t conditional_forwarding_timer; - ftdm_bool_t init_forwarding; + ftdm_timer_id_t immediate_forwarding_timer; + ftdm_bool_t init_conditional_forwarding; + ftdm_bool_t startup_forwarding_disabled; } ftdm_gsm_span_data_t; // command handler function type. @@ -230,9 +239,11 @@ done: static void ftdm_gsm_enable_conditional_forwarding(void *data) { + char number[255]; ftdm_gsm_span_data_t *gsm_data = data; - ftdm_log_chan(gsm_data->bchan, FTDM_LOG_NOTICE, "Enabling conditional forwarding to %s\n", gsm_data->conditional_forward_number); - ftdm_gsm_make_raw_call(data, gsm_data->conditional_forward_number); + snprintf(number, sizeof(number), "%s%s", gsm_data->conditional_forward_prefix, gsm_data->conditional_forward_number); + ftdm_log_chan(gsm_data->bchan, FTDM_LOG_INFO, "Enabling conditional forwarding to %s\n", number); + ftdm_gsm_make_raw_call(data, number); } static void on_wat_span_status(unsigned char span_id, wat_span_status_t *status) @@ -258,12 +269,12 @@ static void on_wat_span_status(unsigned char span_id, wat_span_status_t *status) } else { ftdm_log_chan_msg(gsm_data->bchan, FTDM_LOG_INFO, "Signaling is now down\n"); } - if (gsm_data->init_forwarding == FTDM_TRUE && !ftdm_strlen_zero_buf(gsm_data->conditional_forward_number)) { - ftdm_sched_timer(gsm_data->sched, "conditional_forwarding_delay", 500, + if (gsm_data->init_conditional_forwarding == FTDM_TRUE && !ftdm_strlen_zero_buf(gsm_data->conditional_forward_number)) { + ftdm_sched_timer(gsm_data->sched, "conditional_forwarding_delay", 1000, ftdm_gsm_enable_conditional_forwarding, gsm_data, &gsm_data->conditional_forwarding_timer); - gsm_data->init_forwarding = FTDM_FALSE; + gsm_data->init_conditional_forwarding = FTDM_FALSE; } } break; @@ -754,136 +765,164 @@ static ftdm_state_map_t gsm_state_map = { } }; -static ftdm_status_t ftdm_gsm_state_advance(ftdm_channel_t *ftdmchan) +#define immediate_forward_enabled(gsm_data) !ftdm_strlen_zero_buf(gsm_data->immediate_forward_numbers[0].number) + +static void perform_enable_immediate_forward(void *data) { + ftdm_span_t *fwd_span = NULL; + ftdm_gsm_span_data_t *fwd_gsm_data = NULL; + char *fwd_span_name = NULL; + char *number = NULL; + char cmd[100]; + int i = 0; + ftdm_channel_t *ftdmchan = data; + ftdm_gsm_span_data_t *gsm_data = ftdmchan->span->signal_data; - ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Executing state handler for %s\n", ftdm_channel_state2str(ftdmchan->state)); - - ftdm_channel_complete_state(ftdmchan); - - switch (ftdmchan->state) { - - /* starting an incoming call */ - case FTDM_CHANNEL_STATE_COLLECT: - { - - - } - break; - - /* starting an outgoing call */ - case FTDM_CHANNEL_STATE_DIALING: - { - uint32_t interval = 0; - - ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Starting outgoing call with interval %d\n", interval); - - { - - ftdm_gsm_span_data_t *gsm_data; - wat_con_event_t con_event; - gsm_data = ftdmchan->span->signal_data; - gsm_data->call_id = GSM_OUTBOUND_CALL_ID; - memset(&con_event, 0, sizeof(con_event)); - ftdm_set_string(con_event.called_num.digits, ftdmchan->caller_data.dnis.digits); - ftdm_log(FTDM_LOG_DEBUG, "Dialing number %s\n", con_event.called_num.digits); - wat_con_req(ftdmchan->span->span_id, gsm_data->call_id , &con_event); - - SEND_STATE_SIGNAL(FTDM_SIGEVENT_DIALING); - - - } - - - } - break; - - /* incoming call was offered */ - case FTDM_CHANNEL_STATE_RING: - - /* notify the user about the new call */ - - ftdm_log(FTDM_LOG_INFO, "Answering Incomming Call\r\n"); - SEND_STATE_SIGNAL(FTDM_SIGEVENT_START); - - break; - - /* the call is making progress */ - case FTDM_CHANNEL_STATE_PROGRESS: - case FTDM_CHANNEL_STATE_PROGRESS_MEDIA: - { - SEND_STATE_SIGNAL(FTDM_SIGEVENT_PROGRESS_MEDIA); - ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_UP); - } - break; - - /* the call was answered */ - case FTDM_CHANNEL_STATE_UP: - { - if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) { - - SEND_STATE_SIGNAL(FTDM_SIGEVENT_UP); - } - else { - ftdm_gsm_span_data_t *gsm_data; - gsm_data = ftdmchan->span->signal_data; - wat_con_cfm(ftdmchan->span->span_id, gsm_data->call_id); - } - - - } - break; - - /* just got hangup */ - case FTDM_CHANNEL_STATE_HANGUP: - { - ftdm_gsm_span_data_t *gsm_data; - gsm_data = ftdmchan->span->signal_data; - wat_rel_req(ftdmchan->span->span_id, gsm_data->call_id); - gsm_data->call_id = 0; - SEND_STATE_SIGNAL(FTDM_SIGEVENT_STOP); - } - break; - - case FTDM_CHANNEL_STATE_TERMINATING: - { - SEND_STATE_SIGNAL(FTDM_SIGEVENT_STOP); - } - break; - - /* finished call for good */ - case FTDM_CHANNEL_STATE_DOWN: - { - ftdm_channel_t *closed_chan; - closed_chan = ftdmchan; - ftdm_channel_close(&closed_chan); - ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "State processing ended.\n"); - SEND_STATE_SIGNAL(FTDM_SIGEVENT_STOP); - } - break; - - /* INDICATE_RINGING doesn't apply to MFC/R2. maybe we could generate a tone */ - case FTDM_CHANNEL_STATE_RINGING: - ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "RINGING indicated, ignoring it as it doesn't apply to MFC/R2\n"); - SEND_STATE_SIGNAL(FTDM_SIGEVENT_RINGING); - - break; - - /* put the r2 channel back to IDLE, close ftdmchan and set it's state as DOWN */ - case FTDM_CHANNEL_STATE_RESET: - { - ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "RESET indicated, putting the R2 channel back to IDLE\n"); - ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_DOWN); - } - break; - - default: - { - ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Unhandled channel state change: %s\n", ftdm_channel_state2str(ftdmchan->state)); - } - break; + for (i = 0; i < ftdm_array_len(gsm_data->immediate_forward_numbers); i++) { + fwd_span_name = gsm_data->immediate_forward_numbers[i].span; + fwd_span = NULL; + if (!ftdm_strlen_zero_buf(fwd_span_name) && + ftdm_span_find_by_name(fwd_span_name, &fwd_span) != FTDM_SUCCESS) { + continue; + } + fwd_gsm_data = fwd_span ? fwd_span->signal_data : NULL; + if (fwd_gsm_data && fwd_gsm_data->call_id) { + /* span busy, do not forward here */ + continue; + } + number = gsm_data->immediate_forward_numbers[i].number; + break; } + if (!number) { + ftdm_log_chan_msg(ftdmchan, FTDM_LOG_INFO, "No numbers available to enable immediate forwarding\n"); + return; + } + + ftdm_log_chan(ftdmchan, FTDM_LOG_INFO, "Enabling immediate forwarding to %s\n", number); + snprintf(cmd, sizeof(cmd), "ATD%s%s", gsm_data->immediate_forward_prefix, number); + wat_cmd_req(ftdmchan->span->span_id, cmd, NULL, NULL); +} + +static __inline__ void enable_immediate_forward(ftdm_channel_t *ftdmchan) +{ + ftdm_gsm_span_data_t *gsm_data = ftdmchan->span->signal_data; + ftdm_sched_timer(gsm_data->sched, "immediate_forwarding_delay", 1000, + perform_enable_immediate_forward, + ftdmchan, + &gsm_data->immediate_forwarding_timer); +} + +static __inline__ void disable_all_forwarding(ftdm_channel_t *ftdmchan) +{ + char cmd[100]; + ftdm_gsm_span_data_t *gsm_data = ftdmchan->span->signal_data; + + if (ftdm_strlen_zero_buf(gsm_data->disable_forward_number)) { + return; + } + + snprintf(cmd, sizeof(cmd), "ATD%s", gsm_data->disable_forward_number); + ftdm_log_chan(ftdmchan, FTDM_LOG_INFO, "Disabling GSM immediate forward dialing %s\n", gsm_data->disable_forward_number); + wat_cmd_req(ftdmchan->span->span_id, cmd, NULL, NULL); +} + +static ftdm_status_t ftdm_gsm_state_advance(ftdm_channel_t *ftdmchan) +{ + ftdm_gsm_span_data_t *gsm_data = ftdmchan->span->signal_data; + + ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Executing GSM state handler for %s\n", ftdm_channel_state2str(ftdmchan->state)); + + ftdm_channel_complete_state(ftdmchan); + + switch (ftdmchan->state) { + + /* starting an outgoing call */ + case FTDM_CHANNEL_STATE_DIALING: + { + uint32_t interval = 0; + wat_con_event_t con_event; + + ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Starting outgoing call with interval %d\n", interval); + + gsm_data->call_id = GSM_OUTBOUND_CALL_ID; + memset(&con_event, 0, sizeof(con_event)); + ftdm_set_string(con_event.called_num.digits, ftdmchan->caller_data.dnis.digits); + ftdm_log(FTDM_LOG_DEBUG, "Dialing number %s\n", con_event.called_num.digits); + wat_con_req(ftdmchan->span->span_id, gsm_data->call_id , &con_event); + + SEND_STATE_SIGNAL(FTDM_SIGEVENT_DIALING); + } + break; + + /* incoming call was offered */ + case FTDM_CHANNEL_STATE_RING: + { + /* notify the user about the new call */ + ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Inbound call detected\n"); + SEND_STATE_SIGNAL(FTDM_SIGEVENT_START); + } + break; + + /* the call is making progress */ + case FTDM_CHANNEL_STATE_PROGRESS: + case FTDM_CHANNEL_STATE_PROGRESS_MEDIA: + { + SEND_STATE_SIGNAL(FTDM_SIGEVENT_PROGRESS_MEDIA); + ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_UP); + } + break; + + /* the call was answered */ + case FTDM_CHANNEL_STATE_UP: + { + if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) { + SEND_STATE_SIGNAL(FTDM_SIGEVENT_UP); + } else { + wat_con_cfm(ftdmchan->span->span_id, gsm_data->call_id); + } + if (immediate_forward_enabled(gsm_data)) { + enable_immediate_forward(ftdmchan); + } + } + break; + + /* just got hangup */ + case FTDM_CHANNEL_STATE_HANGUP: + { + wat_rel_req(ftdmchan->span->span_id, gsm_data->call_id); + SEND_STATE_SIGNAL(FTDM_SIGEVENT_STOP); + } + break; + + /* finished call for good */ + case FTDM_CHANNEL_STATE_DOWN: + { + ftdm_channel_t *closed_chan; + gsm_data->call_id = 0; + closed_chan = ftdmchan; + ftdm_channel_close(&closed_chan); + ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "State processing ended.\n"); + SEND_STATE_SIGNAL(FTDM_SIGEVENT_STOP); + if (immediate_forward_enabled(gsm_data)) { + disable_all_forwarding(ftdmchan); + } + } + break; + + /* Outbound call is ringing */ + case FTDM_CHANNEL_STATE_RINGING: + { + SEND_STATE_SIGNAL(FTDM_SIGEVENT_RINGING); + } + break; + + default: + { + ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Unhandled channel state: %s\n", ftdm_channel_state2str(ftdmchan->state)); + } + break; + } return FTDM_SUCCESS; } @@ -1067,7 +1106,50 @@ static FIO_CONFIGURE_SPAN_SIGNALING_FUNCTION(ftdm_gsm_configure_span_signaling) ftdm_log(FTDM_LOG_DEBUG, "Configuring GSM span %s with hardware dtmf %s\n", span->name, val); } else if (!strcasecmp(var, "conditional-forwarding-number")) { ftdm_set_string(gsm_data->conditional_forward_number, val); - gsm_data->init_forwarding = FTDM_TRUE; + gsm_data->init_conditional_forwarding = FTDM_TRUE; + } else if (!strcasecmp(var, "conditional-forwarding-prefix")) { + ftdm_set_string(gsm_data->conditional_forward_prefix, val); + } else if (!strcasecmp(var, "immediate-forwarding-numbers")) { + char *state = NULL; + char *span_end = NULL; + char *number = NULL; + char *span_name = NULL; + int f = 0; + char *valdup = ftdm_strdup(val); + char *s = valdup; + + if (!ftdm_strlen_zero_buf(gsm_data->immediate_forward_numbers[0].number)) { + ftdm_log(FTDM_LOG_ERROR, "immediate-forwarding-numbers already parsed! failed to parse: %s\n", val); + goto ifn_parse_done; + } + + /* The string must be in the form [:], optionally multiple elements separated by comma */ + while ((number = strtok_r(s, ",", &state))) { + if (f == ftdm_array_len(gsm_data->immediate_forward_numbers)) { + ftdm_log(FTDM_LOG_ERROR, "Max number (%d) of immediate forwarding numbers reached!\n", f); + break; + } + + s = NULL; + span_end = strchr(number, ':'); + if (span_end) { + *span_end = '\0'; + span_name = number; + number = (span_end + 1); + ftdm_set_string(gsm_data->immediate_forward_numbers[f].span, span_name); + ftdm_log(FTDM_LOG_DEBUG, "Parsed immediate forwarding to span %s number %s\n", span_name, number); + } else { + ftdm_log(FTDM_LOG_DEBUG, "Parsed immediate forwarding to number %s\n", number); + } + ftdm_set_string(gsm_data->immediate_forward_numbers[f].number, number); + f++; + } +ifn_parse_done: + ftdm_safe_free(valdup); + } else if (!strcasecmp(var, "immediate-forwarding-prefix")) { + ftdm_set_string(gsm_data->immediate_forward_prefix, val); + } else if (!strcasecmp(var, "disable-forwarding-number")) { + ftdm_set_string(gsm_data->disable_forward_number, val); } else { ftdm_log(FTDM_LOG_ERROR, "Ignoring unknown GSM parameter '%s'", var); }