Merge commit 'fsorig/master'

This commit is contained in:
Moises Silva 2010-09-08 12:12:15 -04:00
commit aa7c9b898a
21 changed files with 616 additions and 422 deletions

View File

@ -10,6 +10,8 @@
<param name="moh-sound" value="$${hold_music}"/> <param name="moh-sound" value="$${hold_music}"/>
<!--<param name="record-template" value="$${base_dir}/recordings/${strftime(%Y-%m-%d-%H-%M-%S)}.${destination_number}.${caller_id_number}.${uuid}.wav"/>--> <!--<param name="record-template" value="$${base_dir}/recordings/${strftime(%Y-%m-%d-%H-%M-%S)}.${destination_number}.${caller_id_number}.${uuid}.wav"/>-->
<param name="time-base-score" value="system"/> <param name="time-base-score" value="system"/>
<param name="max-wait-time" value="0"/>
<param name="max-wait-time-with-no-agent" value="0"/>
<param name="tier-rules-apply" value="false"/> <param name="tier-rules-apply" value="false"/>
<param name="tier-rule-wait-second" value="300"/> <param name="tier-rule-wait-second" value="300"/>
<param name="tier-rule-wait-multiply-level" value="true"/> <param name="tier-rule-wait-multiply-level" value="true"/>

View File

@ -715,15 +715,6 @@
--> -->
<X-PRE-PROCESS cmd="include" data="default/*.xml"/> <X-PRE-PROCESS cmd="include" data="default/*.xml"/>
<!--
WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
Anything you put below this line will usually get ignored due to the file in
default/99999_enum.xml as it will transfer the call to the enum dialplan.
WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
-->
<!-- <!--
<extension name="refer"> <extension name="refer">
<condition field="${sip_refer_to}"> <condition field="${sip_refer_to}">
@ -758,7 +749,12 @@
</extension> </extension>
--> -->
<!-- SEE WARNING ABOVE IF YOU ARE TRYING TO ADD EXTENSIONS HERE! --> <extension name="enum">
<condition field="${module_exists(mod_enum)}" expression="true"/>
<condition field="destination_number" expression="^(.*)$">
<action application="transfer" data="$1 enum"/>
</condition>
</extension>
</context> </context>
</include> </include>

View File

@ -1,8 +0,0 @@
<include>
<extension name="enum">
<condition field="${module_exists(mod_enum)}" expression="true"/>
<condition field="destination_number" expression="^(.*)$">
<action application="transfer" data="$1 enum"/>
</condition>
</extension>
</include>

View File

@ -3896,7 +3896,7 @@ SWITCH_STANDARD_API(ft_function)
ftdm_channel_t *fchan = NULL; ftdm_channel_t *fchan = NULL;
ftdm_span_t *span = NULL; ftdm_span_t *span = NULL;
if (argc < 2) { if (argc < 2) {
stream->write_function(stream, "-ERR Usage: oz notrace <span_id> [<chan_id>]\n"); stream->write_function(stream, "-ERR Usage: ftdm notrace <span_id> [<chan_id>]\n");
goto end; goto end;
} }
ftdm_span_find_by_name(argv[1], &span); ftdm_span_find_by_name(argv[1], &span);

View File

@ -1805,6 +1805,7 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_open(uint32_t span_id, uint32_t chan_id,
status = FTDM_SUCCESS; status = FTDM_SUCCESS;
} }
ftdm_set_flag(check, FTDM_CHANNEL_INUSE); ftdm_set_flag(check, FTDM_CHANNEL_INUSE);
ftdm_set_flag(check, FTDM_CHANNEL_OUTBOUND);
*ftdmchan = check; *ftdmchan = check;
} }

View File

@ -207,6 +207,13 @@ struct t30_state_s
/*! \brief This is only used in full duplex (e.g. ISDN) modes. */ /*! \brief This is only used in full duplex (e.g. ISDN) modes. */
int timer_t8; 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. */ /*! \brief TRUE once the far end FAX entity has been detected. */
int far_end_detected; int far_end_detected;

View File

@ -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. */ \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_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) #if defined(__cplusplus)
} }
#endif #endif

View File

