diff --git a/conf/autoload_configs/callcenter.conf.xml b/conf/autoload_configs/callcenter.conf.xml
index 156ed374ca..e764d17f15 100644
--- a/conf/autoload_configs/callcenter.conf.xml
+++ b/conf/autoload_configs/callcenter.conf.xml
@@ -10,6 +10,8 @@
+
+
diff --git a/conf/dialplan/default.xml b/conf/dialplan/default.xml
index 9862eed711..8abb8d8348 100644
--- a/conf/dialplan/default.xml
+++ b/conf/dialplan/default.xml
@@ -715,15 +715,6 @@
-->
-
-
-
+
+
+
+
+
+
diff --git a/conf/dialplan/default/99999_enum.xml b/conf/dialplan/default/99999_enum.xml
deleted file mode 100644
index 6fd2151859..0000000000
--- a/conf/dialplan/default/99999_enum.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
-
-
-
-
diff --git a/libs/freetdm/mod_freetdm/mod_freetdm.c b/libs/freetdm/mod_freetdm/mod_freetdm.c
index 56eebd7dd5..fb85b3515c 100755
--- a/libs/freetdm/mod_freetdm/mod_freetdm.c
+++ b/libs/freetdm/mod_freetdm/mod_freetdm.c
@@ -3896,7 +3896,7 @@ SWITCH_STANDARD_API(ft_function)
ftdm_channel_t *fchan = NULL;
ftdm_span_t *span = NULL;
if (argc < 2) {
- stream->write_function(stream, "-ERR Usage: oz notrace []\n");
+ stream->write_function(stream, "-ERR Usage: ftdm notrace []\n");
goto end;
}
ftdm_span_find_by_name(argv[1], &span);
diff --git a/libs/freetdm/src/ftdm_io.c b/libs/freetdm/src/ftdm_io.c
index fc32a0ff0f..b275403c85 100644
--- a/libs/freetdm/src/ftdm_io.c
+++ b/libs/freetdm/src/ftdm_io.c
@@ -1805,6 +1805,7 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_open(uint32_t span_id, uint32_t chan_id,
status = FTDM_SUCCESS;
}
ftdm_set_flag(check, FTDM_CHANNEL_INUSE);
+ ftdm_set_flag(check, FTDM_CHANNEL_OUTBOUND);
*ftdmchan = check;
}
diff --git a/libs/spandsp/src/spandsp/private/t30.h b/libs/spandsp/src/spandsp/private/t30.h
index 6e4ee0a704..e56753e07d 100644
--- a/libs/spandsp/src/spandsp/private/t30.h
+++ b/libs/spandsp/src/spandsp/private/t30.h
@@ -207,6 +207,13 @@ struct t30_state_s
/*! \brief This is only used in full duplex (e.g. ISDN) modes. */
int timer_t8;
+ /* These fields are guessed based on compiler error forensics, I added them to fix the build -anthm */
+ int remote_interrupts_allowed;
+ int rtp_events;
+ int rtn_events;
+ int retransmit_capable;
+ /* end guessed fields */
+
/*! \brief TRUE once the far end FAX entity has been detected. */
int far_end_detected;
diff --git a/libs/spandsp/src/spandsp/t30.h b/libs/spandsp/src/spandsp/t30.h
index 9b01d3b62b..9df7321abd 100644
--- a/libs/spandsp/src/spandsp/t30.h
+++ b/libs/spandsp/src/spandsp/t30.h
@@ -682,6 +682,8 @@ SPAN_DECLARE(void) t30_get_transfer_statistics(t30_state_t *s, t30_stats_t *t);
\param state TRUE to enable interrupt request, else FALSE. */
SPAN_DECLARE(void) t30_local_interrupt_request(t30_state_t *s, int state);
+SPAN_DECLARE(void) t30_remote_interrupts_allowed(t30_state_t *s, int state);
+
#if defined(__cplusplus)
}
#endif
diff --git a/libs/spandsp/src/t30.c b/libs/spandsp/src/t30.c
index 97c7ed8cc7..45387b8585 100644
--- a/libs/spandsp/src/t30.c
+++ b/libs/spandsp/src/t30.c
@@ -2377,6 +2377,8 @@ static int send_response_to_pps(t30_state_t *s)
}
/*- End of function --------------------------------------------------------*/
+#define VET_ALL_FCD_FRAMES
+
static int process_rx_pps(t30_state_t *s, const uint8_t *msg, int len)
{
int page;
@@ -2387,6 +2389,10 @@ static int process_rx_pps(t30_state_t *s, const uint8_t *msg, int len)
int frame_no;
int first_bad_frame;
int image_ended;
+#if defined(VET_ALL_FCD_FRAMES)
+ int first;
+ int expected_len;
+#endif
if (len < 7)
{
@@ -2469,12 +2475,35 @@ static int process_rx_pps(t30_state_t *s, const uint8_t *msg, int len)
/* Build a bit map of which frames we now have stored OK */
first_bad_frame = 256;
+#if defined(VET_ALL_FCD_FRAMES)
+ first = TRUE;
+ expected_len = 256;
+#endif
for (i = 0; i < 32; i++)
{
s->ecm_frame_map[i + 3] = 0;
for (j = 0; j < 8; j++)
{
frame_no = (i << 3) + j;
+#if defined(VET_ALL_FCD_FRAMES)
+ if (s->ecm_len[frame_no] >= 0)
+ {
+ if (frame_no < s->ecm_frames - 1)
+ {
+ if (first)
+ {
+ if (s->ecm_len[frame_no] == 64)
+ expected_len = 64;
+ first = FALSE;
+ }
+ if (s->ecm_len[frame_no] != expected_len)
+ {
+ span_log(&s->logging, SPAN_LOG_FLOW, "Bad length ECM frame - %d\n", s->ecm_len[frame_no]);
+ s->ecm_len[frame_no] = -1;
+ }
+ }
+ }
+#endif
if (s->ecm_len[frame_no] < 0)
{
s->ecm_frame_map[i + 3] |= (1 << j);
@@ -2524,14 +2553,18 @@ static int process_rx_pps(t30_state_t *s, const uint8_t *msg, int len)
switch (s->last_pps_fcf2)
{
- case T30_NULL:
- case T30_EOP:
- case T30_PRI_EOP:
- case T30_EOM:
- case T30_PRI_EOM:
- case T30_EOS:
- case T30_MPS:
case T30_PRI_MPS:
+ case T30_PRI_EOM:
+ case T30_PRI_EOP:
+ if (s->remote_interrupts_allowed)
+ {
+ }
+ /* Fall through */
+ case T30_NULL:
+ case T30_MPS:
+ case T30_EOM:
+ case T30_EOS:
+ case T30_EOP:
if (s->receiver_not_ready_count > 0)
{
s->receiver_not_ready_count--;
@@ -2635,7 +2668,13 @@ static void process_rx_fcd(t30_state_t *s, const uint8_t *msg, int len)
switch (s->state)
{
case T30_STATE_F_DOC_ECM:
- if (len <= 4 + 256)
+ if (len > 4 + 256)
+ {
+ /* For other frame types we kill the call on an unexpected frame length. For FCD frames it is better to just ignore
+ the frame, and let retries sort things out. */
+ span_log(&s->logging, SPAN_LOG_FLOW, "Unexpected %s frame length - %d\n", t30_frametype(msg[0]), len);
+ }
+ else
{
frame_no = msg[3];
/* Just store the actual image data, and record its length */
@@ -2645,10 +2684,6 @@ static void process_rx_fcd(t30_state_t *s, const uint8_t *msg, int len)
/* In case we are just after a CTC/CTR exchange, which kicked us back to long training */
s->short_train = TRUE;
}
- else
- {
- unexpected_frame_length(s, msg, len);
- }
/* We have received something, so any missing carrier status is out of date */
if (s->current_status == T30_ERR_RX_NOCARRIER)
s->current_status = T30_ERR_OK;
@@ -2677,6 +2712,7 @@ static void process_rx_rcp(t30_state_t *s, const uint8_t *msg, int len)
case T30_STATE_F_POST_DOC_ECM:
/* Just ignore this. It must be an extra RCP. Several are usually sent, to maximise the chance
of receiving a correct one. */
+ timer_t2_start(s);
break;
default:
unexpected_non_final_frame(s, msg, len);
@@ -3063,6 +3099,11 @@ static void process_state_f_doc_non_ecm(t30_state_t *s, const uint8_t *msg, int
case T30_DCS:
process_rx_dcs(s, msg, len);
break;
+ case T30_PRI_MPS:
+ if (s->remote_interrupts_allowed)
+ {
+ }
+ /* Fall through */
case T30_MPS:
/* Treat this as a bad quality page. */
if (s->phase_d_handler)
@@ -3072,16 +3113,11 @@ static void process_state_f_doc_non_ecm(t30_state_t *s, const uint8_t *msg, int
set_state(s, T30_STATE_III_Q_RTN);
send_simple_frame(s, T30_RTN);
break;
- case T30_PRI_MPS:
- /* Treat this as a bad quality page. */
- if (s->phase_d_handler)
+ case T30_PRI_EOM:
+ if (s->remote_interrupts_allowed)
{
- s->phase_d_handler(s, s->phase_d_user_data, fcf);
- s->timer_t3 = ms_to_samples(DEFAULT_TIMER_T3);
}
- s->next_rx_step = msg[2] & 0xFE;
- set_state(s, T30_STATE_III_Q_RTN);
- break;
+ /* Fall through */
case T30_EOM:
case T30_EOS:
/* Treat this as a bad quality page. */
@@ -3093,16 +3129,11 @@ static void process_state_f_doc_non_ecm(t30_state_t *s, const uint8_t *msg, int
set_state(s, T30_STATE_III_Q_RTN);
send_simple_frame(s, T30_RTN);
break;
- case T30_PRI_EOM:
- /* Treat this as a bad quality page. */
- if (s->phase_d_handler)
+ case T30_PRI_EOP:
+ if (s->remote_interrupts_allowed)
{
- s->phase_d_handler(s, s->phase_d_user_data, fcf);
- s->timer_t3 = ms_to_samples(DEFAULT_TIMER_T3);
}
- s->next_rx_step = T30_PRI_EOM;
- set_state(s, T30_STATE_III_Q_RTN);
- break;
+ /* Fall through */
case T30_EOP:
/* Treat this as a bad quality page. */
if (s->phase_d_handler)
@@ -3112,16 +3143,6 @@ static void process_state_f_doc_non_ecm(t30_state_t *s, const uint8_t *msg, int
set_state(s, T30_STATE_III_Q_RTN);
send_simple_frame(s, T30_RTN);
break;
- case T30_PRI_EOP:
- /* Treat this as a bad quality page. */
- if (s->phase_d_handler)
- {
- s->phase_d_handler(s, s->phase_d_user_data, fcf);
- s->timer_t3 = ms_to_samples(DEFAULT_TIMER_T3);
- }
- s->next_rx_step = msg[2] & 0xFE;
- set_state(s, T30_STATE_III_Q_RTN);
- break;
case T30_DCN:
s->current_status = T30_ERR_RX_DCNDATA;
disconnect(s);
@@ -3148,6 +3169,11 @@ static void process_state_f_post_doc_non_ecm(t30_state_t *s, const uint8_t *msg,
fcf = msg[2] & 0xFE;
switch (fcf)
{
+ case T30_PRI_MPS:
+ if (s->remote_interrupts_allowed)
+ {
+ }
+ /* Fall through */
case T30_MPS:
s->next_rx_step = fcf;
queue_phase(s, T30_PHASE_D_TX);
@@ -3179,41 +3205,11 @@ static void process_state_f_post_doc_non_ecm(t30_state_t *s, const uint8_t *msg,
break;
}
break;
- case T30_PRI_MPS:
- s->next_rx_step = fcf;
- switch (copy_quality(s))
+ case T30_PRI_EOM:
+ if (s->remote_interrupts_allowed)
{
- case T30_COPY_QUALITY_PERFECT:
- case T30_COPY_QUALITY_GOOD:
- rx_end_page(s);
- if (s->phase_d_handler)
- {
- s->phase_d_handler(s, s->phase_d_user_data, fcf);
- s->timer_t3 = ms_to_samples(DEFAULT_TIMER_T3);
- }
- terminate_operation_in_progress(s);
- set_state(s, T30_STATE_III_Q_MCF);
- break;
- case T30_COPY_QUALITY_POOR:
- rx_end_page(s);
- if (s->phase_d_handler)
- {
- s->phase_d_handler(s, s->phase_d_user_data, fcf);
- s->timer_t3 = ms_to_samples(DEFAULT_TIMER_T3);
- }
- terminate_operation_in_progress(s);
- set_state(s, T30_STATE_III_Q_RTP);
- break;
- case T30_COPY_QUALITY_BAD:
- if (s->phase_d_handler)
- {
- s->phase_d_handler(s, s->phase_d_user_data, fcf);
- s->timer_t3 = ms_to_samples(DEFAULT_TIMER_T3);
- }
- set_state(s, T30_STATE_III_Q_RTN);
- break;
}
- break;
+ /* Fall through */
case T30_EOM:
case T30_EOS:
s->next_rx_step = fcf;
@@ -3247,41 +3243,11 @@ static void process_state_f_post_doc_non_ecm(t30_state_t *s, const uint8_t *msg,
break;
}
break;
- case T30_PRI_EOM:
- s->next_rx_step = fcf;
- switch (copy_quality(s))
+ case T30_PRI_EOP:
+ if (s->remote_interrupts_allowed)
{
- case T30_COPY_QUALITY_PERFECT:
- case T30_COPY_QUALITY_GOOD:
- rx_end_page(s);
- if (s->phase_d_handler)
- {
- s->phase_d_handler(s, s->phase_d_user_data, fcf);
- s->timer_t3 = ms_to_samples(DEFAULT_TIMER_T3);
- }
- terminate_operation_in_progress(s);
- set_state(s, T30_STATE_III_Q_MCF);
- break;
- case T30_COPY_QUALITY_POOR:
- rx_end_page(s);
- if (s->phase_d_handler)
- {
- s->phase_d_handler(s, s->phase_d_user_data, fcf);
- s->timer_t3 = ms_to_samples(DEFAULT_TIMER_T3);
- }
- terminate_operation_in_progress(s);
- set_state(s, T30_STATE_III_Q_RTP);
- break;
- case T30_COPY_QUALITY_BAD:
- if (s->phase_d_handler)
- {
- s->phase_d_handler(s, s->phase_d_user_data, fcf);
- s->timer_t3 = ms_to_samples(DEFAULT_TIMER_T3);
- }
- set_state(s, T30_STATE_III_Q_RTN);
- break;
}
- break;
+ /* Fall through */
case T30_EOP:
s->next_rx_step = fcf;
queue_phase(s, T30_PHASE_D_TX);
@@ -3312,41 +3278,6 @@ static void process_state_f_post_doc_non_ecm(t30_state_t *s, const uint8_t *msg,
break;
}
break;
- case T30_PRI_EOP:
- s->next_rx_step = fcf;
- switch (copy_quality(s))
- {
- case T30_COPY_QUALITY_PERFECT:
- case T30_COPY_QUALITY_GOOD:
- rx_end_page(s);
- if (s->phase_d_handler)
- {
- s->phase_d_handler(s, s->phase_d_user_data, fcf);
- s->timer_t3 = ms_to_samples(DEFAULT_TIMER_T3);
- }
- terminate_operation_in_progress(s);
- set_state(s, T30_STATE_III_Q_MCF);
- break;
- case T30_COPY_QUALITY_POOR:
- rx_end_page(s);
- if (s->phase_d_handler)
- {
- s->phase_d_handler(s, s->phase_d_user_data, fcf);
- s->timer_t3 = ms_to_samples(DEFAULT_TIMER_T3);
- }
- terminate_operation_in_progress(s);
- set_state(s, T30_STATE_III_Q_RTP);
- break;
- case T30_COPY_QUALITY_BAD:
- if (s->phase_d_handler)
- {
- s->phase_d_handler(s, s->phase_d_user_data, fcf);
- s->timer_t3 = ms_to_samples(DEFAULT_TIMER_T3);
- }
- set_state(s, T30_STATE_III_Q_RTN);
- break;
- }
- break;
case T30_DCN:
s->current_status = T30_ERR_RX_DCNFAX;
disconnect(s);
@@ -3384,21 +3315,7 @@ static void process_state_f_doc_and_post_doc_ecm(t30_state_t *s, const uint8_t *
case T4_RCP:
/* Return to control for partial page. These might come through with or without the final frame tag.
Here we deal with the "final frame tag" case. */
- if (s->state == T30_STATE_F_DOC_ECM)
- {
- /* Return to control for partial page */
- set_state(s, T30_STATE_F_POST_DOC_ECM);
- queue_phase(s, T30_PHASE_D_RX);
- timer_t2_start(s);
- /* We have received something, so any missing carrier status is out of date */
- if (s->current_status == T30_ERR_RX_NOCARRIER)
- s->current_status = T30_ERR_OK;
- }
- else
- {
- /* Just ignore this. It must be an extra RCP. Several are usually sent, to maximise the chance
- of receiving a correct one. */
- }
+ process_rx_rcp(s, msg, len);
break;
case T30_EOR:
if (len != 4)
@@ -3413,7 +3330,10 @@ static void process_state_f_doc_and_post_doc_ecm(t30_state_t *s, const uint8_t *
case T30_PRI_EOP:
case T30_PRI_EOM:
case T30_PRI_MPS:
- /* TODO: Alert operator */
+ if (s->remote_interrupts_allowed)
+ {
+ /* TODO: Alert operator */
+ }
/* Fall through */
case T30_NULL:
case T30_EOP:
@@ -3651,11 +3571,22 @@ static void process_state_ii_q(t30_state_t *s, const uint8_t *msg, int len)
fcf = msg[2] & 0xFE;
switch (fcf)
{
+ case T30_PIP:
+ if (s->remote_interrupts_allowed)
+ {
+ s->retries = 0;
+ if (s->phase_d_handler)
+ {
+ s->phase_d_handler(s, s->phase_d_user_data, fcf);
+ s->timer_t3 = ms_to_samples(DEFAULT_TIMER_T3);
+ }
+ }
+ /* Fall through */
case T30_MCF:
switch (s->next_tx_step)
{
- case T30_MPS:
case T30_PRI_MPS:
+ case T30_MPS:
tx_end_page(s);
if (s->phase_d_handler)
s->phase_d_handler(s, s->phase_d_user_data, fcf);
@@ -3668,8 +3599,8 @@ static void process_state_ii_q(t30_state_t *s, const uint8_t *msg, int len)
set_state(s, T30_STATE_I);
queue_phase(s, T30_PHASE_C_NON_ECM_TX);
break;
- case T30_EOM:
case T30_PRI_EOM:
+ case T30_EOM:
case T30_EOS:
tx_end_page(s);
if (s->phase_d_handler)
@@ -3678,8 +3609,8 @@ static void process_state_ii_q(t30_state_t *s, const uint8_t *msg, int len)
report_tx_result(s, TRUE);
return_to_phase_b(s, FALSE);
break;
- case T30_EOP:
case T30_PRI_EOP:
+ case T30_EOP:
tx_end_page(s);
if (s->phase_d_handler)
s->phase_d_handler(s, s->phase_d_user_data, fcf);
@@ -3690,13 +3621,11 @@ static void process_state_ii_q(t30_state_t *s, const uint8_t *msg, int len)
}
break;
case T30_RTP:
-#if 0
s->rtp_events++;
-#endif
switch (s->next_tx_step)
{
- case T30_MPS:
case T30_PRI_MPS:
+ case T30_MPS:
tx_end_page(s);
if (s->phase_d_handler)
s->phase_d_handler(s, s->phase_d_user_data, fcf);
@@ -3717,8 +3646,8 @@ static void process_state_ii_q(t30_state_t *s, const uint8_t *msg, int len)
queue_phase(s, T30_PHASE_B_TX);
restart_sending_document(s);
break;
- case T30_EOM:
case T30_PRI_EOM:
+ case T30_EOM:
case T30_EOS:
tx_end_page(s);
if (s->phase_d_handler)
@@ -3727,8 +3656,8 @@ static void process_state_ii_q(t30_state_t *s, const uint8_t *msg, int len)
/* TODO: should go back to T, and resend */
return_to_phase_b(s, TRUE);
break;
- case T30_EOP:
case T30_PRI_EOP:
+ case T30_EOP:
tx_end_page(s);
if (s->phase_d_handler)
s->phase_d_handler(s, s->phase_d_user_data, fcf);
@@ -3737,20 +3666,27 @@ static void process_state_ii_q(t30_state_t *s, const uint8_t *msg, int len)
break;
}
break;
+ case T30_PIN:
+ if (s->remote_interrupts_allowed)
+ {
+ s->retries = 0;
+ if (s->phase_d_handler)
+ {
+ s->phase_d_handler(s, s->phase_d_user_data, fcf);
+ s->timer_t3 = ms_to_samples(DEFAULT_TIMER_T3);
+ }
+ }
+ /* Fall through */
case T30_RTN:
-#if 0
s->rtn_events++;
-#endif
switch (s->next_tx_step)
{
- case T30_MPS:
case T30_PRI_MPS:
+ case T30_MPS:
s->retries = 0;
if (s->phase_d_handler)
s->phase_d_handler(s, s->phase_d_user_data, fcf);
-#if 0
if (!s->retransmit_capable)
-#endif
{
/* Send the next page, regardless of the problem with the current one. */
if (tx_start_page(s))
@@ -3771,29 +3707,26 @@ static void process_state_ii_q(t30_state_t *s, const uint8_t *msg, int len)
queue_phase(s, T30_PHASE_B_TX);
restart_sending_document(s);
break;
- case T30_EOM:
case T30_PRI_EOM:
+ case T30_EOM:
case T30_EOS:
s->retries = 0;
if (s->phase_d_handler)
s->phase_d_handler(s, s->phase_d_user_data, fcf);
-#if 0
if (s->retransmit_capable)
{
/* Wait for DIS */
}
else
-#endif
{
return_to_phase_b(s, TRUE);
}
break;
- case T30_EOP:
case T30_PRI_EOP:
+ case T30_EOP:
s->retries = 0;
if (s->phase_d_handler)
s->phase_d_handler(s, s->phase_d_user_data, fcf);
-#if 0
if (s->retransmit_capable)
{
/* Send fresh training, and then repeat the last page */
@@ -3809,36 +3742,19 @@ static void process_state_ii_q(t30_state_t *s, const uint8_t *msg, int len)
restart_sending_document(s);
}
else
-#endif
{
send_dcn(s);
}
break;
}
break;
- case T30_PIP:
- s->retries = 0;
- if (s->phase_d_handler)
- {
- s->phase_d_handler(s, s->phase_d_user_data, fcf);
- s->timer_t3 = ms_to_samples(DEFAULT_TIMER_T3);
- }
- break;
- case T30_PIN:
- s->retries = 0;
- if (s->phase_d_handler)
- {
- s->phase_d_handler(s, s->phase_d_user_data, fcf);
- s->timer_t3 = ms_to_samples(DEFAULT_TIMER_T3);
- }
- break;
case T30_DCN:
switch (s->next_tx_step)
{
- case T30_MPS:
case T30_PRI_MPS:
- case T30_EOM:
case T30_PRI_EOM:
+ case T30_MPS:
+ case T30_EOM:
case T30_EOS:
/* Unexpected DCN after EOM, EOS or MPS sequence */
s->current_status = T30_ERR_RX_DCNPHD;
@@ -4019,8 +3935,8 @@ static void process_state_iv_pps_null(t30_state_t *s, const uint8_t *msg, int le
span_log(&s->logging, SPAN_LOG_FLOW, "Moving on to the next page\n");
switch (s->next_tx_step)
{
- case T30_MPS:
case T30_PRI_MPS:
+ case T30_MPS:
tx_end_page(s);
if (s->phase_d_handler)
s->phase_d_handler(s, s->phase_d_user_data, fcf);
@@ -4036,8 +3952,8 @@ static void process_state_iv_pps_null(t30_state_t *s, const uint8_t *msg, int le
send_first_ecm_frame(s);
}
break;
- case T30_EOM:
case T30_PRI_EOM:
+ case T30_EOM:
case T30_EOS:
tx_end_page(s);
if (s->phase_d_handler)
@@ -4046,8 +3962,8 @@ static void process_state_iv_pps_null(t30_state_t *s, const uint8_t *msg, int le
report_tx_result(s, TRUE);
return_to_phase_b(s, FALSE);
break;
- case T30_EOP:
case T30_PRI_EOP:
+ case T30_EOP:
tx_end_page(s);
if (s->phase_d_handler)
s->phase_d_handler(s, s->phase_d_user_data, fcf);
@@ -4094,6 +4010,17 @@ static void process_state_iv_pps_q(t30_state_t *s, const uint8_t *msg, int len)
fcf = msg[2] & 0xFE;
switch (fcf)
{
+ case T30_PIP:
+ if (s->remote_interrupts_allowed)
+ {
+ s->retries = 0;
+ if (s->phase_d_handler)
+ {
+ s->phase_d_handler(s, s->phase_d_user_data, fcf);
+ s->timer_t3 = ms_to_samples(DEFAULT_TIMER_T3);
+ }
+ }
+ /* Fall through */
case T30_MCF:
s->retries = 0;
s->timer_t5 = 0;
@@ -4112,8 +4039,8 @@ static void process_state_iv_pps_q(t30_state_t *s, const uint8_t *msg, int len)
span_log(&s->logging, SPAN_LOG_FLOW, "Moving on to the next page\n");
switch (s->next_tx_step)
{
- case T30_MPS:
case T30_PRI_MPS:
+ case T30_MPS:
tx_end_page(s);
if (s->phase_d_handler)
s->phase_d_handler(s, s->phase_d_user_data, fcf);
@@ -4129,8 +4056,8 @@ static void process_state_iv_pps_q(t30_state_t *s, const uint8_t *msg, int len)
send_first_ecm_frame(s);
}
break;
- case T30_EOM:
case T30_PRI_EOM:
+ case T30_EOM:
case T30_EOS:
tx_end_page(s);
if (s->phase_d_handler)
@@ -4139,8 +4066,8 @@ static void process_state_iv_pps_q(t30_state_t *s, const uint8_t *msg, int len)
report_tx_result(s, TRUE);
return_to_phase_b(s, FALSE);
break;
- case T30_EOP:
case T30_PRI_EOP:
+ case T30_EOP:
tx_end_page(s);
if (s->phase_d_handler)
s->phase_d_handler(s, s->phase_d_user_data, fcf);
@@ -4158,22 +4085,6 @@ static void process_state_iv_pps_q(t30_state_t *s, const uint8_t *msg, int len)
set_state(s, T30_STATE_IV_PPS_RNR);
send_rr(s);
break;
- case T30_PIP:
- s->retries = 0;
- if (s->phase_d_handler)
- {
- s->phase_d_handler(s, s->phase_d_user_data, fcf);
- s->timer_t3 = ms_to_samples(DEFAULT_TIMER_T3);
- }
- break;
- case T30_PIN:
- s->retries = 0;
- if (s->phase_d_handler)
- {
- s->phase_d_handler(s, s->phase_d_user_data, fcf);
- s->timer_t3 = ms_to_samples(DEFAULT_TIMER_T3);
- }
- break;
case T30_PPR:
process_rx_ppr(s, msg, len);
break;
@@ -4187,6 +4098,17 @@ static void process_state_iv_pps_q(t30_state_t *s, const uint8_t *msg, int len)
case T30_FNV:
process_rx_fnv(s, msg, len);
break;
+ case T30_PIN:
+ if (s->remote_interrupts_allowed)
+ {
+ s->retries = 0;
+ if (s->phase_d_handler)
+ {
+ s->phase_d_handler(s, s->phase_d_user_data, fcf);
+ s->timer_t3 = ms_to_samples(DEFAULT_TIMER_T3);
+ }
+ }
+ /* Fall through */
default:
/* We don't know what to do with this. */
unexpected_final_frame(s, msg, len);
@@ -4203,6 +4125,17 @@ static void process_state_iv_pps_rnr(t30_state_t *s, const uint8_t *msg, int len
fcf = msg[2] & 0xFE;
switch (fcf)
{
+ case T30_PIP:
+ if (s->remote_interrupts_allowed)
+ {
+ s->retries = 0;
+ if (s->phase_d_handler)
+ {
+ s->phase_d_handler(s, s->phase_d_user_data, fcf);
+ s->timer_t3 = ms_to_samples(DEFAULT_TIMER_T3);
+ }
+ }
+ /* Fall through */
case T30_MCF:
s->retries = 0;
s->timer_t5 = 0;
@@ -4221,8 +4154,8 @@ static void process_state_iv_pps_rnr(t30_state_t *s, const uint8_t *msg, int len
span_log(&s->logging, SPAN_LOG_FLOW, "Moving on to the next page\n");
switch (s->next_tx_step)
{
- case T30_MPS:
case T30_PRI_MPS:
+ case T30_MPS:
tx_end_page(s);
if (s->phase_d_handler)
s->phase_d_handler(s, s->phase_d_user_data, fcf);
@@ -4238,8 +4171,8 @@ static void process_state_iv_pps_rnr(t30_state_t *s, const uint8_t *msg, int len
send_first_ecm_frame(s);
}
break;
- case T30_EOM:
case T30_PRI_EOM:
+ case T30_EOM:
case T30_EOS:
tx_end_page(s);
if (s->phase_d_handler)
@@ -4248,8 +4181,8 @@ static void process_state_iv_pps_rnr(t30_state_t *s, const uint8_t *msg, int len
report_tx_result(s, TRUE);
return_to_phase_b(s, FALSE);
break;
- case T30_EOP:
case T30_PRI_EOP:
+ case T30_EOP:
tx_end_page(s);
if (s->phase_d_handler)
s->phase_d_handler(s, s->phase_d_user_data, fcf);
@@ -4267,22 +4200,6 @@ static void process_state_iv_pps_rnr(t30_state_t *s, const uint8_t *msg, int len
set_state(s, T30_STATE_IV_PPS_RNR);
send_rr(s);
break;
- case T30_PIP:
- s->retries = 0;
- if (s->phase_d_handler)
- {
- s->phase_d_handler(s, s->phase_d_user_data, fcf);
- s->timer_t3 = ms_to_samples(DEFAULT_TIMER_T3);
- }
- break;
- case T30_PIN:
- s->retries = 0;
- if (s->phase_d_handler)
- {
- s->phase_d_handler(s, s->phase_d_user_data, fcf);
- s->timer_t3 = ms_to_samples(DEFAULT_TIMER_T3);
- }
- break;
case T30_DCN:
s->current_status = T30_ERR_RX_DCNRRD;
disconnect(s);
@@ -4293,6 +4210,17 @@ static void process_state_iv_pps_rnr(t30_state_t *s, const uint8_t *msg, int len
case T30_FNV:
process_rx_fnv(s, msg, len);
break;
+ case T30_PIN:
+ if (s->remote_interrupts_allowed)
+ {
+ s->retries = 0;
+ if (s->phase_d_handler)
+ {
+ s->phase_d_handler(s, s->phase_d_user_data, fcf);
+ s->timer_t3 = ms_to_samples(DEFAULT_TIMER_T3);
+ }
+ }
+ /* Fall through */
default:
/* We don't know what to do with this. */
unexpected_final_frame(s, msg, len);
@@ -4345,14 +4273,6 @@ static void process_state_iv_eor(t30_state_t *s, const uint8_t *msg, int len)
set_state(s, T30_STATE_IV_EOR_RNR);
send_rr(s);
break;
- case T30_PIN:
- s->retries = 0;
- if (s->phase_d_handler)
- {
- s->phase_d_handler(s, s->phase_d_user_data, fcf);
- s->timer_t3 = ms_to_samples(DEFAULT_TIMER_T3);
- }
- break;
case T30_ERR:
/* TODO: Continue with the next message if MPS or EOM? */
s->timer_t5 = 0;
@@ -4364,6 +4284,17 @@ static void process_state_iv_eor(t30_state_t *s, const uint8_t *msg, int len)
case T30_FNV:
process_rx_fnv(s, msg, len);
break;
+ case T30_PIN:
+ if (s->remote_interrupts_allowed)
+ {
+ s->retries = 0;
+ if (s->phase_d_handler)
+ {
+ s->phase_d_handler(s, s->phase_d_user_data, fcf);
+ s->timer_t3 = ms_to_samples(DEFAULT_TIMER_T3);
+ }
+ }
+ /* Fall through */
default:
/* We don't know what to do with this. */
unexpected_final_frame(s, msg, len);
@@ -4386,14 +4317,6 @@ static void process_state_iv_eor_rnr(t30_state_t *s, const uint8_t *msg, int len
set_state(s, T30_STATE_IV_EOR_RNR);
send_rr(s);
break;
- case T30_PIN:
- s->retries = 0;
- if (s->phase_d_handler)
- {
- s->phase_d_handler(s, s->phase_d_user_data, fcf);
- s->timer_t3 = ms_to_samples(DEFAULT_TIMER_T3);
- }
- break;
case T30_ERR:
/* TODO: Continue with the next message if MPS or EOM? */
s->timer_t5 = 0;
@@ -4409,6 +4332,17 @@ static void process_state_iv_eor_rnr(t30_state_t *s, const uint8_t *msg, int len
case T30_FNV:
process_rx_fnv(s, msg, len);
break;
+ case T30_PIN:
+ if (s->remote_interrupts_allowed)
+ {
+ s->retries = 0;
+ if (s->phase_d_handler)
+ {
+ s->phase_d_handler(s, s->phase_d_user_data, fcf);
+ s->timer_t3 = ms_to_samples(DEFAULT_TIMER_T3);
+ }
+ }
+ /* Fall through */
default:
/* We don't know what to do with this. */
unexpected_final_frame(s, msg, len);
@@ -4596,7 +4530,7 @@ static void process_rx_control_msg(t30_state_t *s, const uint8_t *msg, int len)
/* The following handles context sensitive message types, which should
occur at the end of message sequences. They should, therefore have
the final frame flag set. */
- span_log(&s->logging, SPAN_LOG_FLOW, "In state %d\n", s->state);
+ span_log(&s->logging, SPAN_LOG_FLOW, "Rx final frame in state %d\n", s->state);
switch (s->state)
{
@@ -5082,11 +5016,11 @@ static void timer_t2_expired(t30_state_t *s)
case T30_STATE_F_POST_RCP_MCF:
switch (s->next_rx_step)
{
- case T30_EOM:
case T30_PRI_EOM:
+ case T30_EOM:
case T30_EOS:
/* We didn't receive a response to our T30_MCF after T30_EOM, so we must be OK
- to proceed to phase B, and pretty act like its the beginning of a call. */
+ to proceed to phase B, and pretty much act like its the beginning of a call. */
span_log(&s->logging, SPAN_LOG_FLOW, "Returning to phase B after %s\n", t30_frametype(s->next_rx_step));
set_phase(s, T30_PHASE_B_TX);
timer_t2_start(s);
@@ -5109,6 +5043,8 @@ static void timer_t2_expired(t30_state_t *s)
case T30_STATE_F_POST_DOC_ECM:
case T30_STATE_F_POST_DOC_NON_ECM:
/* While waiting for next FAX page */
+ /* Figure 5-2b/T.30 and note 7 says we should allow 1 to 3 tries at this point.
+ The way we work now is effectively hard coding a 1 try limit */
s->current_status = T30_ERR_RX_T2EXPMPS;
break;
#if 0
@@ -5873,8 +5809,8 @@ SPAN_DECLARE(void) t30_front_end_status(void *user_data, int status)
{
switch (s->next_rx_step)
{
- case T30_MPS:
case T30_PRI_MPS:
+ case T30_MPS:
/* We should now start to get another page */
if (s->error_correcting_mode)
{
@@ -5888,15 +5824,15 @@ SPAN_DECLARE(void) t30_front_end_status(void *user_data, int status)
}
timer_t2_start(s);
break;
- case T30_EOM:
case T30_PRI_EOM:
+ case T30_EOM:
case T30_EOS:
/* See if we get something back, before moving to phase B. */
timer_t2_start(s);
set_phase(s, T30_PHASE_D_RX);
break;
- case T30_EOP:
case T30_PRI_EOP:
+ case T30_EOP:
/* Wait for a DCN. */
set_phase(s, T30_PHASE_D_RX);
timer_t4_start(s);
@@ -6256,6 +6192,12 @@ SPAN_DECLARE(void) t30_local_interrupt_request(t30_state_t *s, int state)
}
/*- End of function --------------------------------------------------------*/
+SPAN_DECLARE(void) t30_remote_interrupts_allowed(t30_state_t *s, int state)
+{
+ s->remote_interrupts_allowed = state;
+}
+/*- End of function --------------------------------------------------------*/
+
SPAN_DECLARE(int) t30_restart(t30_state_t *s)
{
s->phase = T30_PHASE_IDLE;
@@ -6276,10 +6218,9 @@ SPAN_DECLARE(int) t30_restart(t30_state_t *s)
/* The page number is only reset at call establishment */
s->rx_page_number = 0;
s->tx_page_number = 0;
-#if 0
s->rtn_events = 0;
s->rtp_events = 0;
-#endif
+ s->local_interrupt_pending = FALSE;
s->far_end_detected = FALSE;
s->timer_t0_t1 = ms_to_samples(DEFAULT_TIMER_T0);
if (s->calling_party)
diff --git a/libs/spandsp/src/t31.c b/libs/spandsp/src/t31.c
index 5c9c4066b2..22af0fcb66 100644
--- a/libs/spandsp/src/t31.c
+++ b/libs/spandsp/src/t31.c
@@ -2602,7 +2602,9 @@ SPAN_DECLARE(t31_state_t *) t31_init(t31_state_t *s,
t38_tx_packet_handler_t *tx_t38_packet_handler,
void *tx_t38_packet_user_data)
{
+#if 0
v8_parms_t v8_parms;
+#endif
int alloced;
if (at_tx_handler == NULL || modem_control_handler == NULL)
diff --git a/libs/spandsp/src/t38_gateway.c b/libs/spandsp/src/t38_gateway.c
index a572b5dad9..d3539cddc1 100644
--- a/libs/spandsp/src/t38_gateway.c
+++ b/libs/spandsp/src/t38_gateway.c
@@ -426,7 +426,6 @@ static int set_next_tx_type(t38_gateway_state_t *s)
t38_gateway_hdlc_state_t *u;
t = &s->audio.modems;
- u = &s->core.hdlc_to_modem;
t38_non_ecm_buffer_report_output_status(&s->core.non_ecm_to_modem, &s->logging);
if (t->next_tx_handler)
{
@@ -447,6 +446,7 @@ static int set_next_tx_type(t38_gateway_state_t *s)
return TRUE;
}
/*endif*/
+ u = &s->core.hdlc_to_modem;
if (u->in == u->out)
return FALSE;
/*endif*/
@@ -1057,7 +1057,9 @@ static int process_rx_missing(t38_core_state_t *t, void *user_data, int rx_seq_n
static int process_rx_indicator(t38_core_state_t *t, void *user_data, int indicator)
{
t38_gateway_state_t *s;
-
+ t38_gateway_hdlc_state_t *u;
+ int immediate;
+
s = (t38_gateway_state_t *) user_data;
t38_non_ecm_buffer_report_input_status(&s->core.non_ecm_to_modem, &s->logging);
@@ -1067,25 +1069,50 @@ static int process_rx_indicator(t38_core_state_t *t, void *user_data, int indica
return 0;
}
/*endif*/
- if (s->core.hdlc_to_modem.buf[s->core.hdlc_to_modem.in].contents)
+
+ u = &s->core.hdlc_to_modem;
+ immediate = (u->in == u->out);
+ if (u->buf[u->in].contents)
{
- if (++s->core.hdlc_to_modem.in >= T38_TX_HDLC_BUFS)
- s->core.hdlc_to_modem.in = 0;
+ if (++u->in >= T38_TX_HDLC_BUFS)
+ u->in = 0;
/*endif*/
}
/*endif*/
- s->core.hdlc_to_modem.buf[s->core.hdlc_to_modem.in].contents = (indicator | FLAG_INDICATOR);
- if (++s->core.hdlc_to_modem.in >= T38_TX_HDLC_BUFS)
- s->core.hdlc_to_modem.in = 0;
+ u->buf[u->in].contents = (indicator | FLAG_INDICATOR);
+ if (++u->in >= T38_TX_HDLC_BUFS)
+ u->in = 0;
/*endif*/
- t38_non_ecm_buffer_set_mode(&s->core.non_ecm_to_modem, s->core.image_data_mode, s->core.min_row_bits);
- span_log(&s->logging,
- SPAN_LOG_FLOW,
- "Queued change - (%d) %s -> %s\n",
- silence_gen_remainder(&(s->audio.modems.silence_gen)),
- t38_indicator_to_str(t->current_rx_indicator),
- t38_indicator_to_str(indicator));
+ if (immediate)
+ {
+ span_log(&s->logging,
+ SPAN_LOG_FLOW,
+ "Changing - (%d) %s -> %s\n",
+ silence_gen_remainder(&(s->audio.modems.silence_gen)),
+ t38_indicator_to_str(t->current_rx_indicator),
+ t38_indicator_to_str(indicator));
+ switch (s->t38x.current_rx_field_class)
+ {
+ case T38_FIELD_CLASS_NONE:
+ break;
+ case T38_FIELD_CLASS_HDLC:
+ span_log(&s->logging, SPAN_LOG_FLOW, "HDLC shutdown\n");
+ hdlc_tx_frame(&s->audio.modems.hdlc_tx, NULL, 0);
+ break;
+ case T38_FIELD_CLASS_NON_ECM:
+ break;
+ }
+ }
+ else
+ {
+ span_log(&s->logging,
+ SPAN_LOG_FLOW,
+ "Queued change - (%d) %s -> %s\n",
+ silence_gen_remainder(&(s->audio.modems.silence_gen)),
+ t38_indicator_to_str(t->current_rx_indicator),
+ t38_indicator_to_str(indicator));
+ }
s->t38x.current_rx_field_class = T38_FIELD_CLASS_NONE;
/* We need to set this here, since we might have been called as a fake
indication when the real one was missing */
@@ -1440,6 +1467,8 @@ static int process_rx_data(t38_core_state_t *t, void *user_data, int data_type,
xx->corrupt_current_frame[0] = FALSE;
break;
case T38_FIELD_T4_NON_ECM_DATA:
+ if (xx->current_rx_field_class == T38_FIELD_CLASS_NONE)
+ t38_non_ecm_buffer_set_mode(&s->core.non_ecm_to_modem, s->core.image_data_mode, s->core.min_row_bits);
xx->current_rx_field_class = T38_FIELD_CLASS_NON_ECM;
hdlc_buf = &s->core.hdlc_to_modem.buf[s->core.hdlc_to_modem.in];
if (hdlc_buf->contents != (data_type | FLAG_DATA))
@@ -1454,6 +1483,8 @@ static int process_rx_data(t38_core_state_t *t, void *user_data, int data_type,
xx->corrupt_current_frame[0] = FALSE;
break;
case T38_FIELD_T4_NON_ECM_SIG_END:
+ if (xx->current_rx_field_class == T38_FIELD_CLASS_NONE)
+ t38_non_ecm_buffer_set_mode(&s->core.non_ecm_to_modem, s->core.image_data_mode, s->core.min_row_bits);
hdlc_buf = &s->core.hdlc_to_modem.buf[s->core.hdlc_to_modem.in];
/* Some T.38 implementations send multiple T38_FIELD_T4_NON_ECM_SIG_END messages, in IFP packets with
incrementing sequence numbers, which are actually repeats. They get through to this point because
@@ -2432,8 +2463,8 @@ SPAN_DECLARE(t38_gateway_state_t *) t38_gateway_init(t38_gateway_state_t *s,
s->core.to_t38.octets_per_data_packet = 1;
s->core.ecm_allowed = TRUE;
- t38_non_ecm_buffer_init(&s->core.non_ecm_to_modem, FALSE, 0);
//s->core.ms_per_tx_chunk = DEFAULT_MS_PER_TX_CHUNK;
+ t38_non_ecm_buffer_init(&s->core.non_ecm_to_modem, FALSE, 0);
restart_rx_modem(s);
s->core.timed_mode = TIMED_MODE_STARTUP;
s->core.samples_to_timeout = 1;
diff --git a/libs/spandsp/src/v18.c b/libs/spandsp/src/v18.c
index 18d94b4b90..bb26644b62 100644
--- a/libs/spandsp/src/v18.c
+++ b/libs/spandsp/src/v18.c
@@ -111,7 +111,7 @@ static const struct dtmf_to_ascii_s dtmf_to_ascii[] =
{"##8", 'W'},
{"##9", 'Z'},
{"##0", ' '},
-#if defined(WIN32) || ( defined(__SVR4) && defined (__sun))
+#if defined(WIN32) || ( defined(__SVR4) && defined (__sun))
{"#*1", 'X'}, // (Note 1) 111 1011
{"#*2", 'X'}, // (Note 1) 111 1100
{"#*3", 'X'}, // (Note 1) 111 1101
diff --git a/src/include/switch_caller.h b/src/include/switch_caller.h
index b3002081da..f77f1b016d 100644
--- a/src/include/switch_caller.h
+++ b/src/include/switch_caller.h
@@ -103,6 +103,7 @@ SWITCH_BEGIN_EXTERN_C
switch_caller_profile_flag_t flags;
struct switch_caller_profile *originator_caller_profile;
struct switch_caller_profile *originatee_caller_profile;
+ struct switch_caller_profile *origination_caller_profile;
struct switch_caller_profile *hunt_caller_profile;
struct switch_channel_timetable *times;
struct switch_caller_extension *caller_extension;
diff --git a/src/include/switch_channel.h b/src/include/switch_channel.h
index 247beae460..3f92534e3e 100644
--- a/src/include/switch_channel.h
+++ b/src/include/switch_channel.h
@@ -215,6 +215,20 @@ SWITCH_DECLARE(void) switch_channel_set_originatee_caller_profile(switch_channel
*/
SWITCH_DECLARE(switch_caller_profile_t *) switch_channel_get_originatee_caller_profile(switch_channel_t *channel);
+/*!
+ \brief Set the given channel's origination caller profile
+ \param channel channel to assign the profile to
+ \param caller_profile the profile to assign
+*/
+SWITCH_DECLARE(void) switch_channel_set_origination_caller_profile(switch_channel_t *channel, switch_caller_profile_t *caller_profile);
+
+/*!
+ \brief Retrive the given channel's origination caller profile
+ \param channel channel to retrive the profile from
+ \return the requested profile
+*/
+SWITCH_DECLARE(switch_caller_profile_t *) switch_channel_get_origination_caller_profile(switch_channel_t *channel);
+
/*!
\brief Retrive the given channel's unique id
diff --git a/src/mod/applications/mod_callcenter/mod_callcenter.c b/src/mod/applications/mod_callcenter/mod_callcenter.c
index e56309d005..e08757ba38 100644
--- a/src/mod/applications/mod_callcenter/mod_callcenter.c
+++ b/src/mod/applications/mod_callcenter/mod_callcenter.c
@@ -37,6 +37,7 @@
#define CC_AGENT_TYPE_UUID_STANDBY "uuid-standby"
#define CC_SQLITE_DB_NAME "callcenter"
+#define CC_MAX_TIME_DIFF_CHECK 5
/* TODO
drop caller if no agent login
dont allow new caller
@@ -53,6 +54,16 @@ SWITCH_MODULE_DEFINITION(mod_callcenter, mod_callcenter_load, mod_callcenter_shu
static const char *global_cf = "callcenter.conf";
+struct cc_status_table {
+ const char *name;
+ int status;
+};
+
+struct cc_state_table {
+ const char *name;
+ int state;
+};
+
typedef enum {
CC_STATUS_SUCCESS,
CC_STATUS_FALSE,
@@ -77,12 +88,7 @@ typedef enum {
CC_TIER_STATE_STANDBY = 5
} cc_tier_state_t;
-struct cc_tier_state_table {
- const char *name;
- cc_tier_state_t state;
-};
-
-static struct cc_tier_state_table STATE_CHART[] = {
+static struct cc_state_table STATE_CHART[] = {
{"Unknown", CC_TIER_STATE_UNKNOWN},
{"No Answer", CC_TIER_STATE_NO_ANSWER},
{"Ready", CC_TIER_STATE_READY},
@@ -101,12 +107,7 @@ typedef enum {
CC_AGENT_STATUS_ON_BREAK = 4
} cc_agent_status_t;
-struct cc_agent_status_table {
- const char *name;
- cc_agent_status_t status;
-};
-
-static struct cc_agent_status_table AGENT_STATUS_CHART[] = {
+static struct cc_status_table AGENT_STATUS_CHART[] = {
{"Unknown", CC_AGENT_STATUS_UNKNOWN},
{"Logged Out", CC_AGENT_STATUS_LOGGED_OUT},
{"Available", CC_AGENT_STATUS_AVAILABLE},
@@ -124,12 +125,7 @@ typedef enum {
CC_AGENT_STATE_IDLE = 4
} cc_agent_state_t;
-struct cc_agent_state_table {
- const char *name;
- cc_agent_state_t state;
-};
-
-static struct cc_agent_state_table AGENT_STATE_CHART[] = {
+static struct cc_state_table AGENT_STATE_CHART[] = {
{"Unknown", CC_AGENT_STATE_UNKNOWN},
{"Waiting", CC_AGENT_STATE_WAITING},
{"Receiving", CC_AGENT_STATE_RECEIVING},
@@ -147,12 +143,7 @@ typedef enum {
CC_MEMBER_STATE_ABANDONED = 4
} cc_member_state_t;
-struct cc_member_state_table {
- const char *name;
- cc_member_state_t state;
-};
-
-static struct cc_member_state_table MEMBER_STATE_CHART[] = {
+static struct cc_state_table MEMBER_STATE_CHART[] = {
{"Unknown", CC_MEMBER_STATE_UNKNOWN},
{"Waiting", CC_MEMBER_STATE_WAITING},
{"Trying", CC_MEMBER_STATE_TRYING},
@@ -162,10 +153,24 @@ static struct cc_member_state_table MEMBER_STATE_CHART[] = {
};
-/*static char queues_sql[] =
- "CREATE TABLE queues (\n"
- " name VARCHAR(255)\n" ");\n";
- */
+struct cc_member_cancel_reason_table {
+ const char *name;
+ int reason;
+};
+
+typedef enum {
+ CC_MEMBER_CANCEL_REASON_NONE,
+ CC_MEMBER_CANCEL_REASON_TIMEOUT,
+ CC_MEMBER_CANCEL_REASON_NO_AGENT_TIMEOUT
+} cc_member_cancel_reason_t;
+
+static struct cc_member_cancel_reason_table MEMBER_CANCEL_REASON_CHART[] = {
+ {"NONE", CC_MEMBER_CANCEL_REASON_NONE},
+ {"TIMEOUT", CC_MEMBER_CANCEL_REASON_TIMEOUT},
+ {"NO_AGENT_TIMEOUT", CC_MEMBER_CANCEL_REASON_NO_AGENT_TIMEOUT},
+ {NULL, 0}
+};
+
static char members_sql[] =
"CREATE TABLE members (\n"
" queue VARCHAR(255),\n"
@@ -222,7 +227,6 @@ static char agents_sql[] =
" ready_time INTEGER NOT NULL DEFAULT 0\n"
");\n";
-
static char tiers_sql[] =
"CREATE TABLE tiers (\n"
" queue VARCHAR(255),\n"
@@ -247,7 +251,7 @@ const char * cc_tier_state2str(cc_tier_state_t state)
uint8_t x;
const char *str = "Unknown";
- for (x = 0; x < (sizeof(STATE_CHART) / sizeof(struct cc_tier_state_table)) - 1; x++) {
+ for (x = 0; x < (sizeof(STATE_CHART) / sizeof(struct cc_state_table)) - 1; x++) {
if (STATE_CHART[x].state == state) {
str = STATE_CHART[x].name;
break;
@@ -262,7 +266,7 @@ cc_tier_state_t cc_tier_str2state(const char *str)
uint8_t x;
cc_tier_state_t state = CC_TIER_STATE_UNKNOWN;
- for (x = 0; x < (sizeof(STATE_CHART) / sizeof(struct cc_tier_state_table)) - 1 && STATE_CHART[x].name; x++) {
+ for (x = 0; x < (sizeof(STATE_CHART) / sizeof(struct cc_state_table)) - 1 && STATE_CHART[x].name; x++) {
if (!strcasecmp(STATE_CHART[x].name, str)) {
state = STATE_CHART[x].state;
break;
@@ -271,12 +275,41 @@ cc_tier_state_t cc_tier_str2state(const char *str)
return state;
}
+const char * cc_member_cancel_reason2str(cc_member_cancel_reason_t reason)
+{
+ uint8_t x;
+ const char *str = "NONE";
+
+ for (x = 0; x < (sizeof(MEMBER_CANCEL_REASON_CHART) / sizeof(struct cc_member_cancel_reason_table)) - 1; x++) {
+ if (MEMBER_CANCEL_REASON_CHART[x].reason == reason) {
+ str = MEMBER_CANCEL_REASON_CHART[x].name;
+ break;
+ }
+ }
+
+ return str;
+}
+
+cc_member_cancel_reason_t cc_member_cancel_str2reason(const char *str)
+{
+ uint8_t x;
+ cc_member_cancel_reason_t reason = CC_MEMBER_CANCEL_REASON_NONE;
+
+ for (x = 0; x < (sizeof(MEMBER_CANCEL_REASON_CHART) / sizeof(struct cc_member_cancel_reason_table)) - 1 && MEMBER_CANCEL_REASON_CHART[x].name; x++) {
+ if (!strcasecmp(MEMBER_CANCEL_REASON_CHART[x].name, str)) {
+ reason = MEMBER_CANCEL_REASON_CHART[x].reason;
+ break;
+ }
+ }
+ return reason;
+}
+
const char * cc_agent_status2str(cc_agent_status_t status)
{
uint8_t x;
const char *str = "Unknown";
- for (x = 0; x < (sizeof(AGENT_STATUS_CHART) / sizeof(struct cc_agent_status_table)) - 1; x++) {
+ for (x = 0; x < (sizeof(AGENT_STATUS_CHART) / sizeof(struct cc_status_table)) - 1; x++) {
if (AGENT_STATUS_CHART[x].status == status) {
str = AGENT_STATUS_CHART[x].name;
break;
@@ -291,7 +324,7 @@ cc_agent_status_t cc_agent_str2status(const char *str)
uint8_t x;
cc_agent_status_t status = CC_AGENT_STATUS_UNKNOWN;
- for (x = 0; x < (sizeof(AGENT_STATUS_CHART) / sizeof(struct cc_agent_status_table)) - 1 && AGENT_STATUS_CHART[x].name; x++) {
+ for (x = 0; x < (sizeof(AGENT_STATUS_CHART) / sizeof(struct cc_status_table)) - 1 && AGENT_STATUS_CHART[x].name; x++) {
if (!strcasecmp(AGENT_STATUS_CHART[x].name, str)) {
status = AGENT_STATUS_CHART[x].status;
break;
@@ -305,7 +338,7 @@ const char * cc_agent_state2str(cc_agent_state_t state)
uint8_t x;
const char *str = "Unknown";
- for (x = 0; x < (sizeof(AGENT_STATE_CHART) / sizeof(struct cc_agent_state_table)) - 1; x++) {
+ for (x = 0; x < (sizeof(AGENT_STATE_CHART) / sizeof(struct cc_state_table)) - 1; x++) {
if (AGENT_STATE_CHART[x].state == state) {
str = AGENT_STATE_CHART[x].name;
break;
@@ -320,7 +353,7 @@ cc_agent_state_t cc_agent_str2state(const char *str)
uint8_t x;
cc_agent_state_t state = CC_AGENT_STATE_UNKNOWN;
- for (x = 0; x < (sizeof(AGENT_STATE_CHART) / sizeof(struct cc_agent_state_table)) - 1 && AGENT_STATE_CHART[x].name; x++) {
+ for (x = 0; x < (sizeof(AGENT_STATE_CHART) / sizeof(struct cc_state_table)) - 1 && AGENT_STATE_CHART[x].name; x++) {
if (!strcasecmp(AGENT_STATE_CHART[x].name, str)) {
state = AGENT_STATE_CHART[x].state;
break;
@@ -334,7 +367,7 @@ const char * cc_member_state2str(cc_member_state_t state)
uint8_t x;
const char *str = "Unknown";
- for (x = 0; x < (sizeof(MEMBER_STATE_CHART) / sizeof(struct cc_member_state_table)) - 1; x++) {
+ for (x = 0; x < (sizeof(MEMBER_STATE_CHART) / sizeof(struct cc_state_table)) - 1; x++) {
if (MEMBER_STATE_CHART[x].state == state) {
str = MEMBER_STATE_CHART[x].name;
break;
@@ -349,7 +382,7 @@ cc_member_state_t cc_member_str2state(const char *str)
uint8_t x;
cc_member_state_t state = CC_MEMBER_STATE_UNKNOWN;
- for (x = 0; x < (sizeof(MEMBER_STATE_CHART) / sizeof(struct cc_member_state_table)) - 1 && MEMBER_STATE_CHART[x].name; x++) {
+ for (x = 0; x < (sizeof(MEMBER_STATE_CHART) / sizeof(struct cc_state_table)) - 1 && MEMBER_STATE_CHART[x].name; x++) {
if (!strcasecmp(MEMBER_STATE_CHART[x].name, str)) {
state = MEMBER_STATE_CHART[x].state;
break;
@@ -384,19 +417,27 @@ struct cc_queue {
char *moh;
char *record_template;
char *time_base_score;
+
switch_bool_t tier_rules_apply;
uint32_t tier_rule_wait_second;
switch_bool_t tier_rule_wait_multiply_level;
switch_bool_t tier_rule_no_agent_no_wait;
+
uint32_t discard_abandoned_after;
switch_bool_t abandoned_resume_allowed;
+ uint32_t max_wait_time;
+ uint32_t max_wait_time_with_no_agent;
+
switch_mutex_t *mutex;
switch_thread_rwlock_t *rwlock;
switch_memory_pool_t *pool;
uint32_t flags;
+ switch_time_t last_agent_exist;
+ switch_time_t last_agent_exist_check;
+
switch_xml_config_item_t config[CC_QUEUE_CONFIGITEM_COUNT];
switch_xml_config_string_options_t config_str_pool;
@@ -488,6 +529,7 @@ cc_queue_t *queue_set_config(cc_queue_t *queue)
SWITCH_CONFIG_SET_ITEM(queue->config[i++], "moh-sound", SWITCH_CONFIG_STRING, 0, &queue->moh, NULL, &queue->config_str_pool, NULL, NULL);
SWITCH_CONFIG_SET_ITEM(queue->config[i++], "record-template", SWITCH_CONFIG_STRING, 0, &queue->record_template, NULL, &queue->config_str_pool, NULL, NULL);
SWITCH_CONFIG_SET_ITEM(queue->config[i++], "time-base-score", SWITCH_CONFIG_STRING, 0, &queue->time_base_score, "queue", &queue->config_str_pool, NULL, NULL);
+
SWITCH_CONFIG_SET_ITEM(queue->config[i++], "tier-rules-apply", SWITCH_CONFIG_BOOL, 0, &queue->tier_rules_apply, SWITCH_FALSE, NULL, NULL, NULL);
SWITCH_CONFIG_SET_ITEM(queue->config[i++], "tier-rule-wait-second", SWITCH_CONFIG_INT, 0, &queue->tier_rule_wait_second, 0, &config_int_0_86400, NULL, NULL);
SWITCH_CONFIG_SET_ITEM(queue->config[i++], "tier-rule-wait-multiply-level", SWITCH_CONFIG_BOOL, 0, &queue->tier_rule_wait_multiply_level, SWITCH_FALSE, NULL, NULL, NULL);
@@ -495,6 +537,9 @@ cc_queue_t *queue_set_config(cc_queue_t *queue)
SWITCH_CONFIG_SET_ITEM(queue->config[i++], "discard-abandoned-after", SWITCH_CONFIG_INT, 0, &queue->discard_abandoned_after, 60, &config_int_0_86400, NULL, NULL);
SWITCH_CONFIG_SET_ITEM(queue->config[i++], "abandoned-resume-allowed", SWITCH_CONFIG_BOOL, 0, &queue->abandoned_resume_allowed, SWITCH_FALSE, NULL, NULL, NULL);
+ SWITCH_CONFIG_SET_ITEM(queue->config[i++], "max-wait-time", SWITCH_CONFIG_INT, 0, &queue->max_wait_time, 0, &config_int_0_86400, NULL, NULL);
+ SWITCH_CONFIG_SET_ITEM(queue->config[i++], "max-wait-time-with-no-agent", SWITCH_CONFIG_INT, 0, &queue->max_wait_time_with_no_agent, 0, &config_int_0_86400, NULL, NULL);
+
switch_assert(i < CC_QUEUE_CONFIGITEM_COUNT);
return queue;
@@ -646,6 +691,9 @@ static cc_queue_t *load_queue(const char *queue_name)
switch_thread_rwlock_create(&queue->rwlock, pool);
queue->name = switch_core_strdup(pool, queue_name);
+ queue->last_agent_exist = 0;
+ queue->last_agent_exist_check = 0;
+
if (!(dbh = cc_get_db_handle())) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Cannot open DB!\n");
goto end;
@@ -702,7 +750,7 @@ static cc_queue_t *get_queue(const char *queue_name)
struct call_helper {
const char *member_uuid;
- const char *queue;
+ const char *queue_name;
const char *queue_strategy;
const char *member_joined_epoch;
const char *member_caller_name;
@@ -744,7 +792,7 @@ int cc_queue_count(const char *queue)
count = atoi(res);
if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CALLCENTER_EVENT) == SWITCH_STATUS_SUCCESS) {
- switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Name", queue);
+ switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Queue", queue);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Action", "members-count");
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Count", res);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Selection", event_name);
@@ -827,7 +875,7 @@ cc_agent_status_t cc_agent_get(const char *key, const char *agent, char *ret_res
result = CC_STATUS_SUCCESS;
if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CALLCENTER_EVENT) == SWITCH_STATUS_SUCCESS) {
- switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Name", agent);
+ switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Agent", agent);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Action", "agent-status-get");
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Agent-Status", res);
switch_event_fire(&event);
@@ -894,7 +942,7 @@ cc_status_t cc_agent_update(const char *key, const char *value, const char *agen
result = CC_STATUS_SUCCESS;
if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CALLCENTER_EVENT) == SWITCH_STATUS_SUCCESS) {
- switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Name", agent);
+ switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Agent", agent);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Action", "agent-status-change");
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Agent-Status", value);
switch_event_fire(&event);
@@ -918,7 +966,7 @@ cc_status_t cc_agent_update(const char *key, const char *value, const char *agen
result = CC_STATUS_SUCCESS;
if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CALLCENTER_EVENT) == SWITCH_STATUS_SUCCESS) {
- switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Name", agent);
+ switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Agent", agent);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Action", "agent-state-change");
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Agent-State", value);
switch_event_fire(&event);
@@ -1118,13 +1166,13 @@ done:
return result;
}
-cc_status_t cc_tier_del(const char *queue, const char *agent)
+cc_status_t cc_tier_del(const char *queue_name, const char *agent)
{
cc_status_t result = CC_STATUS_SUCCESS;
char *sql;
- switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Deleted tier Agent %s in Queue %s\n", agent, queue);
- sql = switch_mprintf("DELETE FROM tiers WHERE queue = '%q' AND agent = '%q';", queue, agent);
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Deleted tier Agent %s in Queue %s\n", agent, queue_name);
+ sql = switch_mprintf("DELETE FROM tiers WHERE queue = '%q' AND agent = '%q';", queue_name, agent);
cc_execute_sql(NULL, sql, NULL);
switch_safe_free(sql);
@@ -1307,7 +1355,7 @@ static void *SWITCH_THREAD_FUNC outbound_agent_thread_run(switch_thread_t *threa
}
if (!strcasecmp(h->agent_type, CC_AGENT_TYPE_CALLBACK)) {
switch_event_create(&ovars, SWITCH_EVENT_REQUEST_PARAMS);
- switch_event_add_header(ovars, SWITCH_STACK_BOTTOM, "cc_queue", "%s", h->queue);
+ switch_event_add_header(ovars, SWITCH_STACK_BOTTOM, "cc_queue", "%s", h->queue_name);
switch_event_add_header(ovars, SWITCH_STACK_BOTTOM, "cc_member_uuid", "%s", h->member_uuid);
switch_event_add_header(ovars, SWITCH_STACK_BOTTOM, "cc_member_pre_answer_uuid", "%s", h->member_uuid);
switch_event_add_header(ovars, SWITCH_STACK_BOTTOM, "cc_agent", "%s", h->agent_name);
@@ -1327,7 +1375,7 @@ static void *SWITCH_THREAD_FUNC outbound_agent_thread_run(switch_thread_t *threa
switch_event_t *event;
const char *cc_warning_tone = switch_channel_get_variable(agent_channel, "cc_warning_tone");
- switch_channel_set_variable(agent_channel, "cc_queue", h->queue);
+ switch_channel_set_variable(agent_channel, "cc_queue", h->queue_name);
switch_channel_set_variable(agent_channel, "cc_agent", h->agent_name);
switch_channel_set_variable(agent_channel, "cc_agent_type", h->agent_type);
switch_channel_set_variable(agent_channel, "cc_member_uuid", h->member_uuid);
@@ -1384,7 +1432,7 @@ static void *SWITCH_THREAD_FUNC outbound_agent_thread_run(switch_thread_t *threa
if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CALLCENTER_EVENT) == SWITCH_STATUS_SUCCESS) {
switch_channel_event_set_data(agent_channel, event);
- switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Name", h->queue);
+ switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Queue", h->queue_name);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Action", "bridge-agent-start");
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Agent", h->agent_name);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Agent-System", h->agent_system);
@@ -1407,11 +1455,7 @@ static void *SWITCH_THREAD_FUNC outbound_agent_thread_run(switch_thread_t *threa
switch_safe_free(sql);
/* Change the agents Status in the tiers */
- sql = switch_mprintf("UPDATE tiers SET state = '%q' WHERE agent = '%q' AND queue = '%q'",
- cc_tier_state2str(CC_TIER_STATE_ACTIVE_INBOUND), h->agent_name, h->queue);
- cc_execute_sql(NULL, sql, NULL);
- switch_safe_free(sql);
-
+ cc_tier_update("state", cc_tier_state2str(CC_TIER_STATE_ACTIVE_INBOUND), h->queue_name, h->agent_name);
cc_agent_update("state", cc_agent_state2str(CC_AGENT_STATE_IN_A_QUEUE_CALL), h->agent_name);
/* Record session if record-template is provided */
@@ -1424,10 +1468,13 @@ static void *SWITCH_THREAD_FUNC outbound_agent_thread_run(switch_thread_t *threa
}
}
- switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Agent %s answered \"%s\" (%s) from queue %s%s\n",
- h->agent_name, h->member_caller_name, h->member_caller_number, h->queue, (h->record_template?" (Recorded)":""));
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Agent %s answered \"%s\" <%s> from queue %s%s\n",
+ h->agent_name, h->member_caller_name, h->member_caller_number, h->queue_name, (h->record_template?" (Recorded)":""));
switch_ivr_uuid_bridge(h->member_uuid, switch_core_session_get_uuid(agent_session));
+ /* This is used for the waiting caller to quit waiting for a agent */
+ switch_channel_set_variable(member_channel, "cc_agent_uuid", agent_uuid);
+
/* Wait until the member hangup or the agent hangup. This will quit also if the agent transfer the call */
while(switch_channel_up(member_channel) && switch_channel_up(agent_channel) && globals.running) {
switch_yield(100000);
@@ -1436,7 +1483,7 @@ static void *SWITCH_THREAD_FUNC outbound_agent_thread_run(switch_thread_t *threa
if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CALLCENTER_EVENT) == SWITCH_STATUS_SUCCESS) {
switch_channel_event_set_data(agent_channel, event);
- switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Name", h->queue);
+ switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Queue", h->queue_name);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Action", "bridge-agent-end");
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Agent", h->agent_name);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Agent-System", h->agent_system);
@@ -1452,7 +1499,7 @@ static void *SWITCH_THREAD_FUNC outbound_agent_thread_run(switch_thread_t *threa
/* Update Agents Items */
/* Do not remove uuid of the agent if we are a standby agent */
sql = switch_mprintf("UPDATE agents SET %q last_bridge_end = %ld, talk_time = talk_time + (%ld-last_bridge_start) WHERE name = '%q' AND system = '%q';"
- , (strcasecmp(h->agent_type, "uuid-standby")?"uuid = '',":""), (long) switch_epoch_time_now(NULL), (long) switch_epoch_time_now(NULL), h->agent_name, h->agent_system);
+ , (strcasecmp(h->agent_type, CC_AGENT_TYPE_UUID_STANDBY)?"uuid = '',":""), (long) switch_epoch_time_now(NULL), (long) switch_epoch_time_now(NULL), h->agent_name, h->agent_system);
cc_execute_sql(NULL, sql, NULL);
switch_safe_free(sql);
@@ -1464,7 +1511,7 @@ static void *SWITCH_THREAD_FUNC outbound_agent_thread_run(switch_thread_t *threa
/* Caller off event */
if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CALLCENTER_EVENT) == SWITCH_STATUS_SUCCESS) {
switch_channel_event_set_data(member_channel, event);
- switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Name", h->queue);
+ switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Queue", h->queue_name);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Action", "member-queue-end");
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Cause", "Terminated");
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "CC-Agent-Answer-Time", "%ld", (long) (t_agent_answered - t_agent_called));
@@ -1472,8 +1519,10 @@ static void *SWITCH_THREAD_FUNC outbound_agent_thread_run(switch_thread_t *threa
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "CC-Talk-Time", "%ld", (long) (switch_epoch_time_now(NULL) - t_agent_answered));
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "CC-Total-Time", "%ld", (long) (switch_epoch_time_now(NULL) - t_member_called));
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Caller-UUID", h->member_uuid);
- switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Caller-CID-Name", switch_str_nil(switch_channel_get_variable(member_channel, "caller_id_name")));
- switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Caller-CID-Number", switch_str_nil(switch_channel_get_variable(member_channel, "caller_id_number")));
+ switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Caller-CID-Name",
+ switch_str_nil(switch_channel_get_variable(member_channel, "caller_id_name")));
+ switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Caller-CID-Number",
+ switch_str_nil(switch_channel_get_variable(member_channel, "caller_id_number")));
switch_event_fire(&event);
}
@@ -1538,8 +1587,8 @@ done:
sql = switch_mprintf(
"UPDATE tiers SET state = '%q' WHERE agent = '%q' AND queue = '%q' AND (state = '%q' OR state = '%q' OR state = '%q');"
"UPDATE tiers SET state = '%q' WHERE agent = '%q' AND NOT queue = '%q' AND state = '%q'"
- , cc_tier_state2str(tiers_state), h->agent_name, h->queue, cc_tier_state2str(CC_TIER_STATE_ACTIVE_INBOUND), cc_tier_state2str(CC_TIER_STATE_STANDBY), cc_tier_state2str(CC_TIER_STATE_OFFERING),
- cc_tier_state2str(CC_TIER_STATE_READY), h->agent_name, h->queue, cc_tier_state2str(CC_TIER_STATE_STANDBY));
+ , cc_tier_state2str(tiers_state), h->agent_name, h->queue_name, cc_tier_state2str(CC_TIER_STATE_ACTIVE_INBOUND), cc_tier_state2str(CC_TIER_STATE_STANDBY), cc_tier_state2str(CC_TIER_STATE_OFFERING),
+ cc_tier_state2str(CC_TIER_STATE_READY), h->agent_name, h->queue_name, cc_tier_state2str(CC_TIER_STATE_STANDBY));
cc_execute_sql(NULL, sql, NULL);
switch_safe_free(sql);
@@ -1567,7 +1616,7 @@ done:
}
struct agent_callback {
- const char *queue;
+ const char *queue_name;
const char *system;
const char *uuid;
const char *caller_number;
@@ -1579,6 +1628,7 @@ struct agent_callback {
uint32_t tier_rule_wait_second;
switch_bool_t tier_rule_wait_multiply_level;
switch_bool_t tier_rule_no_agent_no_wait;
+ switch_bool_t agent_found;
int tier;
int tier_agent_available;
@@ -1602,6 +1652,8 @@ static int agents_callback(void *pArg, int argc, char **argv, char **columnNames
switch_bool_t contact_agent = SWITCH_TRUE;
+ cbt->agent_found = SWITCH_TRUE;
+
/* Check if we switch to a different tier, if so, check if we should continue further for that member */
if (cbt->tier_rules_apply == SWITCH_TRUE && atoi(agent_tier_level) > cbt->tier) {
@@ -1700,7 +1752,7 @@ static int agents_callback(void *pArg, int argc, char **argv, char **columnNames
h->member_joined_epoch = switch_core_strdup(h->pool, cbt->joined_epoch);
h->member_caller_name = switch_core_strdup(h->pool, cbt->caller_name);
h->member_caller_number = switch_core_strdup(h->pool, cbt->caller_number);
- h->queue = switch_core_strdup(h->pool, cbt->queue);
+ h->queue_name = switch_core_strdup(h->pool, cbt->queue_name);
h->record_template = switch_core_strdup(h->pool, cbt->record_template);
h->no_answer_count = atoi(argv[4]);
h->max_no_answer = atoi(argv[5]);
@@ -1713,8 +1765,8 @@ static int agents_callback(void *pArg, int argc, char **argv, char **columnNames
sql = switch_mprintf(
"UPDATE tiers SET state = '%q' WHERE agent = '%q' AND queue = '%q';"
"UPDATE tiers SET state = '%q' WHERE agent = '%q' AND NOT queue = '%q' AND state = '%q';",
- cc_tier_state2str(CC_TIER_STATE_OFFERING), h->agent_name, h->queue,
- cc_tier_state2str(CC_TIER_STATE_STANDBY), h->agent_name, h->queue, cc_tier_state2str(CC_TIER_STATE_READY));
+ cc_tier_state2str(CC_TIER_STATE_OFFERING), h->agent_name, h->queue_name,
+ cc_tier_state2str(CC_TIER_STATE_STANDBY), h->agent_name, h->queue_name, cc_tier_state2str(CC_TIER_STATE_READY));
cc_execute_sql(NULL, sql, NULL);
switch_safe_free(sql);
@@ -1796,9 +1848,10 @@ static int members_callback(void *pArg, int argc, char **argv, char **columnName
cbt.caller_number = argv[2];
cbt.caller_name = argv[3];
cbt.joined_epoch = argv[4];
- cbt.queue = argv[0];
+ cbt.queue_name = argv[0];
cbt.strategy = queue_strategy;
cbt.record_template = queue_record_template;
+ cbt.agent_found = SWITCH_FALSE;
if (!strcasecmp(queue->strategy, "longest-idle-agent")) {
sql_order_by = switch_mprintf("level, agents.last_offered_call, position");
@@ -1832,6 +1885,18 @@ static int members_callback(void *pArg, int argc, char **argv, char **columnName
switch_safe_free(sql);
switch_safe_free(sql_order_by);
+ /* We update a field in the queue struct so we can kick caller out if waiting for too long with no agent */
+ if (!argv[0] || !(queue = get_queue(argv[0]))) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Queue %s not found locally, skip this member\n", argv[0]);
+ goto end;
+ } else {
+ queue->last_agent_exist_check = switch_epoch_time_now(NULL);
+ if (cbt.agent_found) {
+ queue->last_agent_exist = queue->last_agent_exist_check;
+ }
+ queue_rwunlock(queue);
+ }
+
end:
switch_safe_free(queue_name);
switch_safe_free(queue_strategy);
@@ -1911,30 +1976,53 @@ void cc_agent_dispatch_thread_start(void)
switch_thread_create(&thread, thd_attr, cc_agent_dispatch_thread_run, NULL, globals.pool);
}
-struct member_helper {
- const char *uuid;
+struct member_thread_helper {
+ const char *member_uuid;
+ const char *queue_name;
+ switch_time_t t_member_called;
+ cc_member_cancel_reason_t member_cancel_reason;
+
int running;
switch_memory_pool_t *pool;
};
void *SWITCH_THREAD_FUNC cc_member_thread_run(switch_thread_t *thread, void *obj)
{
- struct member_helper *m = (struct member_helper *) obj;
- switch_core_session_t *member_session = switch_core_session_locate(m->uuid);
- switch_channel_t *channel = NULL;
+ struct member_thread_helper *m = (struct member_thread_helper *) obj;
+ switch_core_session_t *member_session = switch_core_session_locate(m->member_uuid);
+ switch_channel_t *member_channel = NULL;
switch_mutex_lock(globals.mutex);
globals.threads++;
switch_mutex_unlock(globals.mutex);
if (member_session) {
- channel = switch_core_session_get_channel(member_session);
+ member_channel = switch_core_session_get_channel(member_session);
} else {
switch_core_destroy_memory_pool(&m->pool);
return NULL;
}
- while(switch_channel_ready(channel) && m->running && globals.running) {
+ while(switch_channel_ready(member_channel) && m->running && globals.running) {
+ cc_queue_t *queue = NULL;
+
+ if (!m->queue_name || !(queue = get_queue(m->queue_name))) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Queue %s not found\n", m->queue_name);
+ break;
+ }
+ /* Make the Caller Leave if he went over his max wait time */
+ if (queue->max_wait_time > 0 && queue->max_wait_time <= switch_epoch_time_now(NULL) - m->t_member_called) {
+ m->member_cancel_reason = CC_MEMBER_CANCEL_REASON_TIMEOUT;
+ switch_channel_set_flag_value(member_channel, CF_BREAK, 2);
+ }
+
+ /* Will drop the caller if no agent was found for more than X secondes */
+ if (queue->max_wait_time_with_no_agent > 0 && m->t_member_called < queue->last_agent_exist_check - CC_MAX_TIME_DIFF_CHECK &&
+ queue->last_agent_exist_check - queue->last_agent_exist >= queue->max_wait_time_with_no_agent) {
+ m->member_cancel_reason = CC_MEMBER_CANCEL_REASON_NO_AGENT_TIMEOUT;
+ switch_channel_set_flag_value(member_channel, CF_BREAK, 2);
+ }
+
/* TODO Go thought the list of phrases */
/* SAMPLE CODE to playback something over the MOH
@@ -1949,8 +2037,12 @@ void *SWITCH_THREAD_FUNC cc_member_thread_run(switch_thread_t *thread, void *obj
/* If Agent Logoff, we might need to recalculare score based on skill */
/* Play Announcement in order */
- switch_yield(100000);
+
+ queue_rwunlock(queue);
+
+ switch_yield(500000);
}
+
switch_core_session_rwunlock(member_session);
switch_core_destroy_memory_pool(&m->pool);
@@ -1971,26 +2063,27 @@ SWITCH_STANDARD_APP(callcenter_function)
char *mydata = NULL;
cc_queue_t *queue = NULL;
const char *queue_name = NULL;
- switch_channel_t *member_channel = switch_core_session_get_channel(session);
+ switch_core_session_t *member_session = session;
+ switch_channel_t *member_channel = switch_core_session_get_channel(member_session);
char *sql = NULL;
- char *uuid = switch_core_session_get_uuid(session);
- switch_input_args_t args = { 0 };
- struct member_helper *h = NULL;
+ char *member_uuid = switch_core_session_get_uuid(member_session);
+ struct member_thread_helper *h = NULL;
switch_thread_t *thread;
switch_threadattr_t *thd_attr = NULL;
switch_memory_pool_t *pool;
- int cc_base_score_int = 0;
switch_channel_timetable_t *times = NULL;
- const char *cc_base_score = switch_channel_get_variable(member_channel, "cc_base_score");
const char *cc_moh_override = switch_channel_get_variable(member_channel, "cc_moh_override");
+ const char *cc_base_score = switch_channel_get_variable(member_channel, "cc_base_score");
+ int cc_base_score_int = 0;
const char *cur_moh = NULL;
char start_epoch[64];
switch_event_t *event;
switch_time_t t_member_called = switch_epoch_time_now(NULL);
long abandoned_epoch = 0;
+ const char *agent_uuid = NULL;
if (!zstr(data)) {
- mydata = switch_core_session_strdup(session, data);
+ mydata = switch_core_session_strdup(member_session, data);
argc = switch_separate_string(mydata, ' ', argv, (sizeof(argv) / sizeof(argv[0])));
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "No Queue name provided\n");
@@ -2013,7 +2106,7 @@ SWITCH_STANDARD_APP(callcenter_function)
times = switch_channel_get_timetable(member_channel);
switch_snprintf(start_epoch, sizeof(start_epoch), "%" SWITCH_TIME_T_FMT, times->answered / 1000000);
- /* Check of we have a queued abandoned member we can resume from */
+ /* Check if we support and have a queued abandoned member we can resume from */
if (queue->abandoned_resume_allowed == SWITCH_TRUE) {
char res[256];
@@ -2037,9 +2130,9 @@ SWITCH_STANDARD_APP(callcenter_function)
if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CALLCENTER_EVENT) == SWITCH_STATUS_SUCCESS) {
switch_channel_event_set_data(member_channel, event);
- switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Name", queue_name);
+ switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Queue", queue_name);
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "CC-Action", "member-queue-%s", (abandoned_epoch==0?"start":"resume"));
- switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Caller-UUID", uuid);
+ switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Caller-UUID", member_uuid);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Caller-CID-Name", switch_str_nil(switch_channel_get_variable(member_channel, "caller_id_name")));
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Caller-CID-Number", switch_str_nil(switch_channel_get_variable(member_channel, "caller_id_number")));
switch_event_fire(&event);
@@ -2054,7 +2147,7 @@ SWITCH_STANDARD_APP(callcenter_function)
" (queue,system,uuid,system_epoch,joined_epoch,base_score,skill_score,caller_number,caller_name,serving_agent,serving_system,state)"
" VALUES('%q','single_box','%q','%q','%ld','%d','%d','%q','%q','%q','','%q')",
queue_name,
- uuid,
+ member_uuid,
start_epoch,
(long) switch_epoch_time_now(NULL),
cc_base_score_int,
@@ -2069,12 +2162,12 @@ SWITCH_STANDARD_APP(callcenter_function)
char res[256];
/* Update abandoned member */
sql = switch_mprintf("UPDATE members SET uuid = '%q', state = '%q', rejoined_epoch = '%ld' WHERE caller_number = '%q' AND abandoned_epoch = '%ld' AND state = '%q' AND queue = '%q'",
- uuid, cc_member_state2str(CC_MEMBER_STATE_WAITING), (long) switch_epoch_time_now(NULL), switch_str_nil(switch_channel_get_variable(member_channel, "caller_id_number")), abandoned_epoch, cc_member_state2str(CC_MEMBER_STATE_ABANDONED), queue_name);
+ member_uuid, cc_member_state2str(CC_MEMBER_STATE_WAITING), (long) switch_epoch_time_now(NULL), switch_str_nil(switch_channel_get_variable(member_channel, "caller_id_number")), abandoned_epoch, cc_member_state2str(CC_MEMBER_STATE_ABANDONED), queue_name);
cc_execute_sql(queue, sql, NULL);
switch_safe_free(sql);
/* Confirm we took that member in */
- sql = switch_mprintf("SELECT abandoned_epoch FROM members WHERE uuid = '%q' AND state = '%q' AND queue = '%q'", uuid, cc_member_state2str(CC_MEMBER_STATE_WAITING), queue_name);
+ sql = switch_mprintf("SELECT abandoned_epoch FROM members WHERE uuid = '%q' AND state = '%q' AND queue = '%q'", member_uuid, cc_member_state2str(CC_MEMBER_STATE_WAITING), queue_name);
cc_execute_sql2str(NULL, NULL, sql, res, sizeof(res));
switch_safe_free(sql);
@@ -2092,9 +2185,14 @@ SWITCH_STANDARD_APP(callcenter_function)
/* Start Thread that will playback different prompt to the channel */
switch_core_new_memory_pool(&pool);
h = switch_core_alloc(pool, sizeof(*h));
+
h->pool = pool;
- h->uuid = switch_core_strdup(h->pool, uuid);
+ h->member_uuid = switch_core_strdup(h->pool, member_uuid);
+ h->queue_name = switch_core_strdup(h->pool, queue_name);
+ h->t_member_called = t_member_called;
+ h->member_cancel_reason = CC_MEMBER_CANCEL_REASON_NONE;
h->running = 1;
+
switch_threadattr_create(&thd_attr, h->pool);
switch_threadattr_detach_set(thd_attr, 1);
switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE);
@@ -2104,16 +2202,37 @@ SWITCH_STANDARD_APP(callcenter_function)
/* TODO Add DTMF callback support */
/* TODO add MOH infitite loop */
if (cc_moh_override) {
- cur_moh = switch_core_session_strdup(session, cc_moh_override);
+ cur_moh = switch_core_session_strdup(member_session, cc_moh_override);
} else {
- cur_moh = switch_core_session_strdup(session, queue->moh);
+ cur_moh = switch_core_session_strdup(member_session, queue->moh);
}
queue_rwunlock(queue);
- if (cur_moh) {
- switch_ivr_play_file(session, NULL, cur_moh, &args);
- } else {
- switch_ivr_collect_digits_callback(session, &args, 0, 0);
+ while (switch_channel_ready(member_channel)) {
+ switch_input_args_t args = { 0 };
+
+ /* An agent was found, time to exit and let the bridge do it job */
+ if ((agent_uuid = switch_channel_get_variable(member_channel, "cc_agent_uuid"))) {
+ break;
+ }
+ /* If the member thread set a different reason, we monitor it so we can quit the wait */
+ if (h->member_cancel_reason != CC_MEMBER_CANCEL_REASON_NONE) {
+ break;
+ }
+
+ switch_core_session_flush_private_events(member_session);
+
+ if (cur_moh) {
+ switch_status_t status = switch_ivr_play_file(member_session, NULL, cur_moh, &args);
+
+ if (!SWITCH_READ_ACCEPTABLE(status)) {
+ break;
+ }
+
+ } else {
+ switch_ivr_collect_digits_callback(session, &args, 0, 0);
+ }
+
}
/* Stop Member Thread */
@@ -2123,33 +2242,42 @@ SWITCH_STANDARD_APP(callcenter_function)
/* Hangup any agents been callback */
if (!switch_channel_up(member_channel)) { /* If channel is still up, it mean that the member didn't hangup, so we should leave the agent alone */
- switch_core_session_hupall_matching_var("cc_member_uuid", uuid, SWITCH_CAUSE_ORIGINATOR_CANCEL);
+ switch_core_session_hupall_matching_var("cc_member_uuid", member_uuid, SWITCH_CAUSE_ORIGINATOR_CANCEL);
sql = switch_mprintf("UPDATE members SET state = '%q', uuid = '', abandoned_epoch = '%ld' WHERE system = 'single_box' AND uuid = '%q'",
- cc_member_state2str(CC_MEMBER_STATE_ABANDONED), (long) switch_epoch_time_now(NULL), uuid);
+ cc_member_state2str(CC_MEMBER_STATE_ABANDONED), (long) switch_epoch_time_now(NULL), member_uuid);
cc_execute_sql(NULL, sql, NULL);
switch_safe_free(sql);
+ /* Generate an Event and update some channel variable */
if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CALLCENTER_EVENT) == SWITCH_STATUS_SUCCESS) {
switch_channel_event_set_data(member_channel, event);
- switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Name", queue_name);
+ switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Queue", queue_name);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Action", "member-queue-end");
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "CC-Wait-Time", "%ld", (long) (switch_epoch_time_now(NULL) - t_member_called));
- switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Cause", "Abort");
- switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Caller-UUID", uuid);
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Member \"%s\" <%s> exit queue %s due to %s\n",
+ switch_str_nil(switch_channel_get_variable(member_channel, "caller_id_name")),
+ switch_str_nil(switch_channel_get_variable(member_channel, "caller_id_number")),
+ queue_name, cc_member_cancel_reason2str(h->member_cancel_reason));
+
+ switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Cause", cc_member_cancel_reason2str(h->member_cancel_reason));
+ switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Caller-UUID", member_uuid);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Caller-CID-Name", switch_str_nil(switch_channel_get_variable(member_channel, "caller_id_name")));
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Caller-CID-Number", switch_str_nil(switch_channel_get_variable(member_channel, "caller_id_number")));
switch_event_fire(&event);
}
+
/* for xml_cdr needs */
switch_channel_set_variable_printf(member_channel, "cc_queue_canceled_epoch", "%ld", (long) switch_epoch_time_now(NULL));
+ switch_channel_set_variable_printf(member_channel, "cc_cause", "%s", cc_member_cancel_reason2str(h->member_cancel_reason));
/* Send Event with queue count */
cc_queue_count(queue_name);
} else {
+ switch_channel_set_variable_printf(member_channel, "cc_cause", "%s", "answered");
sql = switch_mprintf("UPDATE members SET state = '%q', bridge_epoch = '%ld' WHERE system = 'single_box' AND uuid = '%q'",
- cc_member_state2str(CC_MEMBER_STATE_ANSWERED), (long) switch_epoch_time_now(NULL), uuid);
+ cc_member_state2str(CC_MEMBER_STATE_ANSWERED), (long) switch_epoch_time_now(NULL), member_uuid);
cc_execute_sql(NULL, sql, NULL);
switch_safe_free(sql);
@@ -2525,7 +2653,7 @@ SWITCH_STANDARD_API(cc_config_api_function)
stream->write_function(stream, "%d\n", atoi(res));
if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CALLCENTER_EVENT) == SWITCH_STATUS_SUCCESS) {
- switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Name", queue_name);
+ switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Queue", queue_name);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Action", "members-count");
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Count", res);
switch_event_fire(&event);
diff --git a/src/mod/endpoints/mod_sofia/mod_sofia.c b/src/mod/endpoints/mod_sofia/mod_sofia.c
index 58308225ff..19a44f43e1 100644
--- a/src/mod/endpoints/mod_sofia/mod_sofia.c
+++ b/src/mod/endpoints/mod_sofia/mod_sofia.c
@@ -697,7 +697,11 @@ static switch_status_t sofia_answer_channel(switch_core_session_t *session)
if (!sofia_test_flag(tech_pvt, TFLAG_BYE)) {
char *extra_headers = sofia_glue_get_extra_headers(channel, SOFIA_SIP_RESPONSE_HEADER_PREFIX);
- char *cid = generate_pai_str(session);
+ char *cid = NULL;
+
+ if (sofia_test_pflag(tech_pvt->profile, PFLAG_PASS_CALLEE_ID)) {
+ cid = generate_pai_str(session);
+ }
if (switch_channel_test_flag(tech_pvt->channel, CF_PROXY_MODE) && tech_pvt->early_sdp && strcmp(tech_pvt->early_sdp, tech_pvt->local_sdp_str)) {
/* The SIP RFC for SOA forbids sending a 183 with one sdp then a 200 with another but it won't do us much good unless
@@ -1761,7 +1765,14 @@ static switch_status_t sofia_receive_message(switch_core_session_t *session, swi
nua_update(tech_pvt->nh,
TAG_IF(!zstr_buf(message), SIPTAG_HEADER_STR(message)),
TAG_IF(!zstr(tech_pvt->user_via), SIPTAG_VIA_STR(tech_pvt->user_via)), TAG_END());
- }
+ } else if ((ua && (switch_stristr("cisco", ua)))) {
+ snprintf(message, sizeof(message), "P-Asserted-Identity: \"%s\" ", name, number, tech_pvt->profile->sipip);
+
+ sofia_set_flag_locked(tech_pvt, TFLAG_UPDATING_DISPLAY);
+ nua_update(tech_pvt->nh,
+ TAG_IF(!zstr_buf(message), SIPTAG_HEADER_STR(message)),
+ TAG_IF(!zstr(tech_pvt->user_via), SIPTAG_VIA_STR(tech_pvt->user_via)), TAG_END());
+ }
tech_pvt->last_sent_callee_id_name = switch_core_session_strdup(tech_pvt->session, name);
tech_pvt->last_sent_callee_id_number = switch_core_session_strdup(tech_pvt->session, number);
@@ -2158,7 +2169,11 @@ static switch_status_t sofia_receive_message(switch_core_session_t *session, swi
if (!sofia_test_flag(tech_pvt, TFLAG_BYE)) {
char *extra_header = sofia_glue_get_extra_headers(channel, SOFIA_SIP_PROGRESS_HEADER_PREFIX);
- char *cid = generate_pai_str(session);
+ char *cid = NULL;
+
+ if (sofia_test_pflag(tech_pvt->profile, PFLAG_PASS_CALLEE_ID)) {
+ cid = generate_pai_str(session);
+ }
if (switch_channel_test_flag(tech_pvt->channel, CF_PROXY_MODE) &&
tech_pvt->early_sdp && strcmp(tech_pvt->early_sdp, tech_pvt->local_sdp_str)) {
diff --git a/src/mod/endpoints/mod_sofia/sofia_glue.c b/src/mod/endpoints/mod_sofia/sofia_glue.c
index 15efc1a3e4..82b933c981 100644
--- a/src/mod/endpoints/mod_sofia/sofia_glue.c
+++ b/src/mod/endpoints/mod_sofia/sofia_glue.c
@@ -4664,22 +4664,22 @@ void sofia_glue_tech_track(sofia_profile_t *profile, switch_core_session_t *sess
return;
}
- if (sofia_test_flag(tech_pvt, TFLAG_TRACKED)) {
- sofia_glue_tech_untrack(profile, session, SWITCH_TRUE);
- }
-
if (switch_ivr_generate_xml_cdr(session, &cdr) == SWITCH_STATUS_SUCCESS) {
xml_cdr_text = switch_xml_toxml(cdr, SWITCH_FALSE);
switch_xml_free(cdr);
}
if (xml_cdr_text) {
- sql = switch_mprintf("insert into sip_recovery (runtime_uuid, profile_name, hostname, uuid, metadata) values ('%q','%q','%q','%q','%q')",
- switch_core_get_uuid(), profile->name, mod_sofia_globals.hostname, switch_core_session_get_uuid(session), xml_cdr_text);
-
+ if (sofia_test_flag(tech_pvt, TFLAG_TRACKED)) {
+ sql = switch_mprintf("update sip_recovery set metadata='%q' where uuid='%q'", xml_cdr_text, switch_core_session_get_uuid(session));
+ } else {
+
+ sql = switch_mprintf("insert into sip_recovery (runtime_uuid, profile_name, hostname, uuid, metadata) values ('%q','%q','%q','%q','%q')",
+ switch_core_get_uuid(), profile->name, mod_sofia_globals.hostname, switch_core_session_get_uuid(session), xml_cdr_text);
+ }
+
if (sofia_test_pflag(profile, PFLAG_TRACK_CALLS_EVENTS)) {
switch_event_t *event = NULL;
-
if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, MY_EVENT_RECOVERY_SEND) == SWITCH_STATUS_SUCCESS) {
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "profile_name", profile->name);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "sql", sql);
@@ -4687,8 +4687,8 @@ void sofia_glue_tech_track(sofia_profile_t *profile, switch_core_session_t *sess
}
}
+ sofia_glue_execute_sql(profile, &sql, SWITCH_TRUE);
- sofia_glue_execute_sql_now(profile, &sql, SWITCH_TRUE);
free(xml_cdr_text);
sofia_set_flag(tech_pvt, TFLAG_TRACKED);
diff --git a/src/mod/endpoints/mod_sofia/sofia_reg.c b/src/mod/endpoints/mod_sofia/sofia_reg.c
index eb9b03d958..c78ed90fe3 100644
--- a/src/mod/endpoints/mod_sofia/sofia_reg.c
+++ b/src/mod/endpoints/mod_sofia/sofia_reg.c
@@ -673,8 +673,8 @@ void sofia_reg_check_expire(sofia_profile_t *profile, time_t now, int reboot)
if (now && sofia_test_pflag(profile, PFLAG_NAT_OPTIONS_PING)) {
switch_snprintf(sql, sizeof(sql), "select call_id,sip_user,sip_host,contact,status,rpid,"
"expires,user_agent,server_user,server_host,profile_name"
- " from sip_registrations where (status like '%%AUTO-NAT%%' "
- "or status like '%%UDP-NAT%%') and hostname='%s'", mod_sofia_globals.hostname);
+ " from sip_registrations where (status like '%%NAT%%' "
+ "or contact like '%%fs_nat=true%%') and hostname='%s'", mod_sofia_globals.hostname);
sofia_glue_execute_sql_callback(profile, NULL, sql, sofia_reg_nat_callback, profile);
}
diff --git a/src/switch_channel.c b/src/switch_channel.c
index d1308a1e9f..e3d77d7239 100644
--- a/src/switch_channel.c
+++ b/src/switch_channel.c
@@ -2035,6 +2035,36 @@ SWITCH_DECLARE(void) switch_channel_set_hunt_caller_profile(switch_channel_t *ch
switch_mutex_unlock(channel->profile_mutex);
}
+SWITCH_DECLARE(void) switch_channel_set_origination_caller_profile(switch_channel_t *channel, switch_caller_profile_t *caller_profile)
+{
+ switch_assert(channel != NULL);
+ switch_assert(channel->caller_profile != NULL);
+
+ switch_mutex_lock(channel->profile_mutex);
+
+ if (channel->caller_profile) {
+ caller_profile->next = channel->caller_profile->origination_caller_profile;
+ channel->caller_profile->origination_caller_profile = caller_profile;
+ }
+ switch_assert(channel->caller_profile->origination_caller_profile->next != channel->caller_profile->origination_caller_profile);
+ switch_mutex_unlock(channel->profile_mutex);
+}
+
+SWITCH_DECLARE(switch_caller_profile_t *) switch_channel_get_origination_caller_profile(switch_channel_t *channel)
+{
+ switch_caller_profile_t *profile = NULL;
+ switch_assert(channel != NULL);
+
+ switch_mutex_lock(channel->profile_mutex);
+ if (channel->caller_profile) {
+ profile = channel->caller_profile->origination_caller_profile;
+ }
+ switch_mutex_unlock(channel->profile_mutex);
+
+ return profile;
+}
+
+
SWITCH_DECLARE(void) switch_channel_set_originatee_caller_profile(switch_channel_t *channel, switch_caller_profile_t *caller_profile)
{
switch_assert(channel != NULL);
diff --git a/src/switch_core_session.c b/src/switch_core_session.c
index 74131644f9..9e942fea5e 100644
--- a/src/switch_core_session.c
+++ b/src/switch_core_session.c
@@ -524,6 +524,14 @@ SWITCH_DECLARE(switch_call_cause_t) switch_core_session_outgoing_channel(switch_
switch_channel_set_originator_caller_profile(peer_channel, cloned_profile);
}
}
+
+
+ if ((profile = switch_channel_get_caller_profile(peer_channel))) {
+ if ((cloned_profile = switch_caller_profile_clone(session, profile)) != 0) {
+ switch_channel_set_origination_caller_profile(channel, cloned_profile);
+ }
+ }
+
}
if (switch_event_create(&event, SWITCH_EVENT_CHANNEL_OUTGOING) == SWITCH_STATUS_SUCCESS) {
diff --git a/src/switch_ivr.c b/src/switch_ivr.c
index 61deece636..c1dc33651c 100644
--- a/src/switch_ivr.c
+++ b/src/switch_ivr.c
@@ -2012,6 +2012,21 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_generate_xml_cdr(switch_core_session_
cp_off += switch_ivr_set_xml_profile_data(x_main_cp, caller_profile, 0);
+ if (caller_profile->origination_caller_profile) {
+ switch_caller_profile_t *cp = NULL;
+ int off = 0;
+ if (!(x_o = switch_xml_add_child_d(x_main_cp, "origination", cp_off++))) {
+ goto error;
+ }
+
+ for (cp = caller_profile->origination_caller_profile; cp; cp = cp->next) {
+ if (!(x_caller_profile = switch_xml_add_child_d(x_o, "origination_caller_profile", off++))) {
+ goto error;
+ }
+ switch_ivr_set_xml_profile_data(x_caller_profile, cp, 0);
+ }
+ }
+
if (caller_profile->originator_caller_profile) {
switch_caller_profile_t *cp = NULL;
int off = 0;
diff --git a/src/switch_loadable_module.c b/src/switch_loadable_module.c
index 0ef3624326..67325648b2 100644
--- a/src/switch_loadable_module.c
+++ b/src/switch_loadable_module.c
@@ -1683,16 +1683,20 @@ SWITCH_DECLARE(switch_status_t) switch_api_execute(const char *cmd, const char *
{
switch_api_interface_t *api;
switch_status_t status;
- char *arg_no_spaces;
- char *cmd_no_spaces;
+ char *arg_used;
+ char *cmd_used;
switch_assert(stream != NULL);
switch_assert(stream->data != NULL);
switch_assert(stream->write_function != NULL);
-
- cmd_no_spaces = switch_strip_whitespace(cmd);
- arg_no_spaces = switch_strip_whitespace(arg);
+ if (strcasecmp(cmd, "console_complete")) {
+ cmd_used = switch_strip_whitespace(cmd);
+ arg_used = switch_strip_whitespace(arg);
+ } else {
+ cmd_used = (char *) cmd;
+ arg_used = (char *) arg;
+ }
if (!stream->param_event) {
@@ -1700,17 +1704,17 @@ SWITCH_DECLARE(switch_status_t) switch_api_execute(const char *cmd, const char *
}
if (stream->param_event) {
- if (cmd_no_spaces) {
- switch_event_add_header_string(stream->param_event, SWITCH_STACK_BOTTOM, "API-Command", cmd_no_spaces);
+ if (cmd_used) {
+ switch_event_add_header_string(stream->param_event, SWITCH_STACK_BOTTOM, "API-Command", cmd_used);
}
- if (arg_no_spaces) {
- switch_event_add_header_string(stream->param_event, SWITCH_STACK_BOTTOM, "API-Command-Argument", arg_no_spaces);
+ if (arg_used) {
+ switch_event_add_header_string(stream->param_event, SWITCH_STACK_BOTTOM, "API-Command-Argument", arg_used);
}
}
- if (cmd_no_spaces && (api = switch_loadable_module_get_api_interface(cmd_no_spaces)) != 0) {
- if ((status = api->function(arg_no_spaces, session, stream)) != SWITCH_STATUS_SUCCESS) {
+ if (cmd_used && (api = switch_loadable_module_get_api_interface(cmd_used)) != 0) {
+ if ((status = api->function(arg_used, session, stream)) != SWITCH_STATUS_SUCCESS) {
stream->write_function(stream, "COMMAND RETURNED ERROR!\n");
}
UNPROTECT_INTERFACE(api);
@@ -1723,8 +1727,13 @@ SWITCH_DECLARE(switch_status_t) switch_api_execute(const char *cmd, const char *
switch_event_fire(&stream->param_event);
}
- switch_safe_free(cmd_no_spaces);
- switch_safe_free(arg_no_spaces);
+ if (cmd_used != cmd) {
+ switch_safe_free(cmd_used);
+ }
+
+ if (arg_used != arg) {
+ switch_safe_free(arg_used);
+ }
return status;
}