@ -290,6 +290,7 @@
< value name = " JOINUNAVAIL " / >
< value name = " LEAVEUNAVAIL " / >
< value name = " CONTINUE " / >
< value name = " WITHDRAW " / >
< / variable >
< variable name = " ABANDONED " >
< para > If the call was not answered by an agent this variable will be TRUE . < / para >
@ -298,6 +299,9 @@
< variable name = " DIALEDPEERNUMBER " >
< para > Resource of the agent that was dialed set on the outbound channel . < / para >
< / variable >
< variable name = " QUEUE_WITHDRAW_INFO " >
< para > If the call was successfully withdrawn from the queue , and the withdraw request was provided with optional withdraw info , the withdraw info will be stored in this variable . < / para >
< / variable >
< / variablelist >
< / description >
< see - also >
@ -1057,6 +1061,25 @@
< description >
< / description >
< / manager >
< manager name = " QueueWithdrawCaller " language = " en_US " >
< synopsis >
Request to withdraw a caller from the queue back to the dialplan .
< / synopsis >
< syntax >
< xi : include xpointer = " xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID']) " / >
< parameter name = " Queue " required = " true " >
< para > The name of the queue to take action on . < / para >
< / parameter >
< parameter name = " Caller " required = " true " >
< para > The caller ( channel ) to withdraw from the queue . < / para >
< / parameter >
< parameter name = " WithdrawInfo " required = " false " >
< para > Optional info to store . If the call is successfully withdrawn from the queue , this information will be available in the QUEUE_WITHDRAW_INFO variable . < / para >
< / parameter >
< / syntax >
< description >
< / description >
< / manager >
< managerEvent language = " en_US " name = " QueueParams " >
< managerEventInstance class = " EVENT_FLAG_AGENT " >
@ -1602,6 +1625,7 @@ enum queue_result {
QUEUE_LEAVEUNAVAIL = 5 ,
QUEUE_FULL = 6 ,
QUEUE_CONTINUE = 7 ,
QUEUE_WITHDRAW = 8 ,
} ;
static const struct {
@ -1616,6 +1640,7 @@ static const struct {
{ QUEUE_LEAVEUNAVAIL , " LEAVEUNAVAIL " } ,
{ QUEUE_FULL , " FULL " } ,
{ QUEUE_CONTINUE , " CONTINUE " } ,
{ QUEUE_WITHDRAW , " WITHDRAW " } ,
} ;
enum queue_timeout_priority {
@ -1684,6 +1709,8 @@ struct queue_ent {
time_t start ; /*!< When we started holding */
time_t expire ; /*!< When this entry should expire (time out of queue) */
int cancel_answered_elsewhere ; /*!< Whether we should force the CAE flag on this call (C) option*/
unsigned int withdraw : 1 ; /*!< Should this call exit the queue at its next iteration? Used for QueueWithdrawCaller */
char * withdraw_info ; /*!< Optional info passed by the caller of QueueWithdrawCaller */
struct ast_channel * chan ; /*!< Our channel */
AST_LIST_HEAD_NOLOCK ( , penalty_rule ) qe_rules ; /*!< Local copy of the queue's penalty rules */
struct penalty_rule * pr ; /*!< Pointer to the next penalty rule to implement */
@ -5802,6 +5829,13 @@ static int wait_our_turn(struct queue_ent *qe, int ringing, enum queue_result *r
/* This is the holding pen for callers 2 through maxlen */
for ( ; ; ) {
/* A request to withdraw this call from the queue arrived */
if ( qe - > withdraw ) {
* reason = QUEUE_WITHDRAW ;
res = 1 ;
break ;
}
if ( is_our_turn ( qe ) ) {
break ;
}
@ -7622,6 +7656,51 @@ static int change_priority_caller_on_queue(const char *queuename, const char *ca
}
/*! \brief Request to withdraw a caller from a queue
* \ retval RES_NOSUCHQUEUE queue does not exist
* \ retval RES_OKAY withdraw request sent
* \ retval RES_NOT_CALLER queue exists but no caller
* \ retval RES_EXISTS a withdraw request was already sent for this caller ( channel ) and queue
*/
static int request_withdraw_caller_from_queue ( const char * queuename , const char * caller , const char * withdraw_info )
{
struct call_queue * q ;
struct queue_ent * qe ;
int res = RES_NOSUCHQUEUE ;
/*! \note Ensure the appropriate realtime queue is loaded. Note that this
* short - circuits if the queue is already in memory . */
if ( ! ( q = find_load_queue_rt_friendly ( queuename ) ) ) {
return res ;
}
ao2_lock ( q ) ;
res = RES_NOT_CALLER ;
for ( qe = q - > head ; qe ; qe = qe - > next ) {
if ( ! strcmp ( ast_channel_name ( qe - > chan ) , caller ) ) {
if ( qe - > withdraw ) {
ast_debug ( 1 , " Ignoring duplicate withdraw request of caller %s from queue %s \n " , caller , queuename ) ;
res = RES_EXISTS ;
} else {
ast_debug ( 1 , " Requested withdraw of caller %s from queue %s \n " , caller , queuename ) ;
/* It is not possible to change the withdraw info by further withdraw requests for this caller (channel)
in this queue , so we do not need to worry about a memory leak here . */
if ( withdraw_info ) {
qe - > withdraw_info = ast_strdup ( withdraw_info ) ;
}
qe - > withdraw = 1 ;
res = RES_OKAY ;
}
break ;
}
}
ao2_unlock ( q ) ;
queue_unref ( q ) ;
return res ;
}
static int publish_queue_member_pause ( struct call_queue * q , struct member * member )
{
struct ast_json * json_blob = queue_member_blob_create ( q , member ) ;
@ -8569,6 +8648,13 @@ check_turns:
/* they may dial a digit from the queue context; */
/* or, they may timeout. */
/* A request to withdraw this call from the queue arrived */
if ( qe . withdraw ) {
reason = QUEUE_WITHDRAW ;
res = 1 ;
break ;
}
/* Leave if we have exceeded our queuetimeout */
if ( qe . expire & & ( time ( NULL ) > = qe . expire ) ) {
record_abandoned ( & qe ) ;
@ -8596,6 +8682,13 @@ check_turns:
}
}
/* A request to withdraw this call from the queue arrived */
if ( qe . withdraw ) {
reason = QUEUE_WITHDRAW ;
res = 1 ;
break ;
}
/* Leave if we have exceeded our queuetimeout */
if ( qe . expire & & ( time ( NULL ) > = qe . expire ) ) {
record_abandoned ( & qe ) ;
@ -8670,7 +8763,14 @@ check_turns:
stop :
if ( res ) {
if ( res < 0 ) {
if ( reason = = QUEUE_WITHDRAW ) {
record_abandoned ( & qe ) ;
ast_queue_log ( qe . parent - > name , ast_channel_uniqueid ( qe . chan ) , " NONE " , " WITHDRAW " , " %d|%d|%ld|%.40s " , qe . pos , qe . opos , ( long ) ( time ( NULL ) - qe . start ) , qe . withdraw_info ? qe . withdraw_info : " " ) ;
if ( qe . withdraw_info ) {
pbx_builtin_setvar_helper ( qe . chan , " QUEUE_WITHDRAW_INFO " , qe . withdraw_info ) ;
}
res = 0 ;
} else if ( res < 0 ) {
if ( ! qe . handled ) {
record_abandoned ( & qe ) ;
ast_queue_log ( args . queuename , ast_channel_uniqueid ( chan ) , " NONE " , " ABANDON " ,
@ -8690,6 +8790,13 @@ stop:
}
}
/* Free the optional withdraw info if present */
/* This is done here to catch all cases. e.g. if the call eventually wasn't withdrawn, e.g. answered */
if ( qe . withdraw_info ) {
ast_free ( qe . withdraw_info ) ;
qe . withdraw_info = NULL ;
}
/* Don't allow return code > 0 */
if ( res > = 0 ) {
res = 0 ;
@ -10743,6 +10850,41 @@ static int manager_change_priority_caller_on_queue(struct mansession *s, const s
return 0 ;
}
static int manager_request_withdraw_caller_from_queue ( struct mansession * s , const struct message * m )
{
const char * queuename , * caller , * withdraw_info ;
queuename = astman_get_header ( m , " Queue " ) ;
caller = astman_get_header ( m , " Caller " ) ;
withdraw_info = astman_get_header ( m , " WithdrawInfo " ) ;
if ( ast_strlen_zero ( queuename ) ) {
astman_send_error ( s , m , " 'Queue' not specified. " ) ;
return 0 ;
}
if ( ast_strlen_zero ( caller ) ) {
astman_send_error ( s , m , " 'Caller' not specified. " ) ;
return 0 ;
}
switch ( request_withdraw_caller_from_queue ( queuename , caller , withdraw_info ) ) {
case RES_OKAY :
astman_send_ack ( s , m , " Withdraw requested successfully " ) ;
break ;
case RES_NOSUCHQUEUE :
astman_send_error ( s , m , " Unable to request withdraw from queue: No such queue " ) ;
break ;
case RES_NOT_CALLER :
astman_send_error ( s , m , " Unable to request withdraw from queue: No such caller " ) ;
break ;
case RES_EXISTS :
astman_send_error ( s , m , " Unable to request withdraw from queue: Already requested " ) ;
break ;
}
return 0 ;
}
static char * handle_queue_add_member ( struct ast_cli_entry * e , int cmd , struct ast_cli_args * a )
@ -11472,6 +11614,7 @@ static int unload_module(void)
ast_manager_unregister ( " QueueReset " ) ;
ast_manager_unregister ( " QueueMemberRingInUse " ) ;
ast_manager_unregister ( " QueueChangePriorityCaller " ) ;
ast_manager_unregister ( " QueueWithdrawCaller " ) ;
ast_unregister_application ( app_aqm ) ;
ast_unregister_application ( app_rqm ) ;
ast_unregister_application ( app_pqm ) ;
@ -11585,6 +11728,7 @@ static int load_module(void)
err | = ast_manager_register_xml ( " QueueReload " , 0 , manager_queue_reload ) ;
err | = ast_manager_register_xml ( " QueueReset " , 0 , manager_queue_reset ) ;
err | = ast_manager_register_xml ( " QueueChangePriorityCaller " , 0 , manager_change_priority_caller_on_queue ) ;
err | = ast_manager_register_xml ( " QueueWithdrawCaller " , 0 , manager_request_withdraw_caller_from_queue ) ;
err | = ast_custom_function_register ( & queuevar_function ) ;
err | = ast_custom_function_register ( & queueexists_function ) ;
err | = ast_custom_function_register ( & queuemembercount_function ) ;