<p>Kfir Itzhak has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.asterisk.org/c/asterisk/+/18008">View Change</a></p><pre style="font-family: monospace,monospace; white-space: pre-wrap;">app_queue: Add QueueWithdrawCaller AMI action<br><br>This adds a new AMI action called QueueWithdrawCaller.<br>This AMI action makes it possible to withdraw a caller from a queue, in a safe and generic manner.<br>This is useful for retrieving a specific call and dispatching it to a specific extension.<br>It is a best-effort request. It works by signaling the caller to exit the queue application whenever it can.<br><br>ASTERISK-29909 #close<br><br>Change-Id: Ic15aa238e23b2884abdcaadff2fda7679e29b7ec<br>---<br>M apps/app_queue.c<br>1 file changed, 129 insertions(+), 1 deletion(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/08/18008/1</pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/apps/app_queue.c b/apps/app_queue.c</span><br><span>index df5dba2..76841ee 100644</span><br><span>--- a/apps/app_queue.c</span><br><span>+++ b/apps/app_queue.c</span><br><span>@@ -290,6 +290,7 @@</span><br><span> <value name="JOINUNAVAIL" /></span><br><span> <value name="LEAVEUNAVAIL" /></span><br><span> <value name="CONTINUE" /></span><br><span style="color: hsl(120, 100%, 40%);">+ <value name="WITHDRAW" /></span><br><span> </variable></span><br><span> <variable name="ABANDONED"></span><br><span> <para>If the call was not answered by an agent this variable will be TRUE.</para></span><br><span>@@ -1057,6 +1058,25 @@</span><br><span> <description></span><br><span> </description></span><br><span> </manager></span><br><span style="color: hsl(120, 100%, 40%);">+ <manager name="QueueWithdrawCaller" language="en_US"></span><br><span style="color: hsl(120, 100%, 40%);">+ <synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+ Request to withdraw a caller from the queue back to the dialplan.</span><br><span style="color: hsl(120, 100%, 40%);">+ </synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+ <syntax></span><br><span style="color: hsl(120, 100%, 40%);">+ <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" /></span><br><span style="color: hsl(120, 100%, 40%);">+ <parameter name="Queue" required="true"></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>The name of the queue to take action on.</para></span><br><span style="color: hsl(120, 100%, 40%);">+ </parameter></span><br><span style="color: hsl(120, 100%, 40%);">+ <parameter name="Caller" required="true"></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>The caller (channel) to withdraw from the queue.</para></span><br><span style="color: hsl(120, 100%, 40%);">+ </parameter></span><br><span style="color: hsl(120, 100%, 40%);">+ <parameter name="WithdrawInfo" required="false"></span><br><span style="color: hsl(120, 100%, 40%);">+ <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></span><br><span style="color: hsl(120, 100%, 40%);">+ </parameter></span><br><span style="color: hsl(120, 100%, 40%);">+ </syntax></span><br><span style="color: hsl(120, 100%, 40%);">+ <description></span><br><span style="color: hsl(120, 100%, 40%);">+ </description></span><br><span style="color: hsl(120, 100%, 40%);">+ </manager></span><br><span> </span><br><span> <managerEvent language="en_US" name="QueueParams"></span><br><span> <managerEventInstance class="EVENT_FLAG_AGENT"></span><br><span>@@ -1605,6 +1625,7 @@</span><br><span> QUEUE_LEAVEUNAVAIL = 5,</span><br><span> QUEUE_FULL = 6,</span><br><span> QUEUE_CONTINUE = 7,</span><br><span style="color: hsl(120, 100%, 40%);">+ QUEUE_WITHDRAW = 8,</span><br><span> };</span><br><span> </span><br><span> static const struct {</span><br><span>@@ -1619,6 +1640,7 @@</span><br><span> { QUEUE_LEAVEUNAVAIL, "LEAVEUNAVAIL" },</span><br><span> { QUEUE_FULL, "FULL" },</span><br><span> { QUEUE_CONTINUE, "CONTINUE" },</span><br><span style="color: hsl(120, 100%, 40%);">+ { QUEUE_WITHDRAW, "WITHDRAW" },</span><br><span> };</span><br><span> </span><br><span> enum queue_timeout_priority {</span><br><span>@@ -1687,6 +1709,8 @@</span><br><span> time_t start; /*!< When we started holding */</span><br><span> time_t expire; /*!< When this entry should expire (time out of queue) */</span><br><span> int cancel_answered_elsewhere; /*!< Whether we should force the CAE flag on this call (C) option*/</span><br><span style="color: hsl(120, 100%, 40%);">+ unsigned int withdraw:1; /*!< Should this call exit the queue at its next iteration? Used for QueueWithdrawCaller */</span><br><span style="color: hsl(120, 100%, 40%);">+ char* withdraw_info; /*!< Optional info passed by the caller of QueueWithdrawCaller */</span><br><span> struct ast_channel *chan; /*!< Our channel */</span><br><span> AST_LIST_HEAD_NOLOCK(,penalty_rule) qe_rules; /*!< Local copy of the queue's penalty rules */</span><br><span> struct penalty_rule *pr; /*!< Pointer to the next penalty rule to implement */</span><br><span>@@ -5771,6 +5795,13 @@</span><br><span> /* This is the holding pen for callers 2 through maxlen */</span><br><span> for (;;) {</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ /* A request to withdraw this call from the queue arrived */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (qe->withdraw) {</span><br><span style="color: hsl(120, 100%, 40%);">+ *reason = QUEUE_WITHDRAW;</span><br><span style="color: hsl(120, 100%, 40%);">+ res = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+ break;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> if (is_our_turn(qe)) {</span><br><span> break;</span><br><span> }</span><br><span>@@ -7591,6 +7622,41 @@</span><br><span> }</span><br><span> </span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+/*! \brief Request to withdraw a caller from a queue</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval RES_NOSUCHQUEUE queue does not exist</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval RES_OKAY withdraw request sent</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval RES_NOT_CALLER queue exists but no caller</span><br><span style="color: hsl(120, 100%, 40%);">+*/</span><br><span style="color: hsl(120, 100%, 40%);">+static int request_withdraw_caller_from_queue(const char *queuename, const char *caller, const char *withdraw_info)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct call_queue *q;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct queue_ent *qe;</span><br><span style="color: hsl(120, 100%, 40%);">+ int res = RES_NOSUCHQUEUE;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! \note Ensure the appropriate realtime queue is loaded. Note that this</span><br><span style="color: hsl(120, 100%, 40%);">+ * short-circuits if the queue is already in memory. */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!(q = find_load_queue_rt_friendly(queuename))) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return res;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_lock(q);</span><br><span style="color: hsl(120, 100%, 40%);">+ res = RES_NOT_CALLER;</span><br><span style="color: hsl(120, 100%, 40%);">+ for (qe = q->head; qe; qe = qe->next) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (strcmp(ast_channel_name(qe->chan), caller) == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_debug(1, "Requested withdraw of caller %s from queue %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ caller, queuename);</span><br><span style="color: hsl(120, 100%, 40%);">+ if(withdraw_info && !qe->withdraw_info) {</span><br><span style="color: hsl(120, 100%, 40%);">+ qe->withdraw_info = ast_strdup(withdraw_info);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ qe->withdraw = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+ res = RES_OKAY;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_unlock(q);</span><br><span style="color: hsl(120, 100%, 40%);">+ return res;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> static int publish_queue_member_pause(struct call_queue *q, struct member *member, const char *reason)</span><br><span> {</span><br><span> struct ast_json *json_blob = queue_member_blob_create(q, member);</span><br><span>@@ -8536,6 +8602,13 @@</span><br><span> /* they may dial a digit from the queue context; */</span><br><span> /* or, they may timeout. */</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ /* A request to withdraw this call from the queue arrived */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (qe.withdraw) {</span><br><span style="color: hsl(120, 100%, 40%);">+ reason = QUEUE_WITHDRAW;</span><br><span style="color: hsl(120, 100%, 40%);">+ res = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+ break;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /* Leave if we have exceeded our queuetimeout */</span><br><span> if (qe.expire && (time(NULL) >= qe.expire)) {</span><br><span> record_abandoned(&qe);</span><br><span>@@ -8563,6 +8636,13 @@</span><br><span> }</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ /* A request to withdraw this call from the queue arrived */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (qe.withdraw) {</span><br><span style="color: hsl(120, 100%, 40%);">+ reason = QUEUE_WITHDRAW;</span><br><span style="color: hsl(120, 100%, 40%);">+ res = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+ break;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /* Leave if we have exceeded our queuetimeout */</span><br><span> if (qe.expire && (time(NULL) >= qe.expire)) {</span><br><span> record_abandoned(&qe);</span><br><span>@@ -8637,7 +8717,14 @@</span><br><span> </span><br><span> stop:</span><br><span> if (res) {</span><br><span style="color: hsl(0, 100%, 40%);">- if (res < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (reason == QUEUE_WITHDRAW) {</span><br><span style="color: hsl(120, 100%, 40%);">+ record_abandoned(&qe);</span><br><span style="color: hsl(120, 100%, 40%);">+ 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 : "");</span><br><span style="color: hsl(120, 100%, 40%);">+ if (qe.withdraw_info) {</span><br><span style="color: hsl(120, 100%, 40%);">+ pbx_builtin_setvar_helper(qe.chan, "QUEUE_WITHDRAW_INFO", qe.withdraw_info);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ res = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ } else if (res < 0) {</span><br><span> if (!qe.handled) {</span><br><span> record_abandoned(&qe);</span><br><span> ast_queue_log(args.queuename, ast_channel_uniqueid(chan), "NONE", "ABANDON",</span><br><span>@@ -8657,6 +8744,13 @@</span><br><span> }</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ /* Free the optional withdraw info if present */</span><br><span style="color: hsl(120, 100%, 40%);">+ /* This is done here to catch all cases. e.g. if the call eventually wasn't withdrawn, e.g. answered */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (qe.withdraw_info != NULL) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_free(qe.withdraw_info);</span><br><span style="color: hsl(120, 100%, 40%);">+ qe.withdraw_info = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /* Don't allow return code > 0 */</span><br><span> if (res >= 0) {</span><br><span> res = 0;</span><br><span>@@ -10690,6 +10784,38 @@</span><br><span> return 0;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+static int manager_request_withdraw_caller_from_queue(struct mansession *s, const struct message *m)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *queuename, *caller, *withdraw_info;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ queuename = astman_get_header(m, "Queue");</span><br><span style="color: hsl(120, 100%, 40%);">+ caller = astman_get_header(m, "Caller");</span><br><span style="color: hsl(120, 100%, 40%);">+ withdraw_info = astman_get_header(m, "WithdrawInfo");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_strlen_zero(queuename)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ astman_send_error(s, m, "'Queue' not specified.");</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_strlen_zero(caller)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ astman_send_error(s, m, "'Caller' not specified.");</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ switch (request_withdraw_caller_from_queue(queuename, caller, withdraw_info)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ case RES_OKAY:</span><br><span style="color: hsl(120, 100%, 40%);">+ astman_send_ack(s, m, "Withdraw requested successfully");</span><br><span style="color: hsl(120, 100%, 40%);">+ break;</span><br><span style="color: hsl(120, 100%, 40%);">+ case RES_NOSUCHQUEUE:</span><br><span style="color: hsl(120, 100%, 40%);">+ astman_send_error(s, m, "Unable to request withdraw from queue: No such queue");</span><br><span style="color: hsl(120, 100%, 40%);">+ break;</span><br><span style="color: hsl(120, 100%, 40%);">+ case RES_NOT_CALLER:</span><br><span style="color: hsl(120, 100%, 40%);">+ astman_send_error(s, m, "Unable to request withdraw from queue: No such caller");</span><br><span style="color: hsl(120, 100%, 40%);">+ break;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span> </span><br><span> </span><br><span> static char *handle_queue_add_member(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)</span><br><span>@@ -11419,6 +11545,7 @@</span><br><span> ast_manager_unregister("QueueReset");</span><br><span> ast_manager_unregister("QueueMemberRingInUse");</span><br><span> ast_manager_unregister("QueueChangePriorityCaller");</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_manager_unregister("QueueWithdrawCaller");</span><br><span> ast_unregister_application(app_aqm);</span><br><span> ast_unregister_application(app_rqm);</span><br><span> ast_unregister_application(app_pqm);</span><br><span>@@ -11532,6 +11659,7 @@</span><br><span> err |= ast_manager_register_xml("QueueReload", 0, manager_queue_reload);</span><br><span> err |= ast_manager_register_xml("QueueReset", 0, manager_queue_reset);</span><br><span> err |= ast_manager_register_xml("QueueChangePriorityCaller", 0, manager_change_priority_caller_on_queue);</span><br><span style="color: hsl(120, 100%, 40%);">+ err |= ast_manager_register_xml("QueueWithdrawCaller", 0, manager_request_withdraw_caller_from_queue);</span><br><span> err |= ast_custom_function_register(&queuevar_function);</span><br><span> err |= ast_custom_function_register(&queueexists_function);</span><br><span> err |= ast_custom_function_register(&queuemembercount_function);</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.asterisk.org/c/asterisk/+/18008">change 18008</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://gerrit.asterisk.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.asterisk.org/c/asterisk/+/18008"/><meta itemprop="name" content="View Change"/></div></div>
<div style="display:none"> Gerrit-Project: asterisk </div>
<div style="display:none"> Gerrit-Branch: 16 </div>
<div style="display:none"> Gerrit-Change-Id: Ic15aa238e23b2884abdcaadff2fda7679e29b7ec </div>
<div style="display:none"> Gerrit-Change-Number: 18008 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: Kfir Itzhak <mastertheknife@gmail.com> </div>
<div style="display:none"> Gerrit-MessageType: newchange </div>