<p>sungtae kim has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.asterisk.org/c/asterisk/+/11211">View Change</a></p><pre style="font-family: monospace,monospace; white-space: pre-wrap;">res_stasis.c: Added POST channels/{channelId}/application ARI<br><br>This ARI allows to execute the application within a stasis application.<br><br>Added ARI events for status notifination.<br>ChannelApplicationStarted: Notification that a application has been started.<br>ChannelApplicationFinished: Notification that a application has been finished.<br><br>ASTERISK-28365<br><br>Change-Id: I037bcb9fda54563fdcd2197a5cbda642fdec70e8<br>---<br>M include/asterisk/pbx.h<br>M include/asterisk/stasis_app.h<br>M include/asterisk/stasis_app_impl.h<br>M main/pbx_app.c<br>M res/ari/ari_model_validators.c<br>M res/ari/ari_model_validators.h<br>M res/ari/resource_channels.c<br>M res/ari/resource_channels.h<br>M res/res_ari_channels.c<br>M res/res_stasis.c<br>M res/stasis/control.c<br>M rest-api/api-docs/channels.json<br>M rest-api/api-docs/events.json<br>13 files changed, 823 insertions(+), 3 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/11/11211/1</pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/include/asterisk/pbx.h b/include/asterisk/pbx.h</span><br><span>index cbc5ec2..633a933 100644</span><br><span>--- a/include/asterisk/pbx.h</span><br><span>+++ b/include/asterisk/pbx.h</span><br><span>@@ -267,6 +267,8 @@</span><br><span>  */</span><br><span> int pbx_exec(struct ast_channel *c, struct ast_app *app, const char *data);</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+int pbx_exec_app(struct ast_channel *c, struct ast_app *app, const char *data);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /*!</span><br><span>  * \brief Register a new context or find an existing one</span><br><span>  *</span><br><span>diff --git a/include/asterisk/stasis_app.h b/include/asterisk/stasis_app.h</span><br><span>index 01c7ff4..a9bc68a 100644</span><br><span>--- a/include/asterisk/stasis_app.h</span><br><span>+++ b/include/asterisk/stasis_app.h</span><br><span>@@ -1018,6 +1018,7 @@</span><br><span>  */</span><br><span> int stasis_app_event_allowed(const char *app_name, struct ast_json *event);</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+int stasis_app_control_channel_app(struct stasis_app_control *control, const char *app_name, const char *app_args);</span><br><span> /*! @} */</span><br><span> </span><br><span> #endif /* _ASTERISK_STASIS_APP_H */</span><br><span>diff --git a/include/asterisk/stasis_app_impl.h b/include/asterisk/stasis_app_impl.h</span><br><span>index ce60804..6fce853 100644</span><br><span>--- a/include/asterisk/stasis_app_impl.h</span><br><span>+++ b/include/asterisk/stasis_app_impl.h</span><br><span>@@ -105,4 +105,9 @@</span><br><span> int stasis_app_send_command_async(struct stasis_app_control *control,</span><br><span>         stasis_app_command_cb command, void *data, command_data_destructor_fn data_destructor);</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int publish_channel_application_started_event(struct ast_channel *chan, const char* app, const char* app_args);</span><br><span style="color: hsl(120, 100%, 40%);">+int publish_channel_application_finished_event(struct ast_channel *chan, const char* app, const char* app_args, const char* status);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> #endif /* _ASTERISK_RES_STASIS_H */</span><br><span>diff --git a/main/pbx_app.c b/main/pbx_app.c</span><br><span>index df8126c..bf3c811 100644</span><br><span>--- a/main/pbx_app.c</span><br><span>+++ b/main/pbx_app.c</span><br><span>@@ -498,6 +498,25 @@</span><br><span>  return res;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+int pbx_exec_app(struct ast_channel *c,   /*!< Channel */</span><br><span style="color: hsl(120, 100%, 40%);">+         struct ast_app *app,       /*!< Application */</span><br><span style="color: hsl(120, 100%, 40%);">+             const char *data)          /*!< Data for execution */</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      int ret;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    if (app->module) {</span><br><span style="color: hsl(120, 100%, 40%);">+         ast_module_ref(app->module);</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%);">+   ret = app->execute(c, S_OR(data, ""));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (app->module) {</span><br><span style="color: hsl(120, 100%, 40%);">+         ast_module_unref(app->module);</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 ret;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> static struct ast_cli_entry app_cli[] = {</span><br><span>        AST_CLI_DEFINE(handle_show_applications, "Shows registered dialplan applications"),</span><br><span>        AST_CLI_DEFINE(handle_show_application, "Describe a specific dialplan application"),</span><br><span>diff --git a/res/ari/ari_model_validators.c b/res/ari/ari_model_validators.c</span><br><span>index bc7ac6b..0ee21c1 100644</span><br><span>--- a/res/ari/ari_model_validators.c</span><br><span>+++ b/res/ari/ari_model_validators.c</span><br><span>@@ -1261,6 +1261,76 @@</span><br><span>         return ast_ari_validate_channel;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+int ast_ari_validate_channel_application(struct ast_json *json)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ int res = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+  struct ast_json_iter *iter;</span><br><span style="color: hsl(120, 100%, 40%);">+   int has_args = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     int has_name = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     int has_status = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {</span><br><span style="color: hsl(120, 100%, 40%);">+         if (strcmp("args", ast_json_object_iter_key(iter)) == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                  int prop_is_valid;</span><br><span style="color: hsl(120, 100%, 40%);">+                    has_args = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+                 prop_is_valid = ast_ari_validate_string(</span><br><span style="color: hsl(120, 100%, 40%);">+                              ast_json_object_iter_value(iter));</span><br><span style="color: hsl(120, 100%, 40%);">+                    if (!prop_is_valid) {</span><br><span style="color: hsl(120, 100%, 40%);">+                         ast_log(LOG_ERROR, "ARI ChannelApplication field args failed validation\n");</span><br><span style="color: hsl(120, 100%, 40%);">+                                res = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+                      }</span><br><span style="color: hsl(120, 100%, 40%);">+             } else</span><br><span style="color: hsl(120, 100%, 40%);">+                if (strcmp("name", ast_json_object_iter_key(iter)) == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                  int prop_is_valid;</span><br><span style="color: hsl(120, 100%, 40%);">+                    has_name = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+                 prop_is_valid = ast_ari_validate_string(</span><br><span style="color: hsl(120, 100%, 40%);">+                              ast_json_object_iter_value(iter));</span><br><span style="color: hsl(120, 100%, 40%);">+                    if (!prop_is_valid) {</span><br><span style="color: hsl(120, 100%, 40%);">+                         ast_log(LOG_ERROR, "ARI ChannelApplication field name failed validation\n");</span><br><span style="color: hsl(120, 100%, 40%);">+                                res = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+                      }</span><br><span style="color: hsl(120, 100%, 40%);">+             } else</span><br><span style="color: hsl(120, 100%, 40%);">+                if (strcmp("status", ast_json_object_iter_key(iter)) == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                        int prop_is_valid;</span><br><span style="color: hsl(120, 100%, 40%);">+                    has_status = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+                       prop_is_valid = ast_ari_validate_string(</span><br><span style="color: hsl(120, 100%, 40%);">+                              ast_json_object_iter_value(iter));</span><br><span style="color: hsl(120, 100%, 40%);">+                    if (!prop_is_valid) {</span><br><span style="color: hsl(120, 100%, 40%);">+                         ast_log(LOG_ERROR, "ARI ChannelApplication field status failed validation\n");</span><br><span style="color: hsl(120, 100%, 40%);">+                              res = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+                      }</span><br><span style="color: hsl(120, 100%, 40%);">+             } else</span><br><span style="color: hsl(120, 100%, 40%);">+                {</span><br><span style="color: hsl(120, 100%, 40%);">+                     ast_log(LOG_ERROR,</span><br><span style="color: hsl(120, 100%, 40%);">+                            "ARI ChannelApplication has undocumented field %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                               ast_json_object_iter_key(iter));</span><br><span style="color: hsl(120, 100%, 40%);">+                      res = 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%);">+   if (!has_args) {</span><br><span style="color: hsl(120, 100%, 40%);">+              ast_log(LOG_ERROR, "ARI ChannelApplication missing required field args\n");</span><br><span style="color: hsl(120, 100%, 40%);">+         res = 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 (!has_name) {</span><br><span style="color: hsl(120, 100%, 40%);">+              ast_log(LOG_ERROR, "ARI ChannelApplication missing required field name\n");</span><br><span style="color: hsl(120, 100%, 40%);">+         res = 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 (!has_status) {</span><br><span style="color: hsl(120, 100%, 40%);">+            ast_log(LOG_ERROR, "ARI ChannelApplication missing required field status\n");</span><br><span style="color: hsl(120, 100%, 40%);">+               res = 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%);">+   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%);">+ari_validator ast_ari_validate_channel_application_fn(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      return ast_ari_validate_channel_application;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> int ast_ari_validate_dialed(struct ast_json *json)</span><br><span> {</span><br><span>         int res = 1;</span><br><span>@@ -2050,6 +2120,7 @@</span><br><span>         struct ast_json_iter *iter;</span><br><span>  int has_type = 0;</span><br><span>    int has_application = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+      int has_timestamp = 0;</span><br><span>       int has_args = 0;</span><br><span>    int has_channel = 0;</span><br><span>         int has_destination = 0;</span><br><span>@@ -2086,6 +2157,7 @@</span><br><span>             } else</span><br><span>               if (strcmp("timestamp", ast_json_object_iter_key(iter)) == 0) {</span><br><span>                    int prop_is_valid;</span><br><span style="color: hsl(120, 100%, 40%);">+                    has_timestamp = 1;</span><br><span>                   prop_is_valid = ast_ari_validate_date(</span><br><span>                               ast_json_object_iter_value(iter));</span><br><span>                   if (!prop_is_valid) {</span><br><span>@@ -2142,6 +2214,11 @@</span><br><span>               res = 0;</span><br><span>     }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ if (!has_timestamp) {</span><br><span style="color: hsl(120, 100%, 40%);">+         ast_log(LOG_ERROR, "ARI ApplicationMoveFailed missing required field timestamp\n");</span><br><span style="color: hsl(120, 100%, 40%);">+         res = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+      }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>  if (!has_args) {</span><br><span>             ast_log(LOG_ERROR, "ARI ApplicationMoveFailed missing required field args\n");</span><br><span>             res = 0;</span><br><span>@@ -3093,6 +3170,214 @@</span><br><span>   return ast_ari_validate_bridge_video_source_changed;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+int ast_ari_validate_channel_application_finished(struct ast_json *json)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    int res = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+  struct ast_json_iter *iter;</span><br><span style="color: hsl(120, 100%, 40%);">+   int has_type = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     int has_application = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+      int has_timestamp = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+        int has_channel = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {</span><br><span style="color: hsl(120, 100%, 40%);">+         if (strcmp("asterisk_id", ast_json_object_iter_key(iter)) == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                   int prop_is_valid;</span><br><span style="color: hsl(120, 100%, 40%);">+                    prop_is_valid = ast_ari_validate_string(</span><br><span style="color: hsl(120, 100%, 40%);">+                              ast_json_object_iter_value(iter));</span><br><span style="color: hsl(120, 100%, 40%);">+                    if (!prop_is_valid) {</span><br><span style="color: hsl(120, 100%, 40%);">+                         ast_log(LOG_ERROR, "ARI ChannelApplicationFinished field asterisk_id failed validation\n");</span><br><span style="color: hsl(120, 100%, 40%);">+                         res = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+                      }</span><br><span style="color: hsl(120, 100%, 40%);">+             } else</span><br><span style="color: hsl(120, 100%, 40%);">+                if (strcmp("type", ast_json_object_iter_key(iter)) == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                  int prop_is_valid;</span><br><span style="color: hsl(120, 100%, 40%);">+                    has_type = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+                 prop_is_valid = ast_ari_validate_string(</span><br><span style="color: hsl(120, 100%, 40%);">+                              ast_json_object_iter_value(iter));</span><br><span style="color: hsl(120, 100%, 40%);">+                    if (!prop_is_valid) {</span><br><span style="color: hsl(120, 100%, 40%);">+                         ast_log(LOG_ERROR, "ARI ChannelApplicationFinished field type failed validation\n");</span><br><span style="color: hsl(120, 100%, 40%);">+                                res = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+                      }</span><br><span style="color: hsl(120, 100%, 40%);">+             } else</span><br><span style="color: hsl(120, 100%, 40%);">+                if (strcmp("application", ast_json_object_iter_key(iter)) == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                   int prop_is_valid;</span><br><span style="color: hsl(120, 100%, 40%);">+                    has_application = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+                  prop_is_valid = ast_ari_validate_string(</span><br><span style="color: hsl(120, 100%, 40%);">+                              ast_json_object_iter_value(iter));</span><br><span style="color: hsl(120, 100%, 40%);">+                    if (!prop_is_valid) {</span><br><span style="color: hsl(120, 100%, 40%);">+                         ast_log(LOG_ERROR, "ARI ChannelApplicationFinished field application failed validation\n");</span><br><span style="color: hsl(120, 100%, 40%);">+                         res = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+                      }</span><br><span style="color: hsl(120, 100%, 40%);">+             } else</span><br><span style="color: hsl(120, 100%, 40%);">+                if (strcmp("timestamp", ast_json_object_iter_key(iter)) == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                     int prop_is_valid;</span><br><span style="color: hsl(120, 100%, 40%);">+                    has_timestamp = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+                    prop_is_valid = ast_ari_validate_date(</span><br><span style="color: hsl(120, 100%, 40%);">+                                ast_json_object_iter_value(iter));</span><br><span style="color: hsl(120, 100%, 40%);">+                    if (!prop_is_valid) {</span><br><span style="color: hsl(120, 100%, 40%);">+                         ast_log(LOG_ERROR, "ARI ChannelApplicationFinished field timestamp failed validation\n");</span><br><span style="color: hsl(120, 100%, 40%);">+                           res = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+                      }</span><br><span style="color: hsl(120, 100%, 40%);">+             } else</span><br><span style="color: hsl(120, 100%, 40%);">+                if (strcmp("channel", ast_json_object_iter_key(iter)) == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                       int prop_is_valid;</span><br><span style="color: hsl(120, 100%, 40%);">+                    has_channel = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+                      prop_is_valid = ast_ari_validate_channel(</span><br><span style="color: hsl(120, 100%, 40%);">+                             ast_json_object_iter_value(iter));</span><br><span style="color: hsl(120, 100%, 40%);">+                    if (!prop_is_valid) {</span><br><span style="color: hsl(120, 100%, 40%);">+                         ast_log(LOG_ERROR, "ARI ChannelApplicationFinished field channel failed validation\n");</span><br><span style="color: hsl(120, 100%, 40%);">+                             res = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+                      }</span><br><span style="color: hsl(120, 100%, 40%);">+             } else</span><br><span style="color: hsl(120, 100%, 40%);">+                if (strcmp("channel_application", ast_json_object_iter_key(iter)) == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                   int prop_is_valid;</span><br><span style="color: hsl(120, 100%, 40%);">+                    prop_is_valid = ast_ari_validate_channel_application(</span><br><span style="color: hsl(120, 100%, 40%);">+                         ast_json_object_iter_value(iter));</span><br><span style="color: hsl(120, 100%, 40%);">+                    if (!prop_is_valid) {</span><br><span style="color: hsl(120, 100%, 40%);">+                         ast_log(LOG_ERROR, "ARI ChannelApplicationFinished field channel_application failed validation\n");</span><br><span style="color: hsl(120, 100%, 40%);">+                         res = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+                      }</span><br><span style="color: hsl(120, 100%, 40%);">+             } else</span><br><span style="color: hsl(120, 100%, 40%);">+                {</span><br><span style="color: hsl(120, 100%, 40%);">+                     ast_log(LOG_ERROR,</span><br><span style="color: hsl(120, 100%, 40%);">+                            "ARI ChannelApplicationFinished has undocumented field %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                               ast_json_object_iter_key(iter));</span><br><span style="color: hsl(120, 100%, 40%);">+                      res = 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%);">+   if (!has_type) {</span><br><span style="color: hsl(120, 100%, 40%);">+              ast_log(LOG_ERROR, "ARI ChannelApplicationFinished missing required field type\n");</span><br><span style="color: hsl(120, 100%, 40%);">+         res = 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 (!has_application) {</span><br><span style="color: hsl(120, 100%, 40%);">+               ast_log(LOG_ERROR, "ARI ChannelApplicationFinished missing required field application\n");</span><br><span style="color: hsl(120, 100%, 40%);">+          res = 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 (!has_timestamp) {</span><br><span style="color: hsl(120, 100%, 40%);">+         ast_log(LOG_ERROR, "ARI ChannelApplicationFinished missing required field timestamp\n");</span><br><span style="color: hsl(120, 100%, 40%);">+            res = 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 (!has_channel) {</span><br><span style="color: hsl(120, 100%, 40%);">+           ast_log(LOG_ERROR, "ARI ChannelApplicationFinished missing required field channel\n");</span><br><span style="color: hsl(120, 100%, 40%);">+              res = 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%);">+   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%);">+ari_validator ast_ari_validate_channel_application_finished_fn(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     return ast_ari_validate_channel_application_finished;</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%);">+int ast_ari_validate_channel_application_started(struct ast_json *json)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        int res = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+  struct ast_json_iter *iter;</span><br><span style="color: hsl(120, 100%, 40%);">+   int has_type = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     int has_application = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+      int has_timestamp = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+        int has_channel = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {</span><br><span style="color: hsl(120, 100%, 40%);">+         if (strcmp("asterisk_id", ast_json_object_iter_key(iter)) == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                   int prop_is_valid;</span><br><span style="color: hsl(120, 100%, 40%);">+                    prop_is_valid = ast_ari_validate_string(</span><br><span style="color: hsl(120, 100%, 40%);">+                              ast_json_object_iter_value(iter));</span><br><span style="color: hsl(120, 100%, 40%);">+                    if (!prop_is_valid) {</span><br><span style="color: hsl(120, 100%, 40%);">+                         ast_log(LOG_ERROR, "ARI ChannelApplicationStarted field asterisk_id failed validation\n");</span><br><span style="color: hsl(120, 100%, 40%);">+                          res = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+                      }</span><br><span style="color: hsl(120, 100%, 40%);">+             } else</span><br><span style="color: hsl(120, 100%, 40%);">+                if (strcmp("type", ast_json_object_iter_key(iter)) == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                  int prop_is_valid;</span><br><span style="color: hsl(120, 100%, 40%);">+                    has_type = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+                 prop_is_valid = ast_ari_validate_string(</span><br><span style="color: hsl(120, 100%, 40%);">+                              ast_json_object_iter_value(iter));</span><br><span style="color: hsl(120, 100%, 40%);">+                    if (!prop_is_valid) {</span><br><span style="color: hsl(120, 100%, 40%);">+                         ast_log(LOG_ERROR, "ARI ChannelApplicationStarted field type failed validation\n");</span><br><span style="color: hsl(120, 100%, 40%);">+                         res = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+                      }</span><br><span style="color: hsl(120, 100%, 40%);">+             } else</span><br><span style="color: hsl(120, 100%, 40%);">+                if (strcmp("application", ast_json_object_iter_key(iter)) == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                   int prop_is_valid;</span><br><span style="color: hsl(120, 100%, 40%);">+                    has_application = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+                  prop_is_valid = ast_ari_validate_string(</span><br><span style="color: hsl(120, 100%, 40%);">+                              ast_json_object_iter_value(iter));</span><br><span style="color: hsl(120, 100%, 40%);">+                    if (!prop_is_valid) {</span><br><span style="color: hsl(120, 100%, 40%);">+                         ast_log(LOG_ERROR, "ARI ChannelApplicationStarted field application failed validation\n");</span><br><span style="color: hsl(120, 100%, 40%);">+                          res = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+                      }</span><br><span style="color: hsl(120, 100%, 40%);">+             } else</span><br><span style="color: hsl(120, 100%, 40%);">+                if (strcmp("timestamp", ast_json_object_iter_key(iter)) == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                     int prop_is_valid;</span><br><span style="color: hsl(120, 100%, 40%);">+                    has_timestamp = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+                    prop_is_valid = ast_ari_validate_date(</span><br><span style="color: hsl(120, 100%, 40%);">+                                ast_json_object_iter_value(iter));</span><br><span style="color: hsl(120, 100%, 40%);">+                    if (!prop_is_valid) {</span><br><span style="color: hsl(120, 100%, 40%);">+                         ast_log(LOG_ERROR, "ARI ChannelApplicationStarted field timestamp failed validation\n");</span><br><span style="color: hsl(120, 100%, 40%);">+                            res = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+                      }</span><br><span style="color: hsl(120, 100%, 40%);">+             } else</span><br><span style="color: hsl(120, 100%, 40%);">+                if (strcmp("channel", ast_json_object_iter_key(iter)) == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                       int prop_is_valid;</span><br><span style="color: hsl(120, 100%, 40%);">+                    has_channel = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+                      prop_is_valid = ast_ari_validate_channel(</span><br><span style="color: hsl(120, 100%, 40%);">+                             ast_json_object_iter_value(iter));</span><br><span style="color: hsl(120, 100%, 40%);">+                    if (!prop_is_valid) {</span><br><span style="color: hsl(120, 100%, 40%);">+                         ast_log(LOG_ERROR, "ARI ChannelApplicationStarted field channel failed validation\n");</span><br><span style="color: hsl(120, 100%, 40%);">+                              res = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+                      }</span><br><span style="color: hsl(120, 100%, 40%);">+             } else</span><br><span style="color: hsl(120, 100%, 40%);">+                if (strcmp("channel_application", ast_json_object_iter_key(iter)) == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                   int prop_is_valid;</span><br><span style="color: hsl(120, 100%, 40%);">+                    prop_is_valid = ast_ari_validate_channel_application(</span><br><span style="color: hsl(120, 100%, 40%);">+                         ast_json_object_iter_value(iter));</span><br><span style="color: hsl(120, 100%, 40%);">+                    if (!prop_is_valid) {</span><br><span style="color: hsl(120, 100%, 40%);">+                         ast_log(LOG_ERROR, "ARI ChannelApplicationStarted field channel_application failed validation\n");</span><br><span style="color: hsl(120, 100%, 40%);">+                          res = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+                      }</span><br><span style="color: hsl(120, 100%, 40%);">+             } else</span><br><span style="color: hsl(120, 100%, 40%);">+                {</span><br><span style="color: hsl(120, 100%, 40%);">+                     ast_log(LOG_ERROR,</span><br><span style="color: hsl(120, 100%, 40%);">+                            "ARI ChannelApplicationStarted has undocumented field %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                                ast_json_object_iter_key(iter));</span><br><span style="color: hsl(120, 100%, 40%);">+                      res = 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%);">+   if (!has_type) {</span><br><span style="color: hsl(120, 100%, 40%);">+              ast_log(LOG_ERROR, "ARI ChannelApplicationStarted missing required field type\n");</span><br><span style="color: hsl(120, 100%, 40%);">+          res = 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 (!has_application) {</span><br><span style="color: hsl(120, 100%, 40%);">+               ast_log(LOG_ERROR, "ARI ChannelApplicationStarted missing required field application\n");</span><br><span style="color: hsl(120, 100%, 40%);">+           res = 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 (!has_timestamp) {</span><br><span style="color: hsl(120, 100%, 40%);">+         ast_log(LOG_ERROR, "ARI ChannelApplicationStarted missing required field timestamp\n");</span><br><span style="color: hsl(120, 100%, 40%);">+             res = 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 (!has_channel) {</span><br><span style="color: hsl(120, 100%, 40%);">+           ast_log(LOG_ERROR, "ARI ChannelApplicationStarted missing required field channel\n");</span><br><span style="color: hsl(120, 100%, 40%);">+               res = 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%);">+   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%);">+ari_validator ast_ari_validate_channel_application_started_fn(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      return ast_ari_validate_channel_application_started;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> int ast_ari_validate_channel_caller_id(struct ast_json *json)</span><br><span> {</span><br><span>      int res = 1;</span><br><span>@@ -5446,6 +5731,12 @@</span><br><span>        if (strcmp("BridgeVideoSourceChanged", discriminator) == 0) {</span><br><span>              return ast_ari_validate_bridge_video_source_changed(json);</span><br><span>   } else</span><br><span style="color: hsl(120, 100%, 40%);">+        if (strcmp("ChannelApplicationFinished", discriminator) == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+             return ast_ari_validate_channel_application_finished(json);</span><br><span style="color: hsl(120, 100%, 40%);">+   } else</span><br><span style="color: hsl(120, 100%, 40%);">+        if (strcmp("ChannelApplicationStarted", discriminator) == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+              return ast_ari_validate_channel_application_started(json);</span><br><span style="color: hsl(120, 100%, 40%);">+    } else</span><br><span>       if (strcmp("ChannelCallerId", discriminator) == 0) {</span><br><span>               return ast_ari_validate_channel_caller_id(json);</span><br><span>     } else</span><br><span>@@ -5653,6 +5944,12 @@</span><br><span>      if (strcmp("BridgeVideoSourceChanged", discriminator) == 0) {</span><br><span>              return ast_ari_validate_bridge_video_source_changed(json);</span><br><span>   } else</span><br><span style="color: hsl(120, 100%, 40%);">+        if (strcmp("ChannelApplicationFinished", discriminator) == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+             return ast_ari_validate_channel_application_finished(json);</span><br><span style="color: hsl(120, 100%, 40%);">+   } else</span><br><span style="color: hsl(120, 100%, 40%);">+        if (strcmp("ChannelApplicationStarted", discriminator) == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+              return ast_ari_validate_channel_application_started(json);</span><br><span style="color: hsl(120, 100%, 40%);">+    } else</span><br><span>       if (strcmp("ChannelCallerId", discriminator) == 0) {</span><br><span>               return ast_ari_validate_channel_caller_id(json);</span><br><span>     } else</span><br><span>diff --git a/res/ari/ari_model_validators.h b/res/ari/ari_model_validators.h</span><br><span>index 8fa28bc..993dc9f 100644</span><br><span>--- a/res/ari/ari_model_validators.h</span><br><span>+++ b/res/ari/ari_model_validators.h</span><br><span>@@ -442,6 +442,24 @@</span><br><span> ari_validator ast_ari_validate_channel_fn(void);</span><br><span> </span><br><span> /*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Validator for ChannelApplication.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Channel application info</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param json JSON object to validate.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns True (non-zero) if valid.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns False (zero) if invalid.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_ari_validate_channel_application(struct ast_json *json);</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%);">+ * \brief Function pointer to ast_ari_validate_channel_application().</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * See \ref ast_ari_model_validators.h for more details.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ari_validator ast_ari_validate_channel_application_fn(void);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span>  * \brief Validator for Dialed.</span><br><span>  *</span><br><span>  * Dialed channel information.</span><br><span>@@ -770,6 +788,42 @@</span><br><span> ari_validator ast_ari_validate_bridge_video_source_changed_fn(void);</span><br><span> </span><br><span> /*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Validator for ChannelApplicationFinished.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Notification that a application has been finished.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param json JSON object to validate.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns True (non-zero) if valid.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns False (zero) if invalid.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_ari_validate_channel_application_finished(struct ast_json *json);</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%);">+ * \brief Function pointer to ast_ari_validate_channel_application_finished().</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * See \ref ast_ari_model_validators.h for more details.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ari_validator ast_ari_validate_channel_application_finished_fn(void);</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%);">+ * \brief Validator for ChannelApplicationStarted.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Notification that a application has been started.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param json JSON object to validate.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns True (non-zero) if valid.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns False (zero) if invalid.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_ari_validate_channel_application_started(struct ast_json *json);</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%);">+ * \brief Function pointer to ast_ari_validate_channel_application_started().</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * See \ref ast_ari_model_validators.h for more details.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ari_validator ast_ari_validate_channel_application_started_fn(void);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span>  * \brief Validator for ChannelCallerId.</span><br><span>  *</span><br><span>  * Channel changed Caller ID.</span><br><span>@@ -1497,6 +1551,10 @@</span><br><span>  * - language: string (required)</span><br><span>  * - name: string (required)</span><br><span>  * - state: string (required)</span><br><span style="color: hsl(120, 100%, 40%);">+ * ChannelApplication</span><br><span style="color: hsl(120, 100%, 40%);">+ * - args: string (required)</span><br><span style="color: hsl(120, 100%, 40%);">+ * - name: string (required)</span><br><span style="color: hsl(120, 100%, 40%);">+ * - status: string (required)</span><br><span>  * Dialed</span><br><span>  * DialplanCEP</span><br><span>  * - context: string (required)</span><br><span>@@ -1550,7 +1608,7 @@</span><br><span>  * - asterisk_id: string</span><br><span>  * - type: string (required)</span><br><span>  * - application: string (required)</span><br><span style="color: hsl(0, 100%, 40%);">- * - timestamp: Date</span><br><span style="color: hsl(120, 100%, 40%);">+ * - timestamp: Date (required)</span><br><span>  * - args: List[string] (required)</span><br><span>  * - channel: Channel (required)</span><br><span>  * - destination: string (required)</span><br><span>@@ -1619,6 +1677,20 @@</span><br><span>  * - timestamp: Date (required)</span><br><span>  * - bridge: Bridge (required)</span><br><span>  * - old_video_source_id: string</span><br><span style="color: hsl(120, 100%, 40%);">+ * ChannelApplicationFinished</span><br><span style="color: hsl(120, 100%, 40%);">+ * - asterisk_id: string</span><br><span style="color: hsl(120, 100%, 40%);">+ * - type: string (required)</span><br><span style="color: hsl(120, 100%, 40%);">+ * - application: string (required)</span><br><span style="color: hsl(120, 100%, 40%);">+ * - timestamp: Date (required)</span><br><span style="color: hsl(120, 100%, 40%);">+ * - channel: Channel (required)</span><br><span style="color: hsl(120, 100%, 40%);">+ * - channel_application: ChannelApplication</span><br><span style="color: hsl(120, 100%, 40%);">+ * ChannelApplicationStarted</span><br><span style="color: hsl(120, 100%, 40%);">+ * - asterisk_id: string</span><br><span style="color: hsl(120, 100%, 40%);">+ * - type: string (required)</span><br><span style="color: hsl(120, 100%, 40%);">+ * - application: string (required)</span><br><span style="color: hsl(120, 100%, 40%);">+ * - timestamp: Date (required)</span><br><span style="color: hsl(120, 100%, 40%);">+ * - channel: Channel (required)</span><br><span style="color: hsl(120, 100%, 40%);">+ * - channel_application: ChannelApplication</span><br><span>  * ChannelCallerId</span><br><span>  * - asterisk_id: string</span><br><span>  * - type: string (required)</span><br><span>diff --git a/res/ari/resource_channels.c b/res/ari/resource_channels.c</span><br><span>index eca70ce..5674731 100644</span><br><span>--- a/res/ari/resource_channels.c</span><br><span>+++ b/res/ari/resource_channels.c</span><br><span>@@ -1966,3 +1966,26 @@</span><br><span> </span><br><span>         ast_ari_response_no_content(response);</span><br><span> }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void ast_ari_channels_application(</span><br><span style="color: hsl(120, 100%, 40%);">+             struct ast_variable *headers,</span><br><span style="color: hsl(120, 100%, 40%);">+         struct ast_ari_channels_application_args *args,</span><br><span style="color: hsl(120, 100%, 40%);">+               struct ast_ari_response *response)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  control = find_control(response, args->channel_id);</span><br><span style="color: hsl(120, 100%, 40%);">+        if (control == NULL) {</span><br><span style="color: hsl(120, 100%, 40%);">+                /* Response filled in by find_control */</span><br><span style="color: hsl(120, 100%, 40%);">+              return;</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 (stasis_app_control_channel_app(control, args->app, S_OR(args->app_args, ""))) {</span><br><span style="color: hsl(120, 100%, 40%);">+           ast_ari_response_error(response, 500, "Internal Server Error",</span><br><span style="color: hsl(120, 100%, 40%);">+                      "Failed to execute applications");</span><br><span style="color: hsl(120, 100%, 40%);">+          return;</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_ari_response_no_content(response);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>diff --git a/res/ari/resource_channels.h b/res/ari/resource_channels.h</span><br><span>index fdd7a6b..a43ab37 100644</span><br><span>--- a/res/ari/resource_channels.h</span><br><span>+++ b/res/ari/resource_channels.h</span><br><span>@@ -809,5 +809,33 @@</span><br><span>  * \param[out] response HTTP response</span><br><span>  */</span><br><span> void ast_ari_channels_dial(struct ast_variable *headers, struct ast_ari_channels_dial_args *args, struct ast_ari_response *response);</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Argument struct for ast_ari_channels_application() */</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_ari_channels_application_args {</span><br><span style="color: hsl(120, 100%, 40%);">+   /*! Channel's id */</span><br><span style="color: hsl(120, 100%, 40%);">+       const char *channel_id;</span><br><span style="color: hsl(120, 100%, 40%);">+       /*! Application's name */</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *app;</span><br><span style="color: hsl(120, 100%, 40%);">+      /*! Application's data */</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *app_args;</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%);">+ * \brief Body parsing function for /channels/{channelId}/application.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param body The JSON body from which to parse parameters.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[out] args The args structure to parse into.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval zero on success</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval non-zero on failure</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_ari_channels_application_parse_body(</span><br><span style="color: hsl(120, 100%, 40%);">+    struct ast_json *body,</span><br><span style="color: hsl(120, 100%, 40%);">+        struct ast_ari_channels_application_args *args);</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%);">+ * \brief Execute a application on a channel.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param headers HTTP headers</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param args Swagger parameters</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[out] response HTTP response</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+void ast_ari_channels_application(struct ast_variable *headers, struct ast_ari_channels_application_args *args, struct ast_ari_response *response);</span><br><span> </span><br><span> #endif /* _ASTERISK_RESOURCE_CHANNELS_H */</span><br><span>diff --git a/res/res_ari_channels.c b/res/res_ari_channels.c</span><br><span>index 3d96d60..8f0d541 100644</span><br><span>--- a/res/res_ari_channels.c</span><br><span>+++ b/res/res_ari_channels.c</span><br><span>@@ -2749,6 +2749,95 @@</span><br><span> fin: __attribute__((unused))</span><br><span>       return;</span><br><span> }</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_ari_channels_application_parse_body(</span><br><span style="color: hsl(120, 100%, 40%);">+    struct ast_json *body,</span><br><span style="color: hsl(120, 100%, 40%);">+        struct ast_ari_channels_application_args *args)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    struct ast_json *field;</span><br><span style="color: hsl(120, 100%, 40%);">+       /* Parse query parameters out of it */</span><br><span style="color: hsl(120, 100%, 40%);">+        field = ast_json_object_get(body, "app");</span><br><span style="color: hsl(120, 100%, 40%);">+   if (field) {</span><br><span style="color: hsl(120, 100%, 40%);">+          args->app = ast_json_string_get(field);</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+     field = ast_json_object_get(body, "app_args");</span><br><span style="color: hsl(120, 100%, 40%);">+      if (field) {</span><br><span style="color: hsl(120, 100%, 40%);">+          args->app_args = ast_json_string_get(field);</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%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Parameter parsing callback for /channels/{channelId}/application.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param get_params GET parameters in the HTTP request.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param path_vars Path variables extracted from the request.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param headers HTTP headers.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[out] response Response to the HTTP request.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static void ast_ari_channels_application_cb(</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_tcptls_session_instance *ser,</span><br><span style="color: hsl(120, 100%, 40%);">+      struct ast_variable *get_params, struct ast_variable *path_vars,</span><br><span style="color: hsl(120, 100%, 40%);">+      struct ast_variable *headers, struct ast_json *body, struct ast_ari_response *response)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    struct ast_ari_channels_application_args args = {};</span><br><span style="color: hsl(120, 100%, 40%);">+   struct ast_variable *i;</span><br><span style="color: hsl(120, 100%, 40%);">+#if defined(AST_DEVMODE)</span><br><span style="color: hsl(120, 100%, 40%);">+     int is_valid;</span><br><span style="color: hsl(120, 100%, 40%);">+ int code;</span><br><span style="color: hsl(120, 100%, 40%);">+#endif /* AST_DEVMODE */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ for (i = get_params; i; i = i->next) {</span><br><span style="color: hsl(120, 100%, 40%);">+             if (strcmp(i->name, "app") == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                       args.app = (i->value);</span><br><span style="color: hsl(120, 100%, 40%);">+             } else</span><br><span style="color: hsl(120, 100%, 40%);">+                if (strcmp(i->name, "app_args") == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                  args.app_args = (i->value);</span><br><span style="color: hsl(120, 100%, 40%);">+                } else</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 = path_vars; i; i = i->next) {</span><br><span style="color: hsl(120, 100%, 40%);">+              if (strcmp(i->name, "channelId") == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                 args.channel_id = (i->value);</span><br><span style="color: hsl(120, 100%, 40%);">+              } else</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_ari_channels_application_parse_body(body, &args)) {</span><br><span style="color: hsl(120, 100%, 40%);">+               ast_ari_response_alloc_failed(response);</span><br><span style="color: hsl(120, 100%, 40%);">+              goto fin;</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+     ast_ari_channels_application(headers, &args, response);</span><br><span style="color: hsl(120, 100%, 40%);">+#if defined(AST_DEVMODE)</span><br><span style="color: hsl(120, 100%, 40%);">+ code = response->response_code;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  switch (code) {</span><br><span style="color: hsl(120, 100%, 40%);">+       case 0: /* Implementation is still a stub, or the code wasn't set */</span><br><span style="color: hsl(120, 100%, 40%);">+              is_valid = response->message == NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+              break;</span><br><span style="color: hsl(120, 100%, 40%);">+        case 500: /* Internal Server Error */</span><br><span style="color: hsl(120, 100%, 40%);">+ case 501: /* Not Implemented */</span><br><span style="color: hsl(120, 100%, 40%);">+       case 404: /* Channel cannot be found. */</span><br><span style="color: hsl(120, 100%, 40%);">+      case 409: /* Channel cannot be dialed. */</span><br><span style="color: hsl(120, 100%, 40%);">+             is_valid = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+         break;</span><br><span style="color: hsl(120, 100%, 40%);">+        default:</span><br><span style="color: hsl(120, 100%, 40%);">+              if (200 <= code && code <= 299) {</span><br><span style="color: hsl(120, 100%, 40%);">+                       is_valid = ast_ari_validate_void(</span><br><span style="color: hsl(120, 100%, 40%);">+                             response->message);</span><br><span style="color: hsl(120, 100%, 40%);">+                } else {</span><br><span style="color: hsl(120, 100%, 40%);">+                      ast_log(LOG_ERROR, "Invalid error response %d for /channels/{channelId}/application\n", code);</span><br><span style="color: hsl(120, 100%, 40%);">+                      is_valid = 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%);">+   if (!is_valid) {</span><br><span style="color: hsl(120, 100%, 40%);">+              ast_log(LOG_ERROR, "Response validation failed for /channels/{channelId}/application\n");</span><br><span style="color: hsl(120, 100%, 40%);">+           ast_ari_response_error(response, 500,</span><br><span style="color: hsl(120, 100%, 40%);">+                 "Internal Server Error", "Response validation failed");</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span style="color: hsl(120, 100%, 40%);">+#endif /* AST_DEVMODE */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+fin: __attribute__((unused))</span><br><span style="color: hsl(120, 100%, 40%);">+   return;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span> </span><br><span> /*! \brief REST handler for /api-docs/channels.json */</span><br><span> static struct stasis_rest_handlers channels_create = {</span><br><span>@@ -2921,6 +3010,15 @@</span><br><span>  .children = {  }</span><br><span> };</span><br><span> /*! \brief REST handler for /api-docs/channels.json */</span><br><span style="color: hsl(120, 100%, 40%);">+static struct stasis_rest_handlers channels_channelId_application = {</span><br><span style="color: hsl(120, 100%, 40%);">+       .path_segment = "application",</span><br><span style="color: hsl(120, 100%, 40%);">+      .callbacks = {</span><br><span style="color: hsl(120, 100%, 40%);">+                [AST_HTTP_POST] = ast_ari_channels_application_cb,</span><br><span style="color: hsl(120, 100%, 40%);">+    },</span><br><span style="color: hsl(120, 100%, 40%);">+    .num_children = 0,</span><br><span style="color: hsl(120, 100%, 40%);">+    .children = {  }</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+/*! \brief REST handler for /api-docs/channels.json */</span><br><span> static struct stasis_rest_handlers channels_channelId = {</span><br><span>    .path_segment = "channelId",</span><br><span>       .is_wildcard = 1,</span><br><span>@@ -2929,8 +3027,8 @@</span><br><span>            [AST_HTTP_POST] = ast_ari_channels_originate_with_id_cb,</span><br><span>             [AST_HTTP_DELETE] = ast_ari_channels_hangup_cb,</span><br><span>      },</span><br><span style="color: hsl(0, 100%, 40%);">-      .num_children = 15,</span><br><span style="color: hsl(0, 100%, 40%);">-     .children = { &channels_channelId_continue,&channels_channelId_move,&channels_channelId_redirect,&channels_channelId_answer,&channels_channelId_ring,&channels_channelId_dtmf,&channels_channelId_mute,&channels_channelId_hold,&channels_channelId_moh,&channels_channelId_silence,&channels_channelId_play,&channels_channelId_record,&channels_channelId_variable,&channels_channelId_snoop,&channels_channelId_dial, }</span><br><span style="color: hsl(120, 100%, 40%);">+        .num_children = 16,</span><br><span style="color: hsl(120, 100%, 40%);">+   .children = { &channels_channelId_continue,&channels_channelId_move,&channels_channelId_redirect,&channels_channelId_answer,&channels_channelId_ring,&channels_channelId_dtmf,&channels_channelId_mute,&channels_channelId_hold,&channels_channelId_moh,&channels_channelId_silence,&channels_channelId_play,&channels_channelId_record,&channels_channelId_variable,&channels_channelId_snoop,&channels_channelId_dial,&channels_channelId_application, }</span><br><span> };</span><br><span> /*! \brief REST handler for /api-docs/channels.json */</span><br><span> static struct stasis_rest_handlers channels = {</span><br><span>diff --git a/res/res_stasis.c b/res/res_stasis.c</span><br><span>index e635dc5..782dc49 100644</span><br><span>--- a/res/res_stasis.c</span><br><span>+++ b/res/res_stasis.c</span><br><span>@@ -182,6 +182,96 @@</span><br><span> STASIS_MESSAGE_TYPE_DEFN_LOCAL(start_message_type,</span><br><span>      .to_json = stasis_start_to_json);</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+static struct ast_json *channel_application_finished_to_json(</span><br><span style="color: hsl(120, 100%, 40%);">+          struct stasis_message *message,</span><br><span style="color: hsl(120, 100%, 40%);">+               const struct stasis_message_sanitizer *sanitize)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   struct ast_channel_blob *channel_blob = stasis_message_data(message);</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_json *blob = channel_blob->blob;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      return ast_json_pack("{s: s, s: o?, s: O, s: o}",</span><br><span style="color: hsl(120, 100%, 40%);">+           "type", "ChannelApplicationFinished",</span><br><span style="color: hsl(120, 100%, 40%);">+             "timestamp", ast_json_timeval(*stasis_message_timestamp(message), NULL),</span><br><span style="color: hsl(120, 100%, 40%);">+            "channel_application", blob,</span><br><span style="color: hsl(120, 100%, 40%);">+                "channel", ast_channel_snapshot_to_json(channel_blob->snapshot, NULL)</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%);">+STASIS_MESSAGE_TYPE_DEFN_LOCAL(application_finished_event_type,</span><br><span style="color: hsl(120, 100%, 40%);">+      .to_json = channel_application_finished_to_json,</span><br><span style="color: hsl(120, 100%, 40%);">+/*    .to_ami = multi_user_event_to_ami, */</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%);">+int publish_channel_application_finished_event(</span><br><span style="color: hsl(120, 100%, 40%);">+         struct ast_channel *chan, const char* app, const char* app_args, const char* status)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       struct stasis_message *msg;</span><br><span style="color: hsl(120, 100%, 40%);">+   struct ast_json *j_data;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    j_data = ast_json_pack("{s: s?, s: s?, s: s?}",</span><br><span style="color: hsl(120, 100%, 40%);">+                     "name", app,</span><br><span style="color: hsl(120, 100%, 40%);">+                        "args", app_args,</span><br><span style="color: hsl(120, 100%, 40%);">+                   "status", status</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%);">+  msg = ast_channel_blob_create_from_cache(</span><br><span style="color: hsl(120, 100%, 40%);">+                     ast_channel_uniqueid(chan),</span><br><span style="color: hsl(120, 100%, 40%);">+                   application_finished_event_type(),</span><br><span style="color: hsl(120, 100%, 40%);">+                    j_data);</span><br><span style="color: hsl(120, 100%, 40%);">+      if (msg == NULL) {</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%);">+   stasis_publish(ast_channel_topic(chan), msg);</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_ref(msg, -1);</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 struct ast_json *channel_application_started_to_json(</span><br><span style="color: hsl(120, 100%, 40%);">+          struct stasis_message *message,</span><br><span style="color: hsl(120, 100%, 40%);">+               const struct stasis_message_sanitizer *sanitize)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   struct ast_channel_blob *channel_blob = stasis_message_data(message);</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_json *blob = channel_blob->blob;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      return ast_json_pack("{s: s, s: o?, s: O, s: o}",</span><br><span style="color: hsl(120, 100%, 40%);">+           "type", "ChannelApplicationStarted",</span><br><span style="color: hsl(120, 100%, 40%);">+              "timestamp", ast_json_timeval(*stasis_message_timestamp(message), NULL),</span><br><span style="color: hsl(120, 100%, 40%);">+            "channel_application", blob,</span><br><span style="color: hsl(120, 100%, 40%);">+                "channel", ast_channel_snapshot_to_json(channel_blob->snapshot, NULL)</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%);">+STASIS_MESSAGE_TYPE_DEFN_LOCAL(application_started_event_type,</span><br><span style="color: hsl(120, 100%, 40%);">+ .to_json = channel_application_started_to_json,</span><br><span style="color: hsl(120, 100%, 40%);">+/*     .to_ami = multi_user_event_to_ami, */</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%);">+int publish_channel_application_started_event(struct ast_channel *chan, const char* app, const char* app_args)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       struct stasis_message *msg;</span><br><span style="color: hsl(120, 100%, 40%);">+   struct ast_json *j_data;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    j_data = ast_json_pack("{s: s?, s: s?, s: s?}",</span><br><span style="color: hsl(120, 100%, 40%);">+                     "name", app,</span><br><span style="color: hsl(120, 100%, 40%);">+                        "args", app_args,</span><br><span style="color: hsl(120, 100%, 40%);">+                   "status", "started"</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%);">+  msg = ast_channel_blob_create_from_cache(</span><br><span style="color: hsl(120, 100%, 40%);">+                     ast_channel_uniqueid(chan),</span><br><span style="color: hsl(120, 100%, 40%);">+                   application_started_event_type(),</span><br><span style="color: hsl(120, 100%, 40%);">+                     j_data);</span><br><span style="color: hsl(120, 100%, 40%);">+      if (msg == NULL) {</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%);">+   stasis_publish(ast_channel_topic(chan), msg);</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_ref(msg, -1);</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> /*! AO2 hash function for \ref app */</span><br><span> static int app_hash(const void *obj, const int flags)</span><br><span> {</span><br><span>@@ -2191,6 +2281,9 @@</span><br><span> </span><br><span>    STASIS_MESSAGE_TYPE_CLEANUP(end_message_type);</span><br><span>       STASIS_MESSAGE_TYPE_CLEANUP(start_message_type);</span><br><span style="color: hsl(120, 100%, 40%);">+      STASIS_MESSAGE_TYPE_CLEANUP(application_started_event_type);</span><br><span style="color: hsl(120, 100%, 40%);">+  STASIS_MESSAGE_TYPE_CLEANUP(application_finished_event_type);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> </span><br><span>  return 0;</span><br><span> }</span><br><span>@@ -2314,6 +2407,13 @@</span><br><span>      if (STASIS_MESSAGE_TYPE_INIT(end_message_type) != 0) {</span><br><span>               return AST_MODULE_LOAD_DECLINE;</span><br><span>      }</span><br><span style="color: hsl(120, 100%, 40%);">+     if (STASIS_MESSAGE_TYPE_INIT(application_started_event_type) != 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+          return AST_MODULE_LOAD_DECLINE;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+     if (STASIS_MESSAGE_TYPE_INIT(application_finished_event_type) != 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+         return AST_MODULE_LOAD_DECLINE;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>  apps_registry = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX, 0,</span><br><span>                APPS_NUM_BUCKETS, app_hash, NULL, app_compare);</span><br><span>      app_controls = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX, 0,</span><br><span>diff --git a/res/stasis/control.c b/res/stasis/control.c</span><br><span>index e209a6a..d25b499 100644</span><br><span>--- a/res/stasis/control.c</span><br><span>+++ b/res/stasis/control.c</span><br><span>@@ -40,6 +40,7 @@</span><br><span> #include "asterisk/pbx.h"</span><br><span> #include "asterisk/musiconhold.h"</span><br><span> #include "asterisk/app.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/stasis.h"</span><br><span> </span><br><span> AST_LIST_HEAD(app_control_rules, stasis_app_control_rule);</span><br><span> </span><br><span>@@ -406,6 +407,50 @@</span><br><span>      return 0;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+struct stasis_app_control_application_data {</span><br><span style="color: hsl(120, 100%, 40%);">+      char *app_name;</span><br><span style="color: hsl(120, 100%, 40%);">+       char *app_args;</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 app_control_application(struct stasis_app_control *control,</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_channel *chan, void *data)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      struct stasis_app_control_application_data *application = data;</span><br><span style="color: hsl(120, 100%, 40%);">+       struct ast_app *app;</span><br><span style="color: hsl(120, 100%, 40%);">+  int ret;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    ast_debug(1, "Fired app_control_application. app: %s, app_arg: %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                       application->app_name, application->app_args);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        ret = publish_channel_application_started_event(chan, application->app_name, application->app_args);</span><br><span style="color: hsl(120, 100%, 40%);">+    if (ret) {</span><br><span style="color: hsl(120, 100%, 40%);">+            ast_log(LOG_WARNING, "Could not publish the channel application started event. channelId: %s, app_name: %s, app_args %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                         ast_channel_uniqueid(chan), application->app_name, application->app_args);</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%);">+   app = pbx_findapp(application->app_name);</span><br><span style="color: hsl(120, 100%, 40%);">+  if (!app) {</span><br><span style="color: hsl(120, 100%, 40%);">+           ast_log(LOG_NOTICE, "Could not find application. app: %s, app_args: %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                          application->app_name, application->app_args);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+                publish_channel_application_finished_event(chan, application->app_name, application->app_args, "failed");</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%);">+   ret = pbx_exec_app(chan, app, application->app_args);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    ret = publish_channel_application_finished_event(chan, application->app_name, application->app_args, "done");</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ret) {</span><br><span style="color: hsl(120, 100%, 40%);">+            ast_log(LOG_WARNING, "Could not publish the channel application finished event. channelId: %s, app_name: %s, app_args %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                                ast_channel_uniqueid(chan), application->app_name, application->app_args);</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%);">+   return ret;</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> struct stasis_app_control_move_data {</span><br><span>  char *app_name;</span><br><span>      char *app_args;</span><br><span>@@ -863,6 +908,37 @@</span><br><span>       return ast_channel_snapshot_get_latest(stasis_app_control_get_channel_id(control));</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+int stasis_app_control_channel_app(struct stasis_app_control *control, const char *app_name, const char *app_args)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   struct stasis_app_control_application_data *data;</span><br><span style="color: hsl(120, 100%, 40%);">+     size_t size;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        if (!control || !app_name || !app_args) {</span><br><span style="color: hsl(120, 100%, 40%);">+             ast_log(LOG_WARNING, "Wrong input parameter.\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%);">+     ast_debug(1, "Fired stasis_app_control_chanapp. app_name: %s, app_args: %s\n", app_name, app_args);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       size = sizeof(*data) + strlen(app_name) + 1;</span><br><span style="color: hsl(120, 100%, 40%);">+  size += strlen(app_args) + 1;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       data = ast_calloc(1, size);</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!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%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   data->app_name = (char *)data + sizeof(*data);</span><br><span style="color: hsl(120, 100%, 40%);">+     strcpy(data->app_name, app_name); /* Safe */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     data->app_args = data->app_name + strlen(app_name) + 1;</span><br><span style="color: hsl(120, 100%, 40%);">+ strcpy(data->app_args, app_args); /* Safe */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     stasis_app_send_command_async(control, app_control_application, data, ast_free_ptr);</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%);">+</span><br><span> static int app_send_command_on_condition(struct stasis_app_control *control,</span><br><span>                                      stasis_app_command_cb command_fn, void *data,</span><br><span>                                        command_data_destructor_fn data_destructor,</span><br><span>diff --git a/rest-api/api-docs/channels.json b/rest-api/api-docs/channels.json</span><br><span>index 6161934..28ff14f 100644</span><br><span>--- a/rest-api/api-docs/channels.json</span><br><span>+++ b/rest-api/api-docs/channels.json</span><br><span>@@ -1720,6 +1720,54 @@</span><br><span>                                       ]</span><br><span>                            }</span><br><span>                    ]</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%);">+                     "path": "/channels/{channelId}/application",</span><br><span style="color: hsl(120, 100%, 40%);">+                      "description": "Execute a application on a channel",</span><br><span style="color: hsl(120, 100%, 40%);">+                      "operations": [</span><br><span style="color: hsl(120, 100%, 40%);">+                             {</span><br><span style="color: hsl(120, 100%, 40%);">+                                     "httpMethod": "POST",</span><br><span style="color: hsl(120, 100%, 40%);">+                                     "summary": "Execute a application on a channel.",</span><br><span style="color: hsl(120, 100%, 40%);">+                                 "nickname": "application",</span><br><span style="color: hsl(120, 100%, 40%);">+                                        "responseClass": "void",</span><br><span style="color: hsl(120, 100%, 40%);">+                                  "parameters": [</span><br><span style="color: hsl(120, 100%, 40%);">+                                             {</span><br><span style="color: hsl(120, 100%, 40%);">+                                                     "name": "channelId",</span><br><span style="color: hsl(120, 100%, 40%);">+                                                      "description": "Channel's id",</span><br><span style="color: hsl(120, 100%, 40%);">+                                                        "paramType": "path",</span><br><span style="color: hsl(120, 100%, 40%);">+                                                      "required": true,</span><br><span style="color: hsl(120, 100%, 40%);">+                                                   "allowMultiple": false,</span><br><span style="color: hsl(120, 100%, 40%);">+                                                     "dataType": "string"</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%);">+                                                     "name": "app",</span><br><span style="color: hsl(120, 100%, 40%);">+                                                    "description": "Application's name",</span><br><span style="color: hsl(120, 100%, 40%);">+                                                  "paramType": "query",</span><br><span style="color: hsl(120, 100%, 40%);">+                                                     "required": true,</span><br><span style="color: hsl(120, 100%, 40%);">+                                                   "allowMultiple": false,</span><br><span style="color: hsl(120, 100%, 40%);">+                                                     "dataType": "string"</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%);">+                                                     "name": "app_args",</span><br><span style="color: hsl(120, 100%, 40%);">+                                                       "description": "Application's data",</span><br><span style="color: hsl(120, 100%, 40%);">+                                                  "paramType": "query",</span><br><span style="color: hsl(120, 100%, 40%);">+                                                     "required": true,</span><br><span style="color: hsl(120, 100%, 40%);">+                                                   "allowMultiple": false,</span><br><span style="color: hsl(120, 100%, 40%);">+                                                     "dataType": "string"</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%);">+                                    "errorResponses": [</span><br><span style="color: hsl(120, 100%, 40%);">+                                         {</span><br><span style="color: hsl(120, 100%, 40%);">+                                                     "code": 404,</span><br><span style="color: hsl(120, 100%, 40%);">+                                                        "reason": "Channel cannot be found."</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%);">+                                                     "code": 409,</span><br><span style="color: hsl(120, 100%, 40%);">+                                                        "reason": "Channel cannot be dialed."</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>            }</span><br><span>    ],</span><br><span>   "models": {</span><br><span>@@ -1763,6 +1811,27 @@</span><br><span>                               }</span><br><span>                    }</span><br><span>            },</span><br><span style="color: hsl(120, 100%, 40%);">+            "ChannelApplication": {</span><br><span style="color: hsl(120, 100%, 40%);">+                     "id": "ChannelApplication",</span><br><span style="color: hsl(120, 100%, 40%);">+                       "description": "Channel application info",</span><br><span style="color: hsl(120, 100%, 40%);">+                        "properties": {</span><br><span style="color: hsl(120, 100%, 40%);">+                             "name": {</span><br><span style="color: hsl(120, 100%, 40%);">+                                   "description": "Application name",</span><br><span style="color: hsl(120, 100%, 40%);">+                                        "required": true,</span><br><span style="color: hsl(120, 100%, 40%);">+                                   "type": "string"</span><br><span style="color: hsl(120, 100%, 40%);">+                          },</span><br><span style="color: hsl(120, 100%, 40%);">+                            "args": {</span><br><span style="color: hsl(120, 100%, 40%);">+                                   "description": "Application args",</span><br><span style="color: hsl(120, 100%, 40%);">+                                        "required": true,</span><br><span style="color: hsl(120, 100%, 40%);">+                                   "type": "string"</span><br><span style="color: hsl(120, 100%, 40%);">+                          },</span><br><span style="color: hsl(120, 100%, 40%);">+                            "status": {</span><br><span style="color: hsl(120, 100%, 40%);">+                                 "description": "Application status",</span><br><span style="color: hsl(120, 100%, 40%);">+                                      "required": true,</span><br><span style="color: hsl(120, 100%, 40%);">+                                   "type": "string"</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>           "Channel": {</span><br><span>                       "id": "Channel",</span><br><span>                         "description": "A specific communication connection between Asterisk and an Endpoint.",</span><br><span>diff --git a/rest-api/api-docs/events.json b/rest-api/api-docs/events.json</span><br><span>index c9822f6..6b4c5af 100644</span><br><span>--- a/rest-api/api-docs/events.json</span><br><span>+++ b/rest-api/api-docs/events.json</span><br><span>@@ -167,6 +167,8 @@</span><br><span>                           "BridgeBlindTransfer",</span><br><span>                             "BridgeAttendedTransfer",</span><br><span>                          "BridgeVideoSourceChanged",</span><br><span style="color: hsl(120, 100%, 40%);">+                         "ChannelApplicationStarted",</span><br><span style="color: hsl(120, 100%, 40%);">+                                "ChannelApplicationFinished",</span><br><span>                              "ChannelCreated",</span><br><span>                          "ChannelDestroyed",</span><br><span>                                "ChannelEnteredBridge",</span><br><span>@@ -531,6 +533,34 @@</span><br><span>                             }</span><br><span>                    }</span><br><span>            },</span><br><span style="color: hsl(120, 100%, 40%);">+            "ChannelApplicationStarted": {</span><br><span style="color: hsl(120, 100%, 40%);">+                      "id": "ChannelApplicationStarted",</span><br><span style="color: hsl(120, 100%, 40%);">+                        "description": "Notification that a application has been started.",</span><br><span style="color: hsl(120, 100%, 40%);">+                       "properties": {</span><br><span style="color: hsl(120, 100%, 40%);">+                             "channel": {</span><br><span style="color: hsl(120, 100%, 40%);">+                                        "required": true,</span><br><span style="color: hsl(120, 100%, 40%);">+                                   "type": "Channel"</span><br><span style="color: hsl(120, 100%, 40%);">+                         },</span><br><span style="color: hsl(120, 100%, 40%);">+                            "channel_application": {</span><br><span style="color: hsl(120, 100%, 40%);">+                                    "description": "Application detail info.",</span><br><span style="color: hsl(120, 100%, 40%);">+                                        "type": "ChannelApplication"</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%);">+            "ChannelApplicationFinished": {</span><br><span style="color: hsl(120, 100%, 40%);">+                     "id": "ChannelApplicationFinished",</span><br><span style="color: hsl(120, 100%, 40%);">+                       "description": "Notification that a application has been finished.",</span><br><span style="color: hsl(120, 100%, 40%);">+                      "properties": {</span><br><span style="color: hsl(120, 100%, 40%);">+                             "channel": {</span><br><span style="color: hsl(120, 100%, 40%);">+                                        "required": true,</span><br><span style="color: hsl(120, 100%, 40%);">+                                   "type": "Channel"</span><br><span style="color: hsl(120, 100%, 40%);">+                         },</span><br><span style="color: hsl(120, 100%, 40%);">+                            "channel_application": {</span><br><span style="color: hsl(120, 100%, 40%);">+                                    "description": "Application detail info.",</span><br><span style="color: hsl(120, 100%, 40%);">+                                        "type": "ChannelApplication"</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>           "ChannelCreated": {</span><br><span>                        "id": "ChannelCreated",</span><br><span>                  "description": "Notification that a channel has been created.",</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.asterisk.org/c/asterisk/+/11211">change 11211</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/+/11211"/><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: I037bcb9fda54563fdcd2197a5cbda642fdec70e8 </div>
<div style="display:none"> Gerrit-Change-Number: 11211 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: sungtae kim <pchero21@gmail.com> </div>
<div style="display:none"> Gerrit-MessageType: newchange </div>