@ -2377,6 +2377,8 @@ static int send_response_to_pps(t30_state_t *s)
} }
/*- End of function --------------------------------------------------------*/ /*- End of function --------------------------------------------------------*/
#define VET_ALL_FCD_FRAMES
static int process_rx_pps(t30_state_t *s, const uint8_t *msg, int len) static int process_rx_pps(t30_state_t *s, const uint8_t *msg, int len)
{ {
int page; 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 frame_no;
int first_bad_frame; int first_bad_frame;
int image_ended; int image_ended;
#if defined(VET_ALL_FCD_FRAMES)
int first;
int expected_len;
#endif
if (len < 7) 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 */ /* Build a bit map of which frames we now have stored OK */
first_bad_frame = 256; first_bad_frame = 256;
#if defined(VET_ALL_FCD_FRAMES)
first = TRUE;
expected_len = 256;
#endif
for (i = 0; i < 32; i++) for (i = 0; i < 32; i++)
{ {
s->ecm_frame_map[i + 3] = 0; s->ecm_frame_map[i + 3] = 0;
for (j = 0; j < 8; j++) for (j = 0; j < 8; j++)
{ {
frame_no = (i << 3) + 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) if (s->ecm_len[frame_no] < 0)
{ {
s->ecm_frame_map[i + 3] |= (1 << j); 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) 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_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) if (s->receiver_not_ready_count > 0)
{ {
s->receiver_not_ready_count--; 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) switch (s->state)
{ {
case T30_STATE_F_DOC_ECM: 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]; frame_no = msg[3];
/* Just store the actual image data, and record its length */ /* 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 */ /* In case we are just after a CTC/CTR exchange, which kicked us back to long training */
s->short_train = TRUE; s->short_train = TRUE;
} }
else
{
unexpected_frame_length(s, msg, len);
}
/* We have received something, so any missing carrier status is out of date */ /* We have received something, so any missing carrier status is out of date */
if (s->current_status == T30_ERR_RX_NOCARRIER) if (s->current_status == T30_ERR_RX_NOCARRIER)
s->current_status = T30_ERR_OK; 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: case T30_STATE_F_POST_DOC_ECM:
/* Just ignore this. It must be an extra RCP. Several are usually sent, to maximise the chance /* Just ignore this. It must be an extra RCP. Several are usually sent, to maximise the chance
of receiving a correct one. */ of receiving a correct one. */
timer_t2_start(s);
break; break;
default: default:
unexpected_non_final_frame(s, msg, len); 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: case T30_DCS:
process_rx_dcs(s, msg, len); process_rx_dcs(s, msg, len);
break; break;
case T30_PRI_MPS:
if (s->remote_interrupts_allowed)
{
}
/* Fall through */
case T30_MPS: case T30_MPS:
/* Treat this as a bad quality page. */ /* Treat this as a bad quality page. */
if (s->phase_d_handler) 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); set_state(s, T30_STATE_III_Q_RTN);
send_simple_frame(s, T30_RTN); send_simple_frame(s, T30_RTN);
break; break;
case T30_PRI_MPS: case T30_PRI_EOM:
/* Treat this as a bad quality page. */ if (s->remote_interrupts_allowed)
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; /* Fall through */
set_state(s, T30_STATE_III_Q_RTN);
break;
case T30_EOM: case T30_EOM:
case T30_EOS: case T30_EOS:
/* Treat this as a bad quality page. */ /* 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); set_state(s, T30_STATE_III_Q_RTN);
send_simple_frame(s, T30_RTN); send_simple_frame(s, T30_RTN);
break; break;
case T30_PRI_EOM: case T30_PRI_EOP:
/* Treat this as a bad quality page. */ if (s->remote_interrupts_allowed)
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 = T30_PRI_EOM; /* Fall through */
set_state(s, T30_STATE_III_Q_RTN);
break;
case T30_EOP: case T30_EOP:
/* Treat this as a bad quality page. */ /* Treat this as a bad quality page. */
if (s->phase_d_handler) 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); set_state(s, T30_STATE_III_Q_RTN);
send_simple_frame(s, T30_RTN); send_simple_frame(s, T30_RTN);
break; 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: case T30_DCN:
s->current_status = T30_ERR_RX_DCNDATA; s->current_status = T30_ERR_RX_DCNDATA;
disconnect(s); 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; fcf = msg[2] & 0xFE;
switch (fcf) switch (fcf)
{ {
case T30_PRI_MPS:
if (s->remote_interrupts_allowed)
{
}
/* Fall through */
case T30_MPS: case T30_MPS:
s->next_rx_step = fcf; s->next_rx_step = fcf;
queue_phase(s, T30_PHASE_D_TX); 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;
} }
break; break;
case T30_PRI_MPS: case T30_PRI_EOM:
s->next_rx_step = fcf; if (s->remote_interrupts_allowed)
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; /* Fall through */
case T30_EOM: case T30_EOM:
case T30_EOS: case T30_EOS:
s->next_rx_step = fcf; 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;
} }
break; break;
case T30_PRI_EOM: case T30_PRI_EOP:
s->next_rx_step = fcf; if (s->remote_interrupts_allowed)
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; /* Fall through */
case T30_EOP: case T30_EOP:
s->next_rx_step = fcf; s->next_rx_step = fcf;
queue_phase(s, T30_PHASE_D_TX); 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;
} }
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: case T30_DCN:
s->current_status = T30_ERR_RX_DCNFAX; s->current_status = T30_ERR_RX_DCNFAX;
disconnect(s); 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: case T4_RCP:
/* Return to control for partial page. These might come through with or without the final frame tag. /* 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. */ Here we deal with the "final frame tag" case. */
if (s->state == T30_STATE_F_DOC_ECM) process_rx_rcp(s, msg, len);
{
/* 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. */
}
break; break;
case T30_EOR: case T30_EOR:
if (len != 4) 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_EOP:
case T30_PRI_EOM: case T30_PRI_EOM:
case T30_PRI_MPS: case T30_PRI_MPS:
/* TODO: Alert operator */ if (s->remote_interrupts_allowed)
{
/* TODO: Alert operator */
}
/* Fall through */ /* Fall through */
case T30_NULL: case T30_NULL:
case T30_EOP: 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; fcf = msg[2] & 0xFE;
switch (fcf) 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: case T30_MCF:
switch (s->next_tx_step) switch (s->next_tx_step)
{ {
case T30_MPS:
case T30_PRI_MPS: case T30_PRI_MPS:
case T30_MPS:
tx_end_page(s); tx_end_page(s);
if (s->phase_d_handler) if (s->phase_d_handler)
s->phase_d_handler(s, s->phase_d_user_data, fcf); 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); set_state(s, T30_STATE_I);
queue_phase(s, T30_PHASE_C_NON_ECM_TX); queue_phase(s, T30_PHASE_C_NON_ECM_TX);
break; break;
case T30_EOM:
case T30_PRI_EOM: case T30_PRI_EOM:
case T30_EOM:
case T30_EOS: case T30_EOS:
tx_end_page(s); tx_end_page(s);
if (s->phase_d_handler) 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); report_tx_result(s, TRUE);
return_to_phase_b(s, FALSE); return_to_phase_b(s, FALSE);
break; break;
case T30_EOP:
case T30_PRI_EOP: case T30_PRI_EOP:
case T30_EOP:
tx_end_page(s); tx_end_page(s);
if (s->phase_d_handler) if (s->phase_d_handler)
s->phase_d_handler(s, s->phase_d_user_data, fcf); 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; break;
case T30_RTP: case T30_RTP:
#if 0
s->rtp_events++; s->rtp_events++;
#endif
switch (s->next_tx_step) switch (s->next_tx_step)
{ {
case T30_MPS:
case T30_PRI_MPS: case T30_PRI_MPS:
case T30_MPS:
tx_end_page(s); tx_end_page(s);
if (s->phase_d_handler) if (s->phase_d_handler)
s->phase_d_handler(s, s->phase_d_user_data, fcf); 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); queue_phase(s, T30_PHASE_B_TX);
restart_sending_document(s); restart_sending_document(s);
break; break;
case T30_EOM:
case T30_PRI_EOM: case T30_PRI_EOM:
case T30_EOM:
case T30_EOS: case T30_EOS:
tx_end_page(s); tx_end_page(s);
if (s->phase_d_handler) 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 */ /* TODO: should go back to T, and resend */
return_to_phase_b(s, TRUE); return_to_phase_b(s, TRUE);
break; break;
case T30_EOP:
case T30_PRI_EOP: case T30_PRI_EOP:
case T30_EOP:
tx_end_page(s); tx_end_page(s);
if (s->phase_d_handler) if (s->phase_d_handler)
s->phase_d_handler(s, s->phase_d_user_data, fcf); 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;
} }
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: case T30_RTN:
#if 0
s->rtn_events++; s->rtn_events++;
#endif
switch (s->next_tx_step) switch (s->next_tx_step)
{ {
case T30_MPS:
case T30_PRI_MPS: case T30_PRI_MPS:
case T30_MPS:
s->retries = 0; s->retries = 0;
if (s->phase_d_handler) if (s->phase_d_handler)
s->phase_d_handler(s, s->phase_d_user_data, fcf); s->phase_d_handler(s, s->phase_d_user_data, fcf);
#if 0
if (!s->retransmit_capable) if (!s->retransmit_capable)
#endif
{ {
/* Send the next page, regardless of the problem with the current one. */ /* Send the next page, regardless of the problem with the current one. */
if (tx_start_page(s)) 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); queue_phase(s, T30_PHASE_B_TX);
restart_sending_document(s); restart_sending_document(s);
break; break;
case T30_EOM:
case T30_PRI_EOM: case T30_PRI_EOM:
case T30_EOM:
case T30_EOS: case T30_EOS:
s->retries = 0; s->retries = 0;
if (s->phase_d_handler) if (s->phase_d_handler)
s->phase_d_handler(s, s->phase_d_user_data, fcf); s->phase_d_handler(s, s->phase_d_user_data, fcf);
#if 0
if (s->retransmit_capable) if (s->retransmit_capable)
{ {
/* Wait for DIS */ /* Wait for DIS */
} }
else else
#endif
{ {
return_to_phase_b(s, TRUE); return_to_phase_b(s, TRUE);
} }
break; break;
case T30_EOP:
case T30_PRI_EOP: case T30_PRI_EOP:
case T30_EOP:
s->retries = 0; s->retries = 0;
if (s->phase_d_handler) if (s->phase_d_handler)
s->phase_d_handler(s, s->phase_d_user_data, fcf); s->phase_d_handler(s, s->phase_d_user_data, fcf);
#if 0
if (s->retransmit_capable) if (s->retransmit_capable)
{ {
/* Send fresh training, and then repeat the last page */ /* 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); restart_sending_document(s);
} }
else else
#endif
{ {
send_dcn(s); send_dcn(s);
} }
break; break;
} }
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: case T30_DCN:
switch (s->next_tx_step) switch (s->next_tx_step)
{ {
case T30_MPS:
case T30_PRI_MPS: case T30_PRI_MPS:
case T30_EOM:
case T30_PRI_EOM: case T30_PRI_EOM:
case T30_MPS:
case T30_EOM:
case T30_EOS: case T30_EOS:
/* Unexpected DCN after EOM, EOS or MPS sequence */ /* Unexpected DCN after EOM, EOS or MPS sequence */
s->current_status = T30_ERR_RX_DCNPHD; 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"); span_log(&s->logging, SPAN_LOG_FLOW, "Moving on to the next page\n");
switch (s->next_tx_step) switch (s->next_tx_step)
{ {
case T30_MPS:
case T30_PRI_MPS: case T30_PRI_MPS:
case T30_MPS:
tx_end_page(s); tx_end_page(s);
if (s->phase_d_handler) if (s->phase_d_handler)
s->phase_d_handler(s, s->phase_d_user_data, fcf); 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); send_first_ecm_frame(s);
} }
break; break;
case T30_EOM:
case T30_PRI_EOM: case T30_PRI_EOM:
case T30_EOM:
case T30_EOS: case T30_EOS:
tx_end_page(s); tx_end_page(s);
if (s->phase_d_handler) 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); report_tx_result(s, TRUE);
return_to_phase_b(s, FALSE); return_to_phase_b(s, FALSE);
break; break;
case T30_EOP:
case T30_PRI_EOP: case T30_PRI_EOP:
case T30_EOP:
tx_end_page(s); tx_end_page(s);
if (s->phase_d_handler) if (s->phase_d_handler)
s->phase_d_handler(s, s->phase_d_user_data, fcf); 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; fcf = msg[2] & 0xFE;
switch (fcf) 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: case T30_MCF:
s->retries = 0; s->retries = 0;
s->timer_t5 = 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"); span_log(&s->logging, SPAN_LOG_FLOW, "Moving on to the next page\n");
switch (s->next_tx_step) switch (s->next_tx_step)
{ {
case T30_MPS:
case T30_PRI_MPS: case T30_PRI_MPS:
case T30_MPS:
tx_end_page(s); tx_end_page(s);
if (s->phase_d_handler) if (s->phase_d_handler)
s->phase_d_handler(s, s->phase_d_user_data, fcf); 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); send_first_ecm_frame(s);
} }
break; break;
case T30_EOM:
case T30_PRI_EOM: case T30_PRI_EOM:
case T30_EOM:
case T30_EOS: case T30_EOS:
tx_end_page(s); tx_end_page(s);
if (s->phase_d_handler) 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); report_tx_result(s, TRUE);
return_to_phase_b(s, FALSE); return_to_phase_b(s, FALSE);
break; break;
case T30_EOP:
case T30_PRI_EOP: case T30_PRI_EOP:
case T30_EOP:
tx_end_page(s); tx_end_page(s);
if (s->phase_d_handler) if (s->phase_d_handler)
s->phase_d_handler(s, s->phase_d_user_data, fcf); 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); set_state(s, T30_STATE_IV_PPS_RNR);
send_rr(s); send_rr(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_PPR: case T30_PPR:
process_rx_ppr(s, msg, len); process_rx_ppr(s, msg, len);
break; 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: case T30_FNV:
process_rx_fnv(s, msg, len); process_rx_fnv(s, msg, 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 */
default: default:
/* We don't know what to do with this. */ /* We don't know what to do with this. */
unexpected_final_frame(s, msg, len); 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; fcf = msg[2] & 0xFE;
switch (fcf) 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: case T30_MCF:
s->retries = 0; s->retries = 0;
s->timer_t5 = 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"); span_log(&s->logging, SPAN_LOG_FLOW, "Moving on to the next page\n");
switch (s->next_tx_step) switch (s->next_tx_step)
{ {
case T30_MPS:
case T30_PRI_MPS: case T30_PRI_MPS:
case T30_MPS:
tx_end_page(s); tx_end_page(s);
if (s->phase_d_handler) if (s->phase_d_handler)
s->phase_d_handler(s, s->phase_d_user_data, fcf); 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); send_first_ecm_frame(s);
} }
break; break;
case T30_EOM:
case T30_PRI_EOM: case T30_PRI_EOM:
case T30_EOM:
case T30_EOS: case T30_EOS:
tx_end_page(s); tx_end_page(s);
if (s->phase_d_handler) 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); report_tx_result(s, TRUE);
return_to_phase_b(s, FALSE); return_to_phase_b(s, FALSE);
break; break;
case T30_EOP:
case T30_PRI_EOP: case T30_PRI_EOP:
case T30_EOP:
tx_end_page(s); tx_end_page(s);
if (s->phase_d_handler) if (s->phase_d_handler)
s->phase_d_handler(s, s->phase_d_user_data, fcf); 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); set_state(s, T30_STATE_IV_PPS_RNR);
send_rr(s); send_rr(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: case T30_DCN:
s->current_status = T30_ERR_RX_DCNRRD; s->current_status = T30_ERR_RX_DCNRRD;
disconnect(s); 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: case T30_FNV:
process_rx_fnv(s, msg, len); process_rx_fnv(s, msg, 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 */
default: default:
/* We don't know what to do with this. */ /* We don't know what to do with this. */
unexpected_final_frame(s, msg, len); 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); set_state(s, T30_STATE_IV_EOR_RNR);
send_rr(s); send_rr(s);
break; 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: case T30_ERR:
/* TODO: Continue with the next message if MPS or EOM? */ /* TODO: Continue with the next message if MPS or EOM? */
s->timer_t5 = 0; 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: case T30_FNV:
process_rx_fnv(s, msg, len); process_rx_fnv(s, msg, 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 */
default: default:
/* We don't know what to do with this. */ /* We don't know what to do with this. */
unexpected_final_frame(s, msg, len); 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); set_state(s, T30_STATE_IV_EOR_RNR);
send_rr(s); send_rr(s);
break; 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: case T30_ERR:
/* TODO: Continue with the next message if MPS or EOM? */ /* TODO: Continue with the next message if MPS or EOM? */
s->timer_t5 = 0; 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: case T30_FNV:
process_rx_fnv(s, msg, len); process_rx_fnv(s, msg, 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 */
default: default:
/* We don't know what to do with this. */ /* We don't know what to do with this. */
unexpected_final_frame(s, msg, len); 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 /* The following handles context sensitive message types, which should
occur at the end of message sequences. They should, therefore have occur at the end of message sequences. They should, therefore have
the final frame flag set. */ 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) switch (s->state)
{ {
@ -5082,11 +5016,11 @@ static void timer_t2_expired(t30_state_t *s)
case T30_STATE_F_POST_RCP_MCF: case T30_STATE_F_POST_RCP_MCF:
switch (s->next_rx_step) switch (s->next_rx_step)
{ {
case T30_EOM:
case T30_PRI_EOM: case T30_PRI_EOM:
case T30_EOM:
case T30_EOS: case T30_EOS:
/* We didn't receive a response to our T30_MCF after T30_EOM, so we must be OK /* 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)); 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); set_phase(s, T30_PHASE_B_TX);
timer_t2_start(s); 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_ECM:
case T30_STATE_F_POST_DOC_NON_ECM: case T30_STATE_F_POST_DOC_NON_ECM:
/* While waiting for next FAX page */ /* 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; s->current_status = T30_ERR_RX_T2EXPMPS;
break; break;
#if 0 #if 0
@ -5873,8 +5809,8 @@ SPAN_DECLARE(void) t30_front_end_status(void *user_data, int status)
{ {
switch (s->next_rx_step) switch (s->next_rx_step)
{ {
case T30_MPS:
case T30_PRI_MPS: case T30_PRI_MPS:
case T30_MPS:
/* We should now start to get another page */ /* We should now start to get another page */
if (s->error_correcting_mode) 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); timer_t2_start(s);
break; break;
case T30_EOM:
case T30_PRI_EOM: case T30_PRI_EOM:
case T30_EOM:
case T30_EOS: case T30_EOS:
/* See if we get something back, before moving to phase B. */ /* See if we get something back, before moving to phase B. */
timer_t2_start(s); timer_t2_start(s);
set_phase(s, T30_PHASE_D_RX); set_phase(s, T30_PHASE_D_RX);
break; break;
case T30_EOP:
case T30_PRI_EOP: case T30_PRI_EOP:
case T30_EOP:
/* Wait for a DCN. */ /* Wait for a DCN. */
set_phase(s, T30_PHASE_D_RX); set_phase(s, T30_PHASE_D_RX);
timer_t4_start(s); timer_t4_start(s);
@ -6256,6 +6192,12 @@ SPAN_DECLARE(void) t30_local_interrupt_request(t30_state_t *s, int state)
} }
/*- End of function --------------------------------------------------------*/ /*- 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) SPAN_DECLARE(int) t30_restart(t30_state_t *s)
{ {
s->phase = T30_PHASE_IDLE; 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 */ /* The page number is only reset at call establishment */
s->rx_page_number = 0; s->rx_page_number = 0;
s->tx_page_number = 0; s->tx_page_number = 0;
#if 0
s->rtn_events = 0; s->rtn_events = 0;
s->rtp_events = 0; s->rtp_events = 0;
#endif s->local_interrupt_pending = FALSE;
s->far_end_detected = FALSE; s->far_end_detected = FALSE;
s->timer_t0_t1 = ms_to_samples(DEFAULT_TIMER_T0); s->timer_t0_t1 = ms_to_samples(DEFAULT_TIMER_T0);
if (s->calling_party) if (s->calling_party)

View File

@ -2602,7 +2602,9 @@ SPAN_DECLARE(t31_state_t *) t31_init(t31_state_t *s,
t38_tx_packet_handler_t *tx_t38_packet_handler, t38_tx_packet_handler_t *tx_t38_packet_handler,
void *tx_t38_packet_user_data) void *tx_t38_packet_user_data)
{ {
#if 0
v8_parms_t v8_parms; v8_parms_t v8_parms;
#endif
int alloced; int alloced;
if (at_tx_handler == NULL || modem_control_handler == NULL) if (at_tx_handler == NULL || modem_control_handler == NULL)

View File

@ -426,7 +426,6 @@ static int set_next_tx_type(t38_gateway_state_t *s)
t38_gateway_hdlc_state_t *u; t38_gateway_hdlc_state_t *u;
t = &s->audio.modems; 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); t38_non_ecm_buffer_report_output_status(&s->core.non_ecm_to_modem, &s->logging);
if (t->next_tx_handler) if (t->next_tx_handler)
{ {
@ -447,6 +446,7 @@ static int set_next_tx_type(t38_gateway_state_t *s)
return TRUE; return TRUE;
} }
/*endif*/ /*endif*/
u = &s->core.hdlc_to_modem;
if (u->in == u->out) if (u->in == u->out)
return FALSE; return FALSE;
/*endif*/ /*endif*/
@ -1057,6 +1057,8 @@ 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) static int process_rx_indicator(t38_core_state_t *t, void *user_data, int indicator)
{ {
t38_gateway_state_t *s; t38_gateway_state_t *s;
t38_gateway_hdlc_state_t *u;
int immediate;
s = (t38_gateway_state_t *) user_data; s = (t38_gateway_state_t *) user_data;
@ -1067,25 +1069,50 @@ static int process_rx_indicator(t38_core_state_t *t, void *user_data, int indica
return 0; return 0;
} }
/*endif*/ /*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) if (++u->in >= T38_TX_HDLC_BUFS)
s->core.hdlc_to_modem.in = 0; u->in = 0;
/*endif*/ /*endif*/
} }
/*endif*/ /*endif*/
s->core.hdlc_to_modem.buf[s->core.hdlc_to_modem.in].contents = (indicator | FLAG_INDICATOR); u->buf[u->in].contents = (indicator | FLAG_INDICATOR);
if (++s->core.hdlc_to_modem.in >= T38_TX_HDLC_BUFS) if (++u->in >= T38_TX_HDLC_BUFS)
s->core.hdlc_to_modem.in = 0; u->in = 0;
/*endif*/ /*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, if (immediate)
SPAN_LOG_FLOW, {
"Queued change - (%d) %s -> %s\n", span_log(&s->logging,
silence_gen_remainder(&(s->audio.modems.silence_gen)), SPAN_LOG_FLOW,
t38_indicator_to_str(t->current_rx_indicator), "Changing - (%d) %s -> %s\n",
t38_indicator_to_str(indicator)); 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; 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 /* We need to set this here, since we might have been called as a fake
indication when the real one was missing */ 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; xx->corrupt_current_frame[0] = FALSE;
break; break;
case T38_FIELD_T4_NON_ECM_DATA: 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; xx->current_rx_field_class = T38_FIELD_CLASS_NON_ECM;
hdlc_buf = &s->core.hdlc_to_modem.buf[s->core.hdlc_to_modem.in]; hdlc_buf = &s->core.hdlc_to_modem.buf[s->core.hdlc_to_modem.in];
if (hdlc_buf->contents != (data_type | FLAG_DATA)) 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; xx->corrupt_current_frame[0] = FALSE;
break; break;
case T38_FIELD_T4_NON_ECM_SIG_END: 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]; 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 /* 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 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.to_t38.octets_per_data_packet = 1;
s->core.ecm_allowed = TRUE; 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; //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); restart_rx_modem(s);
s->core.timed_mode = TIMED_MODE_STARTUP; s->core.timed_mode = TIMED_MODE_STARTUP;
s->core.samples_to_timeout = 1; s->core.samples_to_timeout = 1;

View File

@ -111,7 +111,7 @@ static const struct dtmf_to_ascii_s dtmf_to_ascii[] =
{"##8", 'W'}, {"##8", 'W'},
{"##9", 'Z'}, {"##9", 'Z'},
{"##0", ' '}, {"##0", ' '},
#if defined(WIN32) || ( defined(__SVR4) && defined (__sun)) #if defined(WIN32) || ( defined(__SVR4) && defined (__sun))
{"#*1", 'X'}, // (Note 1) 111 1011 {"#*1", 'X'}, // (Note 1) 111 1011
{"#*2", 'X'}, // (Note 1) 111 1100 {"#*2", 'X'}, // (Note 1) 111 1100
{"#*3", 'X'}, // (Note 1) 111 1101 {"#*3", 'X'}, // (Note 1) 111 1101

View File

@ -103,6 +103,7 @@ SWITCH_BEGIN_EXTERN_C
switch_caller_profile_flag_t flags; switch_caller_profile_flag_t flags;
struct switch_caller_profile *originator_caller_profile; struct switch_caller_profile *originator_caller_profile;
struct switch_caller_profile *originatee_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_caller_profile *hunt_caller_profile;
struct switch_channel_timetable *times; struct switch_channel_timetable *times;
struct switch_caller_extension *caller_extension; struct switch_caller_extension *caller_extension;

View File

@ -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); 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 \brief Retrive the given channel's unique id

View File

@ -37,6 +37,7 @@
#define CC_AGENT_TYPE_UUID_STANDBY "uuid-standby" #define CC_AGENT_TYPE_UUID_STANDBY "uuid-standby"
#define CC_SQLITE_DB_NAME "callcenter" #define CC_SQLITE_DB_NAME "callcenter"
#define CC_MAX_TIME_DIFF_CHECK 5
/* TODO /* TODO
drop caller if no agent login drop caller if no agent login
dont allow new caller 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"; 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 { typedef enum {
CC_STATUS_SUCCESS, CC_STATUS_SUCCESS,
CC_STATUS_FALSE, CC_STATUS_FALSE,
@ -77,12 +88,7 @@ typedef enum {
CC_TIER_STATE_STANDBY = 5 CC_TIER_STATE_STANDBY = 5
} cc_tier_state_t; } cc_tier_state_t;
struct cc_tier_state_table { static struct cc_state_table STATE_CHART[] = {
const char *name;
cc_tier_state_t state;
};
static struct cc_tier_state_table STATE_CHART[] = {
{"Unknown", CC_TIER_STATE_UNKNOWN}, {"Unknown", CC_TIER_STATE_UNKNOWN},
{"No Answer", CC_TIER_STATE_NO_ANSWER}, {"No Answer", CC_TIER_STATE_NO_ANSWER},
{"Ready", CC_TIER_STATE_READY}, {"Ready", CC_TIER_STATE_READY},
@ -101,12 +107,7 @@ typedef enum {
CC_AGENT_STATUS_ON_BREAK = 4 CC_AGENT_STATUS_ON_BREAK = 4
} cc_agent_status_t; } cc_agent_status_t;
struct cc_agent_status_table { static struct cc_status_table AGENT_STATUS_CHART[] = {
const char *name;
cc_agent_status_t status;
};
static struct cc_agent_status_table AGENT_STATUS_CHART[] = {
{"Unknown", CC_AGENT_STATUS_UNKNOWN}, {"Unknown", CC_AGENT_STATUS_UNKNOWN},
{"Logged Out", CC_AGENT_STATUS_LOGGED_OUT}, {"Logged Out", CC_AGENT_STATUS_LOGGED_OUT},
{"Available", CC_AGENT_STATUS_AVAILABLE}, {"Available", CC_AGENT_STATUS_AVAILABLE},
@ -124,12 +125,7 @@ typedef enum {
CC_AGENT_STATE_IDLE = 4 CC_AGENT_STATE_IDLE = 4
} cc_agent_state_t; } cc_agent_state_t;
struct cc_agent_state_table { static struct cc_state_table AGENT_STATE_CHART[] = {
const char *name;
cc_agent_state_t state;
};
static struct cc_agent_state_table AGENT_STATE_CHART[] = {
{"Unknown", CC_AGENT_STATE_UNKNOWN}, {"Unknown", CC_AGENT_STATE_UNKNOWN},
{"Waiting", CC_AGENT_STATE_WAITING}, {"Waiting", CC_AGENT_STATE_WAITING},
{"Receiving", CC_AGENT_STATE_RECEIVING}, {"Receiving", CC_AGENT_STATE_RECEIVING},
@ -147,12 +143,7 @@ typedef enum {
CC_MEMBER_STATE_ABANDONED = 4 CC_MEMBER_STATE_ABANDONED = 4
} cc_member_state_t; } cc_member_state_t;
struct cc_member_state_table { static struct cc_state_table MEMBER_STATE_CHART[] = {
const char *name;
cc_member_state_t state;
};
static struct cc_member_state_table MEMBER_STATE_CHART[] = {
{"Unknown", CC_MEMBER_STATE_UNKNOWN}, {"Unknown", CC_MEMBER_STATE_UNKNOWN},
{"Waiting", CC_MEMBER_STATE_WAITING}, {"Waiting", CC_MEMBER_STATE_WAITING},
{"Trying", CC_MEMBER_STATE_TRYING}, {"Trying", CC_MEMBER_STATE_TRYING},
@ -162,10 +153,24 @@ static struct cc_member_state_table MEMBER_STATE_CHART[] = {
}; };
/*static char queues_sql[] = struct cc_member_cancel_reason_table {
"CREATE TABLE queues (\n" const char *name;
" name VARCHAR(255)\n" ");\n"; 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[] = static char members_sql[] =
"CREATE TABLE members (\n" "CREATE TABLE members (\n"
" queue VARCHAR(255),\n" " queue VARCHAR(255),\n"
@ -222,7 +227,6 @@ static char agents_sql[] =
" ready_time INTEGER NOT NULL DEFAULT 0\n" " ready_time INTEGER NOT NULL DEFAULT 0\n"
");\n"; ");\n";
static char tiers_sql[] = static char tiers_sql[] =
"CREATE TABLE tiers (\n" "CREATE TABLE tiers (\n"
" queue VARCHAR(255),\n" " queue VARCHAR(255),\n"
@ -247,7 +251,7 @@ const char * cc_tier_state2str(cc_tier_state_t state)
uint8_t x; uint8_t x;
const char *str = "Unknown"; 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) { if (STATE_CHART[x].state == state) {
str = STATE_CHART[x].name; str = STATE_CHART[x].name;
break; break;
@ -262,7 +266,7 @@ cc_tier_state_t cc_tier_str2state(const char *str)
uint8_t x; uint8_t x;
cc_tier_state_t state = CC_TIER_STATE_UNKNOWN; 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)) { if (!strcasecmp(STATE_CHART[x].name, str)) {
state = STATE_CHART[x].state; state = STATE_CHART[x].state;
break; break;
@ -271,12 +275,41 @@ cc_tier_state_t cc_tier_str2state(const char *str)
return state; 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) const char * cc_agent_status2str(cc_agent_status_t status)
{ {
uint8_t x; uint8_t x;
const char *str = "Unknown"; 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) { if (AGENT_STATUS_CHART[x].status == status) {
str = AGENT_STATUS_CHART[x].name; str = AGENT_STATUS_CHART[x].name;
break; break;
@ -291,7 +324,7 @@ cc_agent_status_t cc_agent_str2status(const char *str)
uint8_t x; uint8_t x;
cc_agent_status_t status = CC_AGENT_STATUS_UNKNOWN; 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)) { if (!strcasecmp(AGENT_STATUS_CHART[x].name, str)) {
status = AGENT_STATUS_CHART[x].status; status = AGENT_STATUS_CHART[x].status;
break; break;
@ -305,7 +338,7 @@ const char * cc_agent_state2str(cc_agent_state_t state)
uint8_t x; uint8_t x;
const char *str = "Unknown"; 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) { if (AGENT_STATE_CHART[x].state == state) {
str = AGENT_STATE_CHART[x].name; str = AGENT_STATE_CHART[x].name;
break; break;
@ -320,7 +353,7 @@ cc_agent_state_t cc_agent_str2state(const char *str)
uint8_t x; uint8_t x;
cc_agent_state_t state = CC_AGENT_STATE_UNKNOWN; 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)) { if (!strcasecmp(AGENT_STATE_CHART[x].name, str)) {
state = AGENT_STATE_CHART[x].state; state = AGENT_STATE_CHART[x].state;
break; break;
@ -334,7 +367,7 @@ const char * cc_member_state2str(cc_member_state_t state)
uint8_t x; uint8_t x;
const char *str = "Unknown"; 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) { if (MEMBER_STATE_CHART[x].state == state) {
str = MEMBER_STATE_CHART[x].name; str = MEMBER_STATE_CHART[x].name;
break; break;
@ -349,7 +382,7 @@ cc_member_state_t cc_member_str2state(const char *str)
uint8_t x; uint8_t x;
cc_member_state_t state = CC_MEMBER_STATE_UNKNOWN; 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)) { if (!strcasecmp(MEMBER_STATE_CHART[x].name, str)) {
state = MEMBER_STATE_CHART[x].state; state = MEMBER_STATE_CHART[x].state;
break; break;
@ -384,19 +417,27 @@ struct cc_queue {
char *moh; char *moh;
char *record_template; char *record_template;
char *time_base_score; char *time_base_score;
switch_bool_t tier_rules_apply; switch_bool_t tier_rules_apply;
uint32_t tier_rule_wait_second; uint32_t tier_rule_wait_second;
switch_bool_t tier_rule_wait_multiply_level; switch_bool_t tier_rule_wait_multiply_level;
switch_bool_t tier_rule_no_agent_no_wait; switch_bool_t tier_rule_no_agent_no_wait;
uint32_t discard_abandoned_after; uint32_t discard_abandoned_after;
switch_bool_t abandoned_resume_allowed; switch_bool_t abandoned_resume_allowed;
uint32_t max_wait_time;
uint32_t max_wait_time_with_no_agent;
switch_mutex_t *mutex; switch_mutex_t *mutex;
switch_thread_rwlock_t *rwlock; switch_thread_rwlock_t *rwlock;
switch_memory_pool_t *pool; switch_memory_pool_t *pool;
uint32_t flags; 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_item_t config[CC_QUEUE_CONFIGITEM_COUNT];
switch_xml_config_string_options_t config_str_pool; 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++], "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++], "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++], "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-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-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); 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++], "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++], "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); switch_assert(i < CC_QUEUE_CONFIGITEM_COUNT);
return queue; return queue;
@ -646,6 +691,9 @@ static cc_queue_t *load_queue(const char *queue_name)
switch_thread_rwlock_create(&queue->rwlock, pool); switch_thread_rwlock_create(&queue->rwlock, pool);
queue->name = switch_core_strdup(pool, queue_name); 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())) { if (!(dbh = cc_get_db_handle())) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Cannot open DB!\n"); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Cannot open DB!\n");
goto end; goto end;
@ -702,7 +750,7 @@ static cc_queue_t *get_queue(const char *queue_name)
struct call_helper { struct call_helper {
const char *member_uuid; const char *member_uuid;
const char *queue; const char *queue_name;
const char *queue_strategy; const char *queue_strategy;
const char *member_joined_epoch; const char *member_joined_epoch;
const char *member_caller_name; const char *member_caller_name;
@ -744,7 +792,7 @@ int cc_queue_count(const char *queue)
count = atoi(res); count = atoi(res);
if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CALLCENTER_EVENT) == SWITCH_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", 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-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-Count", res);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Selection", event_name); 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; result = CC_STATUS_SUCCESS;
if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CALLCENTER_EVENT) == SWITCH_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-Action", "agent-status-get");
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Agent-Status", res); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Agent-Status", res);
switch_event_fire(&event); 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; result = CC_STATUS_SUCCESS;
if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CALLCENTER_EVENT) == SWITCH_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-Action", "agent-status-change");
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Agent-Status", value); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Agent-Status", value);
switch_event_fire(&event); 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; result = CC_STATUS_SUCCESS;
if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CALLCENTER_EVENT) == SWITCH_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-Action", "agent-state-change");
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Agent-State", value); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Agent-State", value);
switch_event_fire(&event); switch_event_fire(&event);
@ -1118,13 +1166,13 @@ done:
return result; 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; cc_status_t result = CC_STATUS_SUCCESS;
char *sql; char *sql;
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Deleted tier Agent %s in Queue %s\n", agent, queue); 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, agent); sql = switch_mprintf("DELETE FROM tiers WHERE queue = '%q' AND agent = '%q';", queue_name, agent);
cc_execute_sql(NULL, sql, NULL); cc_execute_sql(NULL, sql, NULL);
switch_safe_free(sql); 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)) { if (!strcasecmp(h->agent_type, CC_AGENT_TYPE_CALLBACK)) {
switch_event_create(&ovars, SWITCH_EVENT_REQUEST_PARAMS); 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_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_member_pre_answer_uuid", "%s", h->member_uuid);
switch_event_add_header(ovars, SWITCH_STACK_BOTTOM, "cc_agent", "%s", h->agent_name); 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; switch_event_t *event;
const char *cc_warning_tone = switch_channel_get_variable(agent_channel, "cc_warning_tone"); 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", h->agent_name);
switch_channel_set_variable(agent_channel, "cc_agent_type", h->agent_type); switch_channel_set_variable(agent_channel, "cc_agent_type", h->agent_type);
switch_channel_set_variable(agent_channel, "cc_member_uuid", h->member_uuid); 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) { if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CALLCENTER_EVENT) == SWITCH_STATUS_SUCCESS) {
switch_channel_event_set_data(agent_channel, event); 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-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", h->agent_name);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Agent-System", h->agent_system); 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); switch_safe_free(sql);
/* Change the agents Status in the tiers */ /* Change the agents Status in the tiers */
sql = switch_mprintf("UPDATE tiers SET state = '%q' WHERE agent = '%q' AND queue = '%q'", cc_tier_update("state", cc_tier_state2str(CC_TIER_STATE_ACTIVE_INBOUND), h->queue_name, h->agent_name);
cc_tier_state2str(CC_TIER_STATE_ACTIVE_INBOUND), h->agent_name, h->queue);
cc_execute_sql(NULL, sql, NULL);
switch_safe_free(sql);
cc_agent_update("state", cc_agent_state2str(CC_AGENT_STATE_IN_A_QUEUE_CALL), 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 */ /* 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", 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)":"")); 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)); 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 */ /* 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) { while(switch_channel_up(member_channel) && switch_channel_up(agent_channel) && globals.running) {
switch_yield(100000); 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) { if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CALLCENTER_EVENT) == SWITCH_STATUS_SUCCESS) {
switch_channel_event_set_data(agent_channel, event); 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-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", h->agent_name);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Agent-System", h->agent_system); 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 */ /* Update Agents Items */
/* Do not remove uuid of the agent if we are a standby agent */ /* 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';" 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); cc_execute_sql(NULL, sql, NULL);
switch_safe_free(sql); switch_safe_free(sql);
@ -1464,7 +1511,7 @@ static void *SWITCH_THREAD_FUNC outbound_agent_thread_run(switch_thread_t *threa
/* Caller off event */ /* Caller off event */
if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CALLCENTER_EVENT) == SWITCH_STATUS_SUCCESS) { if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CALLCENTER_EVENT) == SWITCH_STATUS_SUCCESS) {
switch_channel_event_set_data(member_channel, event); 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-Action", "member-queue-end");
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Cause", "Terminated"); 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)); 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-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(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-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-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_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); switch_event_fire(&event);
} }
@ -1538,8 +1587,8 @@ done:
sql = switch_mprintf( 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 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'" "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(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, cc_tier_state2str(CC_TIER_STATE_STANDBY)); 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); cc_execute_sql(NULL, sql, NULL);
switch_safe_free(sql); switch_safe_free(sql);
@ -1567,7 +1616,7 @@ done:
} }
struct agent_callback { struct agent_callback {
const char *queue; const char *queue_name;
const char *system; const char *system;
const char *uuid; const char *uuid;
const char *caller_number; const char *caller_number;
@ -1579,6 +1628,7 @@ struct agent_callback {
uint32_t tier_rule_wait_second; uint32_t tier_rule_wait_second;
switch_bool_t tier_rule_wait_multiply_level; switch_bool_t tier_rule_wait_multiply_level;
switch_bool_t tier_rule_no_agent_no_wait; switch_bool_t tier_rule_no_agent_no_wait;
switch_bool_t agent_found;
int tier; int tier;
int tier_agent_available; 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; 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 */ /* 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) { 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_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_name = switch_core_strdup(h->pool, cbt->caller_name);
h->member_caller_number = switch_core_strdup(h->pool, cbt->caller_number); 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->record_template = switch_core_strdup(h->pool, cbt->record_template);
h->no_answer_count = atoi(argv[4]); h->no_answer_count = atoi(argv[4]);
h->max_no_answer = atoi(argv[5]); 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( sql = switch_mprintf(
"UPDATE tiers SET state = '%q' WHERE agent = '%q' AND queue = '%q';" "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';", "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_OFFERING), h->agent_name, h->queue_name,
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_STANDBY), h->agent_name, h->queue_name, cc_tier_state2str(CC_TIER_STATE_READY));
cc_execute_sql(NULL, sql, NULL); cc_execute_sql(NULL, sql, NULL);
switch_safe_free(sql); 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_number = argv[2];
cbt.caller_name = argv[3]; cbt.caller_name = argv[3];
cbt.joined_epoch = argv[4]; cbt.joined_epoch = argv[4];
cbt.queue = argv[0]; cbt.queue_name = argv[0];
cbt.strategy = queue_strategy; cbt.strategy = queue_strategy;
cbt.record_template = queue_record_template; cbt.record_template = queue_record_template;
cbt.agent_found = SWITCH_FALSE;
if (!strcasecmp(queue->strategy, "longest-idle-agent")) { if (!strcasecmp(queue->strategy, "longest-idle-agent")) {
sql_order_by = switch_mprintf("level, agents.last_offered_call, position"); 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);
switch_safe_free(sql_order_by); 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: end:
switch_safe_free(queue_name); switch_safe_free(queue_name);
switch_safe_free(queue_strategy); 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); switch_thread_create(&thread, thd_attr, cc_agent_dispatch_thread_run, NULL, globals.pool);
} }
struct member_helper { struct member_thread_helper {
const char *uuid; const char *member_uuid;
const char *queue_name;
switch_time_t t_member_called;
cc_member_cancel_reason_t member_cancel_reason;
int running; int running;
switch_memory_pool_t *pool; switch_memory_pool_t *pool;
}; };
void *SWITCH_THREAD_FUNC cc_member_thread_run(switch_thread_t *thread, void *obj) void *SWITCH_THREAD_FUNC cc_member_thread_run(switch_thread_t *thread, void *obj)
{ {
struct member_helper *m = (struct member_helper *) obj; struct member_thread_helper *m = (struct member_thread_helper *) obj;
switch_core_session_t *member_session = switch_core_session_locate(m->uuid); switch_core_session_t *member_session = switch_core_session_locate(m->member_uuid);
switch_channel_t *channel = NULL; switch_channel_t *member_channel = NULL;
switch_mutex_lock(globals.mutex); switch_mutex_lock(globals.mutex);
globals.threads++; globals.threads++;
switch_mutex_unlock(globals.mutex); switch_mutex_unlock(globals.mutex);
if (member_session) { if (member_session) {
channel = switch_core_session_get_channel(member_session); member_channel = switch_core_session_get_channel(member_session);
} else { } else {
switch_core_destroy_memory_pool(&m->pool); switch_core_destroy_memory_pool(&m->pool);
return NULL; 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 */ /* TODO Go thought the list of phrases */
/* SAMPLE CODE to playback something over the MOH /* 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 */ /* If Agent Logoff, we might need to recalculare score based on skill */
/* Play Announcement in order */ /* Play Announcement in order */
switch_yield(100000);
queue_rwunlock(queue);
switch_yield(500000);
} }
switch_core_session_rwunlock(member_session); switch_core_session_rwunlock(member_session);
switch_core_destroy_memory_pool(&m->pool); switch_core_destroy_memory_pool(&m->pool);
@ -1971,26 +2063,27 @@ SWITCH_STANDARD_APP(callcenter_function)
char *mydata = NULL; char *mydata = NULL;
cc_queue_t *queue = NULL; cc_queue_t *queue = NULL;
const char *queue_name = 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 *sql = NULL;
char *uuid = switch_core_session_get_uuid(session); char *member_uuid = switch_core_session_get_uuid(member_session);
switch_input_args_t args = { 0 }; struct member_thread_helper *h = NULL;
struct member_helper *h = NULL;
switch_thread_t *thread; switch_thread_t *thread;
switch_threadattr_t *thd_attr = NULL; switch_threadattr_t *thd_attr = NULL;
switch_memory_pool_t *pool; switch_memory_pool_t *pool;
int cc_base_score_int = 0;
switch_channel_timetable_t *times = NULL; 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_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; const char *cur_moh = NULL;
char start_epoch[64]; char start_epoch[64];
switch_event_t *event; switch_event_t *event;
switch_time_t t_member_called = switch_epoch_time_now(NULL); switch_time_t t_member_called = switch_epoch_time_now(NULL);
long abandoned_epoch = 0; long abandoned_epoch = 0;
const char *agent_uuid = NULL;
if (!zstr(data)) { 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]))); argc = switch_separate_string(mydata, ' ', argv, (sizeof(argv) / sizeof(argv[0])));
} else { } else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "No Queue name provided\n"); 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); times = switch_channel_get_timetable(member_channel);
switch_snprintf(start_epoch, sizeof(start_epoch), "%" SWITCH_TIME_T_FMT, times->answered / 1000000); 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) { if (queue->abandoned_resume_allowed == SWITCH_TRUE) {
char res[256]; 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) { if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CALLCENTER_EVENT) == SWITCH_STATUS_SUCCESS) {
switch_channel_event_set_data(member_channel, event); 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(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-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-Number", switch_str_nil(switch_channel_get_variable(member_channel, "caller_id_number")));
switch_event_fire(&event); 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)" " (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')", " VALUES('%q','single_box','%q','%q','%ld','%d','%d','%q','%q','%q','','%q')",
queue_name, queue_name,
uuid, member_uuid,
start_epoch, start_epoch,
(long) switch_epoch_time_now(NULL), (long) switch_epoch_time_now(NULL),
cc_base_score_int, cc_base_score_int,
@ -2069,12 +2162,12 @@ SWITCH_STANDARD_APP(callcenter_function)
char res[256]; char res[256];
/* Update abandoned member */ /* 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'", 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); cc_execute_sql(queue, sql, NULL);
switch_safe_free(sql); switch_safe_free(sql);
/* Confirm we took that member in */ /* 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)); cc_execute_sql2str(NULL, NULL, sql, res, sizeof(res));
switch_safe_free(sql); switch_safe_free(sql);
@ -2092,9 +2185,14 @@ SWITCH_STANDARD_APP(callcenter_function)
/* Start Thread that will playback different prompt to the channel */ /* Start Thread that will playback different prompt to the channel */
switch_core_new_memory_pool(&pool); switch_core_new_memory_pool(&pool);
h = switch_core_alloc(pool, sizeof(*h)); h = switch_core_alloc(pool, sizeof(*h));
h->pool = pool; 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; h->running = 1;
switch_threadattr_create(&thd_attr, h->pool); switch_threadattr_create(&thd_attr, h->pool);
switch_threadattr_detach_set(thd_attr, 1); switch_threadattr_detach_set(thd_attr, 1);
switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE); 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 DTMF callback support */
/* TODO add MOH infitite loop */ /* TODO add MOH infitite loop */
if (cc_moh_override) { 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 { } else {
cur_moh = switch_core_session_strdup(session, queue->moh); cur_moh = switch_core_session_strdup(member_session, queue->moh);
} }
queue_rwunlock(queue); queue_rwunlock(queue);
if (cur_moh) { while (switch_channel_ready(member_channel)) {
switch_ivr_play_file(session, NULL, cur_moh, &args); switch_input_args_t args = { 0 };
} else {
switch_ivr_collect_digits_callback(session, &args, 0, 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 */ /* Stop Member Thread */
@ -2123,33 +2242,42 @@ SWITCH_STANDARD_APP(callcenter_function)
/* Hangup any agents been callback */ /* 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 */ 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'", 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); cc_execute_sql(NULL, sql, NULL);
switch_safe_free(sql); 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) { if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CALLCENTER_EVENT) == SWITCH_STATUS_SUCCESS) {
switch_channel_event_set_data(member_channel, event); 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_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(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_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Member \"%s\" <%s> exit queue %s due to %s\n",
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Caller-UUID", uuid); 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-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-Number", switch_str_nil(switch_channel_get_variable(member_channel, "caller_id_number")));
switch_event_fire(&event); switch_event_fire(&event);
} }
/* for xml_cdr needs */ /* 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_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 */ /* Send Event with queue count */
cc_queue_count(queue_name); cc_queue_count(queue_name);
} else { } 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'", 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); cc_execute_sql(NULL, sql, NULL);
switch_safe_free(sql); switch_safe_free(sql);
@ -2525,7 +2653,7 @@ SWITCH_STANDARD_API(cc_config_api_function)
stream->write_function(stream, "%d\n", atoi(res)); stream->write_function(stream, "%d\n", atoi(res));
if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CALLCENTER_EVENT) == SWITCH_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", 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-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-Count", res);
switch_event_fire(&event); switch_event_fire(&event);

