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