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);
}