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