<p>George Joseph <strong>submitted</strong> this change.</p><p><a href="https://gerrit.asterisk.org/c/asterisk/+/15928">View Change</a></p><div style="white-space:pre-wrap">Approvals:
Joshua Colp: Looks good to me, but someone else must approve
George Joseph: Looks good to me, approved; Approved for Submit
</div><pre style="font-family: monospace,monospace; white-space: pre-wrap;">app_waitforcond: New application<br><br>While several applications exist to wait for<br>a certain event to occur, none allow waiting<br>for any generic expression to become true.<br>This application allows for waiting for a condition<br>to become true, with configurable timeout and<br>checking interval.<br><br>ASTERISK-29444<br><br>Change-Id: I08adf2824b8bc63405778cf355963b5005612f41<br>---<br>A apps/app_waitforcond.c<br>A doc/CHANGES-staging/app_waitforcond.txt<br>2 files changed, 239 insertions(+), 0 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/apps/app_waitforcond.c b/apps/app_waitforcond.c</span><br><span>new file mode 100644</span><br><span>index 0000000..7bc254c</span><br><span>--- /dev/null</span><br><span>+++ b/apps/app_waitforcond.c</span><br><span>@@ -0,0 +1,234 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/*</span><br><span style="color: hsl(120, 100%, 40%);">+ * Asterisk -- An open source telephony toolkit.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Copyright (C) 2021, Naveen Albert</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Naveen Albert <asterisk@phreaknet.org></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * See http://www.asterisk.org for more information about</span><br><span style="color: hsl(120, 100%, 40%);">+ * the Asterisk project. Please do not directly contact</span><br><span style="color: hsl(120, 100%, 40%);">+ * any of the maintainers of this project for assistance;</span><br><span style="color: hsl(120, 100%, 40%);">+ * the project provides a web site, mailing lists and IRC</span><br><span style="color: hsl(120, 100%, 40%);">+ * channels for your use.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is free software, distributed under the terms of</span><br><span style="color: hsl(120, 100%, 40%);">+ * the GNU General Public License Version 2. See the LICENSE file</span><br><span style="color: hsl(120, 100%, 40%);">+ * at the top of the source tree.</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%);">+/*! \file</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Sleep until a condition is true</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \author Naveen Albert <asterisk@phreaknet.org></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \ingroup applications</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%);">+/*** MODULEINFO</span><br><span style="color: hsl(120, 100%, 40%);">+ <support_level>extended</support_level></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%);">+#include "asterisk.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/logger.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/channel.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/pbx.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/module.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/app.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*** DOCUMENTATION</span><br><span style="color: hsl(120, 100%, 40%);">+ <application name="WaitForCondition" language="en_US"></span><br><span style="color: hsl(120, 100%, 40%);">+ <synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+ Wait (sleep) until the given condition is true.</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%);">+ <parameter name="replacementchar" required="true"></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>Specifies the character in the expression used to replace the <literal>$</literal></span><br><span style="color: hsl(120, 100%, 40%);">+ character. This character should not be used anywhere in the expression itself.</para></span><br><span style="color: hsl(120, 100%, 40%);">+ </parameter></span><br><span style="color: hsl(120, 100%, 40%);">+ <parameter name="expression" required="true"></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>A modified logical expression with the <literal>$</literal> characters replaced by</span><br><span style="color: hsl(120, 100%, 40%);">+ <replaceable>replacementchar</replaceable>. This is necessary to pass the expression itself</span><br><span style="color: hsl(120, 100%, 40%);">+ into the application, rather than its initial evaluation.</para></span><br><span style="color: hsl(120, 100%, 40%);">+ </parameter></span><br><span style="color: hsl(120, 100%, 40%);">+ <parameter name="timeout"></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>The maximum amount of time, in seconds, this application should wait for a condition</span><br><span style="color: hsl(120, 100%, 40%);">+ to become true before dialplan execution continues automatically to the next priority.</span><br><span style="color: hsl(120, 100%, 40%);">+ By default, there is no timeout.</para></span><br><span style="color: hsl(120, 100%, 40%);">+ </parameter></span><br><span style="color: hsl(120, 100%, 40%);">+ <parameter name="interval"></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>The frequency, in seconds, of polling the condition, which can be adjusted depending</span><br><span style="color: hsl(120, 100%, 40%);">+ on how time-sensitive execution needs to be. By default, this is 0.05.</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%);">+ <para>Waits until <replaceable>expression</replaceable> evaluates to true, checking every</span><br><span style="color: hsl(120, 100%, 40%);">+ <replaceable>interval</replaceable> seconds for up to <replaceable>timeout</replaceable>. Default</span><br><span style="color: hsl(120, 100%, 40%);">+ is evaluate <replaceable>expression</replaceable> every 50 milliseconds with no timeout.</para></span><br><span style="color: hsl(120, 100%, 40%);">+ <example title="Wait for ${condition} to become 1 for up to 40 seconds, checking every 500ms"></span><br><span style="color: hsl(120, 100%, 40%);">+ same => n,WaitForCondition(#,#["#{condition}"="1"],40,0.5)</span><br><span style="color: hsl(120, 100%, 40%);">+ </example></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>Sets <variable>WAITFORCONDITIONSTATUS</variable> to one of the following values:</para></span><br><span style="color: hsl(120, 100%, 40%);">+ <variablelist></span><br><span style="color: hsl(120, 100%, 40%);">+ <variable name="WAITFORCONDITIONSTATUS"></span><br><span style="color: hsl(120, 100%, 40%);">+ <value name="TRUE"></span><br><span style="color: hsl(120, 100%, 40%);">+ Condition evaluated to true before timeout expired.</span><br><span style="color: hsl(120, 100%, 40%);">+ </value></span><br><span style="color: hsl(120, 100%, 40%);">+ <value name="FAILURE"></span><br><span style="color: hsl(120, 100%, 40%);">+ Invalid argument.</span><br><span style="color: hsl(120, 100%, 40%);">+ </value></span><br><span style="color: hsl(120, 100%, 40%);">+ <value name="TIMEOUT"></span><br><span style="color: hsl(120, 100%, 40%);">+ Timeout elapsed without condition evaluating to true.</span><br><span style="color: hsl(120, 100%, 40%);">+ </value></span><br><span style="color: hsl(120, 100%, 40%);">+ <value name="HANGUP"></span><br><span style="color: hsl(120, 100%, 40%);">+ Channel hung up before condition became true.</span><br><span style="color: hsl(120, 100%, 40%);">+ </value></span><br><span style="color: hsl(120, 100%, 40%);">+ </variable></span><br><span style="color: hsl(120, 100%, 40%);">+ </variablelist></span><br><span style="color: hsl(120, 100%, 40%);">+ </description></span><br><span style="color: hsl(120, 100%, 40%);">+ </application></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%);">+static char *app = "WaitForCondition";</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int waitforcond_exec(struct ast_channel *chan, const char *data)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ int ms, i;</span><br><span style="color: hsl(120, 100%, 40%);">+ double timeout = 0, poll = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ int timeout_ms = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ int poll_ms = 50; /* default is evaluate the condition every 50ms */</span><br><span style="color: hsl(120, 100%, 40%);">+ struct timeval start = ast_tvnow();</span><br><span style="color: hsl(120, 100%, 40%);">+ char dollarsignrep;</span><br><span style="color: hsl(120, 100%, 40%);">+ int brackets = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ char *pos, *open_bracket, *expression, *optargs = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ char condition[512];</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_DECLARE_APP_ARGS(args,</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_APP_ARG(timeout);</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_APP_ARG(interval);</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%);">+ pos = ast_strdupa(data);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_strlen_zero(pos)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR, "WaitForCondition requires a condition\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ pbx_builtin_setvar_helper(chan, "WAITFORCONDITIONSTATUS", "FAILURE");</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%);">+ /* is there at least a [ followed by a ] somewhere ? */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!(open_bracket = strchr(pos, '[')) || !strchr(open_bracket, ']')) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR, "No expression detected. Did you forget to replace the $ signs?\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ pbx_builtin_setvar_helper(chan, "WAITFORCONDITIONSTATUS", "FAILURE");</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%);">+ dollarsignrep = pos[0];</span><br><span style="color: hsl(120, 100%, 40%);">+ if (dollarsignrep == '$' || dollarsignrep == '[' || dollarsignrep == ']'</span><br><span style="color: hsl(120, 100%, 40%);">+ || dollarsignrep == '{' || dollarsignrep == '}') {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR, "Dollar sign replacement cannot be %c.\n", dollarsignrep);</span><br><span style="color: hsl(120, 100%, 40%);">+ pbx_builtin_setvar_helper(chan, "WAITFORCONDITIONSTATUS", "FAILURE");</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%);">+ ++pos;</span><br><span style="color: hsl(120, 100%, 40%);">+ if (pos[0] != ',') {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR, "Invalid separator: %c\n", pos[0]);</span><br><span style="color: hsl(120, 100%, 40%);">+ pbx_builtin_setvar_helper(chan, "WAITFORCONDITIONSTATUS", "FAILURE");</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%);">+ ++pos;</span><br><span style="color: hsl(120, 100%, 40%);">+ if (pos[0] != dollarsignrep) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR, "Expression start does not match provided replacement: %c\n", pos[0]);</span><br><span style="color: hsl(120, 100%, 40%);">+ pbx_builtin_setvar_helper(chan, "WAITFORCONDITIONSTATUS", "FAILURE");</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%);">+ expression = pos; /* we're at the start of the expression */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* commas may appear within the expression, so go until we've encountered as many closing brackets as opening */</span><br><span style="color: hsl(120, 100%, 40%);">+ while (++pos) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (pos[0] == '\0') {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR, "Could not parse end of expression.\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ pbx_builtin_setvar_helper(chan, "WAITFORCONDITIONSTATUS", "FAILURE");</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%);">+ if (pos[0] == '[') {</span><br><span style="color: hsl(120, 100%, 40%);">+ brackets++;</span><br><span style="color: hsl(120, 100%, 40%);">+ } else if (pos[0] == ']') {</span><br><span style="color: hsl(120, 100%, 40%);">+ brackets--;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (brackets == 0) { /* reached end of expression */</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%);">+ ++pos;</span><br><span style="color: hsl(120, 100%, 40%);">+ if (pos[0] != '\0') {</span><br><span style="color: hsl(120, 100%, 40%);">+ ++pos; /* eat comma separator */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (pos[0] != '\0') {</span><br><span style="color: hsl(120, 100%, 40%);">+ optargs = ast_strdupa(pos);</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_STANDARD_APP_ARGS(args, optargs);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!ast_strlen_zero(args.timeout)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (sscanf(args.timeout, "%30lg", &timeout) != 1) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "Invalid timeout provided: %s. No timeout set.\n", args.timeout);</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ timeout_ms = timeout * 1000.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(args.interval)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (sscanf(args.interval, "%30lg", &poll) != 1) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "Invalid polling interval provided: %s. Default unchanged.\n", args.interval);</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (poll < 0.001) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "Polling interval cannot be less than 1ms. Default unchanged.\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ poll_ms = poll * 1000.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%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ for (i = 0; expression[i] != '\0'; i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (expression[i] == dollarsignrep) {</span><br><span style="color: hsl(120, 100%, 40%);">+ expression[i] = '$'; /* replace $s back into expression for variable parsing */</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 style="color: hsl(120, 100%, 40%);">+ if (timeout_ms > 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_debug(1, "Waiting for condition for %f seconds: %s (checking every %d ms)", timeout, expression, poll_ms);</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_debug(1, "Waiting for condition, forever: %s (checking every %d ms)", expression, poll_ms);</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%);">+ while (1) {</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Substitute variables now */</span><br><span style="color: hsl(120, 100%, 40%);">+ pbx_substitute_variables_helper(chan, expression, condition, sizeof(condition) - 1);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (pbx_checkcondition(condition)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ pbx_builtin_setvar_helper(chan, "WAITFORCONDITIONSTATUS", "TRUE");</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%);">+ /* If a timeout was specified, check that it hasn't expired */</span><br><span style="color: hsl(120, 100%, 40%);">+ if ((timeout_ms > 0) && !(ms = ast_remaining_ms(start, timeout_ms))) {</span><br><span style="color: hsl(120, 100%, 40%);">+ pbx_builtin_setvar_helper(chan, "WAITFORCONDITIONSTATUS", "TIMEOUT");</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%);">+ if (ast_safe_sleep(chan, poll_ms)) { /* don't waste CPU, we don't need a super tight loop */</span><br><span style="color: hsl(120, 100%, 40%);">+ pbx_builtin_setvar_helper(chan, "WAITFORCONDITIONSTATUS", "HANGUP");</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1; /* channel hung up */</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 style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int unload_module(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ return ast_unregister_application(app);</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%);">+static int load_module(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ return ast_register_application_xml(app, waitforcond_exec);</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%);">+AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "Wait until condition is true");</span><br><span>diff --git a/doc/CHANGES-staging/app_waitforcond.txt b/doc/CHANGES-staging/app_waitforcond.txt</span><br><span>new file mode 100644</span><br><span>index 0000000..a7ab600</span><br><span>--- /dev/null</span><br><span>+++ b/doc/CHANGES-staging/app_waitforcond.txt</span><br><span>@@ -0,0 +1,5 @@</span><br><span style="color: hsl(120, 100%, 40%);">+Subject: WaitForCondition application</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+This application provides a way to halt</span><br><span style="color: hsl(120, 100%, 40%);">+dialplan execution until a provided</span><br><span style="color: hsl(120, 100%, 40%);">+condition evaluates to true.</span><br><span></span><br></pre><div style="white-space:pre-wrap"></div><p>To view, visit <a href="https://gerrit.asterisk.org/c/asterisk/+/15928">change 15928</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/+/15928"/><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: I08adf2824b8bc63405778cf355963b5005612f41 </div>
<div style="display:none"> Gerrit-Change-Number: 15928 </div>
<div style="display:none"> Gerrit-PatchSet: 9 </div>
<div style="display:none"> Gerrit-Owner: N A <mail@interlinked.x10host.com> </div>
<div style="display:none"> Gerrit-Reviewer: Friendly Automation </div>
<div style="display:none"> Gerrit-Reviewer: George Joseph <gjoseph@digium.com> </div>
<div style="display:none"> Gerrit-Reviewer: Joshua Colp <jcolp@sangoma.com> </div>
<div style="display:none"> Gerrit-Reviewer: Kevin Harwell <kharwell@digium.com> </div>
<div style="display:none"> Gerrit-Reviewer: Richard Mudgett <rmudgett@digium.com> </div>
<div style="display:none"> Gerrit-MessageType: merged </div>