<p>N A has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.asterisk.org/c/asterisk/+/17719">View Change</a></p><pre style="font-family: monospace,monospace; white-space: pre-wrap;">pbx: Add CLI commands to warn of bad branches or audio playback<br><br>Adds two dialplan CLI commands to scan the dialplan preemptively<br>for common runtime problems.<br><br>'dialplan analyze fallthrough' looks through branching applications<br>for attempted branches to nonexistent dialplan locations.<br><br>'dialplan analyze audio' looks through audio applications<br>for attempted playback of nonexistent audio files.<br><br>These will not find all possible problems but any problems found<br>would cause an error at runtime. This allows users to preemptively<br>find these problems before they occur.<br><br>ASTERISK-29828 #close<br><br>Change-Id: Iaf7a2c8a6982ee5adaae7f7aa63e311d0e9106e3<br>---<br>A doc/CHANGES-staging/pbx-cli.txt<br>M main/pbx.c<br>2 files changed, 460 insertions(+), 0 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/19/17719/1</pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/doc/CHANGES-staging/pbx-cli.txt b/doc/CHANGES-staging/pbx-cli.txt</span><br><span>new file mode 100644</span><br><span>index 0000000..662d1d1</span><br><span>--- /dev/null</span><br><span>+++ b/doc/CHANGES-staging/pbx-cli.txt</span><br><span>@@ -0,0 +1,6 @@</span><br><span style="color: hsl(120, 100%, 40%);">+Subject: Add CLI commands to preemptively find runtime errors</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+Adds the 'dialplan analyze fallthrough' and 'dialplan analyze</span><br><span style="color: hsl(120, 100%, 40%);">+audio' commands to scan the dialplan for branches to</span><br><span style="color: hsl(120, 100%, 40%);">+nonexistent dialplan locations and attempted use of nonexistent</span><br><span style="color: hsl(120, 100%, 40%);">+audio files.</span><br><span>diff --git a/main/pbx.c b/main/pbx.c</span><br><span>index 07cf8e7..6bdb4df 100644</span><br><span>--- a/main/pbx.c</span><br><span>+++ b/main/pbx.c</span><br><span>@@ -5666,6 +5666,325 @@</span><br><span> return (dpc->total_exten == old_total_exten) ? -1 : res;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+static int pbx_parseable_location(char **label, char **context, char **exten, char **pri, int *ipri)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ *context = *exten = *pri = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Split context,exten,pri */</span><br><span style="color: hsl(120, 100%, 40%);">+ *context = strsep(label, ",");</span><br><span style="color: hsl(120, 100%, 40%);">+ *exten = strsep(label, ",");</span><br><span style="color: hsl(120, 100%, 40%);">+ *pri = strsep(label, ",");</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!*exten) {</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Only a priority in this one */</span><br><span style="color: hsl(120, 100%, 40%);">+ *pri = *context;</span><br><span style="color: hsl(120, 100%, 40%);">+ *exten = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ *context = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ } else if (!*pri) {</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Only an extension and priority in this one */</span><br><span style="color: hsl(120, 100%, 40%);">+ *pri = *exten;</span><br><span style="color: hsl(120, 100%, 40%);">+ *exten = *context;</span><br><span style="color: hsl(120, 100%, 40%);">+ *context = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ *ipri = atoi(*pri);</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%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \internal</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Checks if a dialplan location exists</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval 0 location exists</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval -1 location does not exist</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static int dialplan_location_exists(char *context, struct ast_context *c, char *exten, struct ast_exten *e, char *pri, int ipri)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!context) {</span><br><span style="color: hsl(120, 100%, 40%);">+ context = (char*) ast_get_context_name(c); /* assume current context */</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!exten) {</span><br><span style="color: hsl(120, 100%, 40%);">+ exten = (char*) ast_get_extension_name(e); /* assume current extension */</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_debug(8, "Scrutinizing %s:%d %s: %s,%s,%s\n", ast_get_extension_registrar_file(e), ast_get_extension_registrar_line(e),</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_get_extension_app(e), context, exten, pri);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ipri && ast_exists_extension(NULL, context, exten, ipri, e->matchcid == AST_EXT_MATCHCID_ON ? e->cidmatch : NULL)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ } else if (ast_findlabel_extension(NULL, context, exten, pri, e->matchcid == AST_EXT_MATCHCID_ON ? e->cidmatch : NULL) > 0) {</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%);">+ return -1;</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%);">+ * \internal</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief See if a dialplan extension change will fall through</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \note A successful return does not necessarily mean a fallthrough will not happen.</span><br><span style="color: hsl(120, 100%, 40%);">+ * This function should not return false positives, but there will be false negatives.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param c context</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param e exten</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval 0 indeterminate</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval -1 fallthrough</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static int dialplan_find_fallthrough(struct ast_context *c, struct ast_exten *e)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ char *context, *exten, *pri;</span><br><span style="color: hsl(120, 100%, 40%);">+ char *data, *sep;</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *app = ast_get_extension_app(e);</span><br><span style="color: hsl(120, 100%, 40%);">+ int ipri;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (strcasecmp(app, "Goto") && strcasecmp(app, "GotoIf") && strcasecmp(app, "Gosub") && strcasecmp(app, "GosubIf")) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0; /* no location change */</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ /* in theory, ChannelRedirect is another easy one to handle, but 99.99% of the time, channel will be a variable... */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ data = ast_strdupa(ast_get_extension_app_data(e));</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_strlen_zero(data)) {</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 (!strcasecmp(app, "Goto")) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (strchr(data, '$')) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0; /* if location contains a variable, there's no real way to know at "compile" time, gotta hope for the best at runtime... */</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ pbx_parseable_location(&data, &context, &exten, &pri, &ipri);</span><br><span style="color: hsl(120, 100%, 40%);">+ return dialplan_location_exists(context, c, exten, e, pri, ipri);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!strcasecmp(app, "GotoIf")) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (strchr(data, '$')) {</span><br><span style="color: hsl(120, 100%, 40%);">+ /* in theory, could parse the condition and each branch separately, but we'd need to find the right ? and :</span><br><span style="color: hsl(120, 100%, 40%);">+ This is more difficult than actual GotoIf/GosubIf because data isn't variable-substituted at all in advance,</span><br><span style="color: hsl(120, 100%, 40%);">+ nor can we substitute it. */</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%);">+ data = strchr(data, '?');</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!data) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1; /* this shouldn't happen, so *something* is wrong... */</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ data++;</span><br><span style="color: hsl(120, 100%, 40%);">+ /* because we already bailed out early if a $ was detected, it is guaranteed that the first : will be the separator */</span><br><span style="color: hsl(120, 100%, 40%);">+ sep = strchr(data, ':');</span><br><span style="color: hsl(120, 100%, 40%);">+ if (sep) {</span><br><span style="color: hsl(120, 100%, 40%);">+ sep[0] = '\0';</span><br><span style="color: hsl(120, 100%, 40%);">+ sep++;</span><br><span style="color: hsl(120, 100%, 40%);">+ if (*sep) { /* make sure this branch contains data */</span><br><span style="color: hsl(120, 100%, 40%);">+ pbx_parseable_location(&sep, &context, &exten, &pri, &ipri);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (dialplan_location_exists(context, c, exten, e, pri, ipri)) {</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%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!*data) { /* make sure this branch contains data */</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0; /* if location contains a variable, there's no real way to know at "compile" time, gotta hope for the best at runtime... */</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ pbx_parseable_location(&data, &context, &exten, &pri, &ipri);</span><br><span style="color: hsl(120, 100%, 40%);">+ return dialplan_location_exists(context, c, exten, e, pri, ipri);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!strcasecmp(app, "Gosub")) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (strchr(data, '$')) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0; /* if location contains a variable, there's no real way to know at "compile" time, gotta hope for the best at runtime... */</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ sep = strchr(data, '('); /* start of arguments */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (sep) {</span><br><span style="color: hsl(120, 100%, 40%);">+ sep[0] = '\0'; /* discard Gosub arguments */</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ pbx_parseable_location(&data, &context, &exten, &pri, &ipri);</span><br><span style="color: hsl(120, 100%, 40%);">+ return dialplan_location_exists(context, c, exten, e, pri, ipri);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!strcasecmp(app, "GosubIf")) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (strchr(data, '$')) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0; /* in theory, could parse the condition and each branch separately, but we'd need to find the right ? and : */</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ data = strchr(data, '?');</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!data) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1; /* this shouldn't happen, so *something* is wrong... */</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ data++;</span><br><span style="color: hsl(120, 100%, 40%);">+ /* because we already bailed out early if a $ was detected, it is guaranteed that the first : will be the separator */</span><br><span style="color: hsl(120, 100%, 40%);">+ sep = strchr(data, ':');</span><br><span style="color: hsl(120, 100%, 40%);">+ if (sep) {</span><br><span style="color: hsl(120, 100%, 40%);">+ sep[0] = '\0';</span><br><span style="color: hsl(120, 100%, 40%);">+ sep++;</span><br><span style="color: hsl(120, 100%, 40%);">+ if (*sep) { /* make sure this branch contains data */</span><br><span style="color: hsl(120, 100%, 40%);">+ char *argstart = strchr(data, '(');</span><br><span style="color: hsl(120, 100%, 40%);">+ if (argstart) {</span><br><span style="color: hsl(120, 100%, 40%);">+ argstart[0] = '\0'; /* discard Gosub arguments */</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ pbx_parseable_location(&sep, &context, &exten, &pri, &ipri);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (dialplan_location_exists(context, c, exten, e, pri, ipri)) {</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%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!*data) { /* make sure this branch contains data */</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0; /* if location contains a variable, there's no real way to know at "compile" time, gotta hope for the best at runtime... */</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ sep = strchr(data, '('); /* start of arguments */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (sep) {</span><br><span style="color: hsl(120, 100%, 40%);">+ sep[0] = '\0'; /* discard Gosub arguments */</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ pbx_parseable_location(&data, &context, &exten, &pri, &ipri);</span><br><span style="color: hsl(120, 100%, 40%);">+ return dialplan_location_exists(context, c, exten, e, pri, ipri);</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 style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int dialplan_find_fallthrough_callback(struct ast_context *c, struct ast_exten *e)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ if (dialplan_find_fallthrough(c, e)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "%s:%d: %s(%s) will fail (branch to nonexistent location)\n", ast_get_extension_registrar_file(e),</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_get_extension_registrar_line(e), ast_get_extension_app(e), (char*) ast_get_extension_app_data(e));</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0; /* always return 0, so we always crawl every priority in the dialplan */</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 dialplan_find_missing_audio_callback(struct ast_context *c, struct ast_exten *e)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *app;</span><br><span style="color: hsl(120, 100%, 40%);">+ char *data;</span><br><span style="color: hsl(120, 100%, 40%);">+ char *cur, *audiodata;</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(audio);</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_APP_ARG(audio2);</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_APP_ARG(digits);</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_APP_ARG(opts);</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_APP_ARG(rest);</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%);">+ app = ast_get_extension_app(e);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (strcasecmp(app, "Playback") && strcasecmp(app, "ControlPlayback") && strcasecmp(app, "Background") && strcasecmp(app, "BackgroundDetect") && strcasecmp(app, "Read") && strcasecmp(app, "ReadExten")) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0; /* not relevant to this callback */</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%);">+ data = ast_strdupa(ast_get_extension_app_data(e));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_STANDARD_APP_ARGS(args, data);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_strlen_zero(args.audio)) { /* arg1 is not audio for Read/ReadExten, but regardless it IS a mandatory arg so this is NEVER valid */</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "%s:%d: %s(%s) missing mandatory argument\n", ast_get_extension_registrar_file(e),</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_get_extension_registrar_line(e), ast_get_extension_app(e), (char*) ast_get_extension_app_data(e));</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 (!strcasecmp(app, "Playback") || !strcasecmp(app, "ControlPlayback") || !strcasecmp(app, "Background") || !strcasecmp(app, "BackgroundDetect")) {</span><br><span style="color: hsl(120, 100%, 40%);">+ audiodata = args.audio;</span><br><span style="color: hsl(120, 100%, 40%);">+ /* by delaying this check until here, we will be able to parse more things since succeeding parameters with variables are discarded first */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (strchr(audiodata, '$')) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0; /* ignore audio args with variables, hope for the best at runtime... */</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ } else { /* Read/ReadExten */</span><br><span style="color: hsl(120, 100%, 40%);">+ audiodata = args.audio2;</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_strlen_zero(audiodata) || strchr(audiodata, '$') || (args.audio && strchr(args.audio, '$'))) { /* if previous args contained vars, that could mess up our primitive parsing */</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0; /* ignore audio args with variables, hope for the best at runtime... */</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%);">+ while ((cur = strsep(&audiodata, "&"))) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!strncmp(cur, "/tmp/", 5)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ continue; /* ignore tmp files, for obvious reasons... */</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_fileexists(cur, NULL, NULL)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ continue;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (args.opts && strchr(args.opts, 'i')) {</span><br><span style="color: hsl(120, 100%, 40%);">+ continue; /* ignore indications tone names for Read/ReadExten */</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "%s:%d: %s(%s) will fail (audio file '%s' does not exist)\n", ast_get_extension_registrar_file(e),</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_get_extension_registrar_line(e), ast_get_extension_app(e), (char*) ast_get_extension_app_data(e), cur);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0; /* always return 0, so we always crawl every priority in the dialplan */</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 dialplan_crawl_helper(int fd, const char *context, const char *exten, struct dialplan_counters *dpc, const struct ast_include *rinclude, int includecount, const char *includes[], int (*crawl_callback)(struct ast_context *c, struct ast_exten *e))</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_context *c = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ int res = 0, old_total_exten = dpc->total_exten;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_rdlock_contexts();</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* walk all contexts ... */</span><br><span style="color: hsl(120, 100%, 40%);">+ while ( (c = ast_walk_contexts(c)) ) {</span><br><span style="color: hsl(120, 100%, 40%);">+ int idx;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_exten *e;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (context && strcmp(ast_get_context_name(c), context)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ continue; /* skip this one, name doesn't match */</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ dpc->context_existence = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_rdlock_context(c);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!exten) {</span><br><span style="color: hsl(120, 100%, 40%);">+ dpc->total_context++;</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%);">+ /* walk extensions ... */</span><br><span style="color: hsl(120, 100%, 40%);">+ e = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ while ( (e = ast_walk_context_extensions(c, e)) ) {</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_exten *p;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (exten && !ast_extension_match(ast_get_extension_name(e), exten)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ continue; /* skip, extension match failed */</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ dpc->extension_existence = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+ dpc->total_prio++;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (crawl_callback(c, e)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ continue;</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%);">+ dpc->total_exten++;</span><br><span style="color: hsl(120, 100%, 40%);">+ /* walk next extension peers */</span><br><span style="color: hsl(120, 100%, 40%);">+ p = e; /* skip the first one, we already got it */</span><br><span style="color: hsl(120, 100%, 40%);">+ while ( (p = ast_walk_extension_priorities(e, p)) ) {</span><br><span style="color: hsl(120, 100%, 40%);">+ dpc->total_prio++;</span><br><span style="color: hsl(120, 100%, 40%);">+ if (crawl_callback(c, p)) {</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%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ for (idx = 0; idx < ast_context_includes_count(c); idx++) {</span><br><span style="color: hsl(120, 100%, 40%);">+ const struct ast_include *i = ast_context_includes_get(c, idx);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (exten) {</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Check all includes for the requested extension */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (includecount >= AST_PBX_MAX_STACK) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "Maximum include depth exceeded!\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ int dupe = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ int x;</span><br><span style="color: hsl(120, 100%, 40%);">+ for (x = 0; x < includecount; x++) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!strcasecmp(includes[x], ast_get_include_name(i))) {</span><br><span style="color: hsl(120, 100%, 40%);">+ dupe++;</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%);">+ if (!dupe) {</span><br><span style="color: hsl(120, 100%, 40%);">+ includes[includecount] = ast_get_include_name(i);</span><br><span style="color: hsl(120, 100%, 40%);">+ dialplan_crawl_helper(fd, ast_get_include_name(i), exten, dpc, i, includecount + 1, includes, crawl_callback);</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "Avoiding circular include of %s within %s\n", ast_get_include_name(i), context);</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%);">+ ast_unlock_context(c);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_unlock_contexts();</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return (dpc->total_exten == old_total_exten) ? -1 : 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%);">+static int dialplan_analyze_fallthroughs(int fd, const char *context, const char *exten, struct dialplan_counters *dpc, const struct ast_include *rinclude, int includecount, const char *includes[])</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ return dialplan_crawl_helper(fd, context, exten, dpc, rinclude, includecount, includes, dialplan_find_fallthrough_callback);</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 dialplan_analyze_audio(int fd, const char *context, const char *exten, struct dialplan_counters *dpc, const struct ast_include *rinclude, int includecount, const char *includes[])</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ return dialplan_crawl_helper(fd, context, exten, dpc, rinclude, includecount, includes, dialplan_find_missing_audio_callback);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> static int show_debug_helper(int fd, const char *context, const char *exten, struct dialplan_counters *dpc, struct ast_include *rinclude, int includecount, const char *includes[])</span><br><span> {</span><br><span> struct ast_context *c = NULL;</span><br><span>@@ -5787,6 +6106,138 @@</span><br><span> return CLI_SUCCESS;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+static char *handle_analyze_fallthrough(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ char *exten = NULL, *context = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Variables used for different counters */</span><br><span style="color: hsl(120, 100%, 40%);">+ struct dialplan_counters counters;</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *incstack[AST_PBX_MAX_STACK];</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ switch (cmd) {</span><br><span style="color: hsl(120, 100%, 40%);">+ case CLI_INIT:</span><br><span style="color: hsl(120, 100%, 40%);">+ e->command = "dialplan analyze fallthrough";</span><br><span style="color: hsl(120, 100%, 40%);">+ e->usage =</span><br><span style="color: hsl(120, 100%, 40%);">+ "Usage: dialplan analyze fallthrough [[exten@]context]\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ " Analyzes dialplan for extension fallthroughs\n";</span><br><span style="color: hsl(120, 100%, 40%);">+ return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ case CLI_GENERATE:</span><br><span style="color: hsl(120, 100%, 40%);">+ return complete_show_dialplan_context(a->line, a->word, a->pos, a->n);</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%);">+ memset(&counters, 0, sizeof(counters));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (a->argc != 3 && a->argc != 4)</span><br><span style="color: hsl(120, 100%, 40%);">+ return CLI_SHOWUSAGE;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* we obtain [exten@]context? if yes, split them ... */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (a->argc == 4) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (strchr(a->argv[3], '@')) { /* split into exten & context */</span><br><span style="color: hsl(120, 100%, 40%);">+ context = ast_strdupa(a->argv[3]);</span><br><span style="color: hsl(120, 100%, 40%);">+ exten = strsep(&context, "@");</span><br><span style="color: hsl(120, 100%, 40%);">+ /* change empty strings to NULL */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_strlen_zero(exten))</span><br><span style="color: hsl(120, 100%, 40%);">+ exten = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ } else { /* no '@' char, only context given */</span><br><span style="color: hsl(120, 100%, 40%);">+ context = ast_strdupa(a->argv[3]);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_strlen_zero(context))</span><br><span style="color: hsl(120, 100%, 40%);">+ context = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ /* else Show complete dial plan, context and exten are NULL */</span><br><span style="color: hsl(120, 100%, 40%);">+ dialplan_analyze_fallthroughs(a->fd, context, exten, &counters, NULL, 0, incstack);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* check for input failure and throw some error messages */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (context && !counters.context_existence) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_cli(a->fd, "There is no existence of '%s' context\n", context);</span><br><span style="color: hsl(120, 100%, 40%);">+ return CLI_FAILURE;</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 (exten && !counters.extension_existence) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (context)</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_cli(a->fd, "There is no existence of %s@%s extension\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ exten, context);</span><br><span style="color: hsl(120, 100%, 40%);">+ else</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_cli(a->fd,</span><br><span style="color: hsl(120, 100%, 40%);">+ "There is no existence of '%s' extension in all contexts\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ exten);</span><br><span style="color: hsl(120, 100%, 40%);">+ return CLI_FAILURE;</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_cli(a->fd,"-= %d %s (%d %s) in %d %s. =-\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ counters.total_exten, counters.total_exten == 1 ? "extension" : "extensions",</span><br><span style="color: hsl(120, 100%, 40%);">+ counters.total_prio, counters.total_prio == 1 ? "priority" : "priorities",</span><br><span style="color: hsl(120, 100%, 40%);">+ counters.total_context, counters.total_context == 1 ? "context" : "contexts");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* everything ok */</span><br><span style="color: hsl(120, 100%, 40%);">+ return CLI_SUCCESS;</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 *handle_analyze_audio(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ char *exten = NULL, *context = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Variables used for different counters */</span><br><span style="color: hsl(120, 100%, 40%);">+ struct dialplan_counters counters;</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *incstack[AST_PBX_MAX_STACK];</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ switch (cmd) {</span><br><span style="color: hsl(120, 100%, 40%);">+ case CLI_INIT:</span><br><span style="color: hsl(120, 100%, 40%);">+ e->command = "dialplan analyze audio";</span><br><span style="color: hsl(120, 100%, 40%);">+ e->usage =</span><br><span style="color: hsl(120, 100%, 40%);">+ "Usage: dialplan analyze audio [[exten@]context]\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ " Analyzes dialplan for missing audio files\n";</span><br><span style="color: hsl(120, 100%, 40%);">+ return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ case CLI_GENERATE:</span><br><span style="color: hsl(120, 100%, 40%);">+ return complete_show_dialplan_context(a->line, a->word, a->pos, a->n);</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%);">+ memset(&counters, 0, sizeof(counters));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (a->argc != 3 && a->argc != 4)</span><br><span style="color: hsl(120, 100%, 40%);">+ return CLI_SHOWUSAGE;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* we obtain [exten@]context? if yes, split them ... */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (a->argc == 4) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (strchr(a->argv[3], '@')) { /* split into exten & context */</span><br><span style="color: hsl(120, 100%, 40%);">+ context = ast_strdupa(a->argv[3]);</span><br><span style="color: hsl(120, 100%, 40%);">+ exten = strsep(&context, "@");</span><br><span style="color: hsl(120, 100%, 40%);">+ /* change empty strings to NULL */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_strlen_zero(exten))</span><br><span style="color: hsl(120, 100%, 40%);">+ exten = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ } else { /* no '@' char, only context given */</span><br><span style="color: hsl(120, 100%, 40%);">+ context = ast_strdupa(a->argv[3]);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_strlen_zero(context))</span><br><span style="color: hsl(120, 100%, 40%);">+ context = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ /* else Show complete dial plan, context and exten are NULL */</span><br><span style="color: hsl(120, 100%, 40%);">+ dialplan_analyze_audio(a->fd, context, exten, &counters, NULL, 0, incstack);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* check for input failure and throw some error messages */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (context && !counters.context_existence) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_cli(a->fd, "There is no existence of '%s' context\n", context);</span><br><span style="color: hsl(120, 100%, 40%);">+ return CLI_FAILURE;</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 (exten && !counters.extension_existence) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (context)</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_cli(a->fd, "There is no existence of %s@%s extension\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ exten, context);</span><br><span style="color: hsl(120, 100%, 40%);">+ else</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_cli(a->fd,</span><br><span style="color: hsl(120, 100%, 40%);">+ "There is no existence of '%s' extension in all contexts\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ exten);</span><br><span style="color: hsl(120, 100%, 40%);">+ return CLI_FAILURE;</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_cli(a->fd,"-= %d %s (%d %s) in %d %s. =-\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ counters.total_exten, counters.total_exten == 1 ? "extension" : "extensions",</span><br><span style="color: hsl(120, 100%, 40%);">+ counters.total_prio, counters.total_prio == 1 ? "priority" : "priorities",</span><br><span style="color: hsl(120, 100%, 40%);">+ counters.total_context, counters.total_context == 1 ? "context" : "contexts");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* everything ok */</span><br><span style="color: hsl(120, 100%, 40%);">+ return CLI_SUCCESS;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /*! \brief Send ack once */</span><br><span> static char *handle_debug_dialplan(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)</span><br><span> {</span><br><span>@@ -6163,6 +6614,8 @@</span><br><span> AST_CLI_DEFINE(handle_show_device2extenstate, "Show expected exten state from multiple device states"),</span><br><span> #endif</span><br><span> AST_CLI_DEFINE(handle_show_dialplan, "Show dialplan"),</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_CLI_DEFINE(handle_analyze_fallthrough, "Analyzes dialplan for extension fallthroughs"),</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_CLI_DEFINE(handle_analyze_audio, "Analyzes dialplan for missing referenced audio files"),</span><br><span> AST_CLI_DEFINE(handle_debug_dialplan, "Show fast extension pattern matching data structures"),</span><br><span> AST_CLI_DEFINE(handle_unset_extenpatternmatchnew, "Use the Old extension pattern matching algorithm."),</span><br><span> AST_CLI_DEFINE(handle_set_extenpatternmatchnew, "Use the New extension pattern matching algorithm."),</span><br><span>@@ -9033,3 +9486,4 @@</span><br><span> </span><br><span> return (hints && hintdevices && autohints && statecbs) ? 0 : -1;</span><br><span> }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.asterisk.org/c/asterisk/+/17719">change 17719</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/+/17719"/><meta itemprop="name" content="View Change"/></div></div>
<div style="display:none"> Gerrit-Project: asterisk </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-Change-Id: Iaf7a2c8a6982ee5adaae7f7aa63e311d0e9106e3 </div>
<div style="display:none"> Gerrit-Change-Number: 17719 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: N A <mail@interlinked.x10host.com> </div>
<div style="display:none"> Gerrit-MessageType: newchange </div>