View File

@ -697,7 +697,11 @@ static switch_status_t sofia_answer_channel(switch_core_session_t *session)
if (!sofia_test_flag(tech_pvt, TFLAG_BYE)) { if (!sofia_test_flag(tech_pvt, TFLAG_BYE)) {
char *extra_headers = sofia_glue_get_extra_headers(channel, SOFIA_SIP_RESPONSE_HEADER_PREFIX); 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)) { 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 /* 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, nua_update(tech_pvt->nh,
TAG_IF(!zstr_buf(message), SIPTAG_HEADER_STR(message)), 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()); 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\" <sip:%s@%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_name = switch_core_session_strdup(tech_pvt->session, name);
tech_pvt->last_sent_callee_id_number = switch_core_session_strdup(tech_pvt->session, number); 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)) { if (!sofia_test_flag(tech_pvt, TFLAG_BYE)) {
char *extra_header = sofia_glue_get_extra_headers(channel, SOFIA_SIP_PROGRESS_HEADER_PREFIX); 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) && 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)) { tech_pvt->early_sdp && strcmp(tech_pvt->early_sdp, tech_pvt->local_sdp_str)) {

View File

@ -4664,22 +4664,22 @@ void sofia_glue_tech_track(sofia_profile_t *profile, switch_core_session_t *sess
return; 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) { if (switch_ivr_generate_xml_cdr(session, &cdr) == SWITCH_STATUS_SUCCESS) {
xml_cdr_text = switch_xml_toxml(cdr, SWITCH_FALSE); xml_cdr_text = switch_xml_toxml(cdr, SWITCH_FALSE);
switch_xml_free(cdr); switch_xml_free(cdr);
} }
if (xml_cdr_text) { if (xml_cdr_text) {
sql = switch_mprintf("insert into sip_recovery (runtime_uuid, profile_name, hostname, uuid, metadata) values ('%q','%q','%q','%q','%q')", if (sofia_test_flag(tech_pvt, TFLAG_TRACKED)) {
switch_core_get_uuid(), profile->name, mod_sofia_globals.hostname, switch_core_session_get_uuid(session), xml_cdr_text); 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)) { if (sofia_test_pflag(profile, PFLAG_TRACK_CALLS_EVENTS)) {
switch_event_t *event = NULL; switch_event_t *event = NULL;
if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, MY_EVENT_RECOVERY_SEND) == SWITCH_STATUS_SUCCESS) { 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, "profile_name", profile->name);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "sql", sql); 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); free(xml_cdr_text);
sofia_set_flag(tech_pvt, TFLAG_TRACKED); sofia_set_flag(tech_pvt, TFLAG_TRACKED);

View File

@ -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)) { 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," switch_snprintf(sql, sizeof(sql), "select call_id,sip_user,sip_host,contact,status,rpid,"
"expires,user_agent,server_user,server_host,profile_name" "expires,user_agent,server_user,server_host,profile_name"
" from sip_registrations where (status like '%%AUTO-NAT%%' " " from sip_registrations where (status like '%%NAT%%' "
"or status like '%%UDP-NAT%%') and hostname='%s'", mod_sofia_globals.hostname); "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); sofia_glue_execute_sql_callback(profile, NULL, sql, sofia_reg_nat_callback, profile);
} }

View File

@ -2035,6 +2035,36 @@ SWITCH_DECLARE(void) switch_channel_set_hunt_caller_profile(switch_channel_t *ch
switch_mutex_unlock(channel->profile_mutex); 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_DECLARE(void) switch_channel_set_originatee_caller_profile(switch_channel_t *channel, switch_caller_profile_t *caller_profile)
{ {
switch_assert(channel != NULL); switch_assert(channel != NULL);

View File

@ -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); 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) { if (switch_event_create(&event, SWITCH_EVENT_CHANNEL_OUTGOING) == SWITCH_STATUS_SUCCESS) {

View File

@ -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); 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) { if (caller_profile->originator_caller_profile) {
switch_caller_profile_t *cp = NULL; switch_caller_profile_t *cp = NULL;
int off = 0; int off = 0;

View File

@ -1683,16 +1683,20 @@ SWITCH_DECLARE(switch_status_t) switch_api_execute(const char *cmd, const char *
{ {
switch_api_interface_t *api; switch_api_interface_t *api;
switch_status_t status; switch_status_t status;
char *arg_no_spaces; char *arg_used;
char *cmd_no_spaces; char *cmd_used;
switch_assert(stream != NULL); switch_assert(stream != NULL);
switch_assert(stream->data != NULL); switch_assert(stream->data != NULL);
switch_assert(stream->write_function != NULL); switch_assert(stream->write_function != NULL);
if (strcasecmp(cmd, "console_complete")) {
cmd_no_spaces = switch_strip_whitespace(cmd); cmd_used = switch_strip_whitespace(cmd);
arg_no_spaces = switch_strip_whitespace(arg); arg_used = switch_strip_whitespace(arg);
} else {
cmd_used = (char *) cmd;
arg_used = (char *) arg;
}
if (!stream->param_event) { 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 (stream->param_event) {
if (cmd_no_spaces) { if (cmd_used) {
switch_event_add_header_string(stream->param_event, SWITCH_STACK_BOTTOM, "API-Command", cmd_no_spaces); switch_event_add_header_string(stream->param_event, SWITCH_STACK_BOTTOM, "API-Command", cmd_used);
} }
if (arg_no_spaces) { if (arg_used) {
switch_event_add_header_string(stream->param_event, SWITCH_STACK_BOTTOM, "API-Command-Argument", arg_no_spaces); 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 (cmd_used && (api = switch_loadable_module_get_api_interface(cmd_used)) != 0) {
if ((status = api->function(arg_no_spaces, session, stream)) != SWITCH_STATUS_SUCCESS) { if ((status = api->function(arg_used, session, stream)) != SWITCH_STATUS_SUCCESS) {
stream->write_function(stream, "COMMAND RETURNED ERROR!\n"); stream->write_function(stream, "COMMAND RETURNED ERROR!\n");
} }
UNPROTECT_INTERFACE(api); 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_event_fire(&stream->param_event);
} }
switch_safe_free(cmd_no_spaces); if (cmd_used != cmd) {
switch_safe_free(arg_no_spaces); switch_safe_free(cmd_used);
}
if (arg_used != arg) {
switch_safe_free(arg_used);
}
return status; return status;
} }