main/pbx: Move dialplan application management routines to pbx_app.c.
authorCorey Farrell <git@cfware.com>
Tue, 5 Jan 2016 01:46:25 +0000 (20:46 -0500)
committerCorey Farrell <git@cfware.com>
Tue, 5 Jan 2016 01:46:25 +0000 (20:46 -0500)
This is the sixth patch in a series meant to reduce the bulk of pbx.c.
This moves dialplan application management functions to their own source.

Change-Id: I444c10fb90a3cdf9f3047605d6a8aad49c22c44c

include/asterisk/_private.h
main/asterisk.c
main/pbx.c
main/pbx_app.c [new file with mode: 0644]
main/pbx_private.h

index 4cc8e2f..730e415 100644 (file)
@@ -21,6 +21,7 @@ int load_pbx_builtins(void);  /*!< Provided by pbx_builtins.c */
 int load_pbx_functions_cli(void);      /*!< Provided by pbx_functions.c */
 int load_pbx_variables(void);  /*!< Provided by pbx_variables.c */
 int load_pbx_switch(void);             /*!< Provided by pbx_switch.c */
+int load_pbx_app(void);                /*!< Provided by pbx_app.c */
 int init_logger(void);                 /*!< Provided by logger.c */
 void close_logger(void);               /*!< Provided by logger.c */
 void logger_queue_start(void);         /*!< Provided by logger.c */
index 86e2240..448d627 100644 (file)
@@ -4615,6 +4615,11 @@ static void asterisk_daemon(int isroot, const char *runuser, const char *rungrou
                exit(1);
        }
 
+       if (load_pbx_app()) {
+               printf("Failed: load_pbx_app\n%s", term_quit());
+               exit(1);
+       }
+
        if (ast_local_init()) {
                printf("Failed: ast_local_init\n%s", term_quit());
                exit(1);
index 8a75283..3c9d04b 100644 (file)
@@ -322,24 +322,6 @@ struct ast_context {
        char name[0];                           /*!< Name of the context */
 };
 
-/*! \brief ast_app: A registered application */
-struct ast_app {
-       int (*execute)(struct ast_channel *chan, const char *data);
-       AST_DECLARE_STRING_FIELDS(
-               AST_STRING_FIELD(synopsis);     /*!< Synopsis text for 'show applications' */
-               AST_STRING_FIELD(description);  /*!< Description (help text) for 'show application &lt;name&gt;' */
-               AST_STRING_FIELD(syntax);       /*!< Syntax text for 'core show applications' */
-               AST_STRING_FIELD(arguments);    /*!< Arguments description */
-               AST_STRING_FIELD(seealso);      /*!< See also */
-       );
-#ifdef AST_XML_DOCS
-       enum ast_doc_src docsrc;                /*!< Where the documentation come from. */
-#endif
-       AST_RWLIST_ENTRY(ast_app) list;         /*!< Next app in list */
-       struct ast_module *module;              /*!< Module this app belongs to */
-       char name[0];                           /*!< Name of the application */
-};
-
 /*! \brief ast_state_cb: An extension state notify register item */
 struct ast_state_cb {
        /*! Watcher ID returned when registered. */
@@ -760,13 +742,6 @@ AST_MUTEX_DEFINE_STATIC(conlock);
  */
 AST_MUTEX_DEFINE_STATIC(context_merge_lock);
 
-/*!
- * \brief Registered applications container.
- *
- * It is sorted by application name.
- */
-static AST_RWLIST_HEAD_STATIC(apps, ast_app);
-
 static int stateid = 1;
 /*!
  * \note When holding this container's lock, do _not_ do
@@ -956,72 +931,6 @@ int check_contexts(char *file, int line )
 }
 #endif
 
-/*
-   \note This function is special. It saves the stack so that no matter
-   how many times it is called, it returns to the same place */
-int pbx_exec(struct ast_channel *c,    /*!< Channel */
-            struct ast_app *app,       /*!< Application */
-            const char *data)          /*!< Data for execution */
-{
-       int res;
-       struct ast_module_user *u = NULL;
-       const char *saved_c_appl;
-       const char *saved_c_data;
-
-       /* save channel values */
-       saved_c_appl= ast_channel_appl(c);
-       saved_c_data= ast_channel_data(c);
-
-       ast_channel_lock(c);
-       ast_channel_appl_set(c, app->name);
-       ast_channel_data_set(c, data);
-       ast_channel_publish_snapshot(c);
-       ast_channel_unlock(c);
-
-       if (app->module)
-               u = __ast_module_user_add(app->module, c);
-       res = app->execute(c, S_OR(data, ""));
-       if (app->module && u)
-               __ast_module_user_remove(app->module, u);
-       /* restore channel values */
-       ast_channel_appl_set(c, saved_c_appl);
-       ast_channel_data_set(c, saved_c_data);
-       return res;
-}
-
-static struct ast_app *pbx_findapp_nolock(const char *name)
-{
-       struct ast_app *cur;
-       int cmp;
-
-       AST_RWLIST_TRAVERSE(&apps, cur, list) {
-               cmp = strcasecmp(name, cur->name);
-               if (cmp > 0) {
-                       continue;
-               }
-               if (!cmp) {
-                       /* Found it. */
-                       break;
-               }
-               /* Not in container. */
-               cur = NULL;
-               break;
-       }
-
-       return cur;
-}
-
-struct ast_app *pbx_findapp(const char *app)
-{
-       struct ast_app *ret;
-
-       AST_RWLIST_RDLOCK(&apps);
-       ret = pbx_findapp_nolock(app);
-       AST_RWLIST_UNLOCK(&apps);
-
-       return ret;
-}
-
 static inline int include_valid(struct ast_include *i)
 {
        if (!i->hastime)
@@ -2911,11 +2820,11 @@ static int pbx_extension_helper(struct ast_channel *c, struct ast_context *con,
                        if (substitute) {
                                pbx_substitute_variables_helper(c, substitute, passdata, sizeof(passdata)-1);
                        }
-                       ast_debug(1, "Launching '%s'\n", app->name);
+                       ast_debug(1, "Launching '%s'\n", app_name(app));
                        if (VERBOSITY_ATLEAST(3)) {
                                ast_verb(3, "Executing [%s@%s:%d] " COLORIZE_FMT "(\"" COLORIZE_FMT "\", \"" COLORIZE_FMT "\") %s\n",
                                        exten, context, priority,
-                                       COLORIZE(COLOR_BRCYAN, 0, app->name),
+                                       COLORIZE(COLOR_BRCYAN, 0, app_name(app)),
                                        COLORIZE(COLOR_BRMAGENTA, 0, ast_channel_name(c)),
                                        COLORIZE(COLOR_BRMAGENTA, 0, passdata),
                                        "in new stack");
@@ -5298,218 +5207,10 @@ int ast_context_unlockmacro(const char *context)
        return ret;
 }
 
-/*! \brief Dynamically register a new dial plan application */
-int ast_register_application2(const char *app, int (*execute)(struct ast_channel *, const char *), const char *synopsis, const char *description, void *mod)
-{
-       struct ast_app *tmp;
-       struct ast_app *cur;
-       int length;
-#ifdef AST_XML_DOCS
-       char *tmpxml;
-#endif
-
-       AST_RWLIST_WRLOCK(&apps);
-       cur = pbx_findapp_nolock(app);
-       if (cur) {
-               ast_log(LOG_WARNING, "Already have an application '%s'\n", app);
-               AST_RWLIST_UNLOCK(&apps);
-               return -1;
-       }
-
-       length = sizeof(*tmp) + strlen(app) + 1;
-
-       if (!(tmp = ast_calloc(1, length))) {
-               AST_RWLIST_UNLOCK(&apps);
-               return -1;
-       }
-
-       if (ast_string_field_init(tmp, 128)) {
-               AST_RWLIST_UNLOCK(&apps);
-               ast_free(tmp);
-               return -1;
-       }
-
-       strcpy(tmp->name, app);
-       tmp->execute = execute;
-       tmp->module = mod;
-
-#ifdef AST_XML_DOCS
-       /* Try to lookup the docs in our XML documentation database */
-       if (ast_strlen_zero(synopsis) && ast_strlen_zero(description)) {
-               /* load synopsis */
-               tmpxml = ast_xmldoc_build_synopsis("application", app, ast_module_name(tmp->module));
-               ast_string_field_set(tmp, synopsis, tmpxml);
-               ast_free(tmpxml);
-
-               /* load description */
-               tmpxml = ast_xmldoc_build_description("application", app, ast_module_name(tmp->module));
-               ast_string_field_set(tmp, description, tmpxml);
-               ast_free(tmpxml);
-
-               /* load syntax */
-               tmpxml = ast_xmldoc_build_syntax("application", app, ast_module_name(tmp->module));
-               ast_string_field_set(tmp, syntax, tmpxml);
-               ast_free(tmpxml);
-
-               /* load arguments */
-               tmpxml = ast_xmldoc_build_arguments("application", app, ast_module_name(tmp->module));
-               ast_string_field_set(tmp, arguments, tmpxml);
-               ast_free(tmpxml);
-
-               /* load seealso */
-               tmpxml = ast_xmldoc_build_seealso("application", app, ast_module_name(tmp->module));
-               ast_string_field_set(tmp, seealso, tmpxml);
-               ast_free(tmpxml);
-               tmp->docsrc = AST_XML_DOC;
-       } else {
-#endif
-               ast_string_field_set(tmp, synopsis, synopsis);
-               ast_string_field_set(tmp, description, description);
-#ifdef AST_XML_DOCS
-               tmp->docsrc = AST_STATIC_DOC;
-       }
-#endif
-
-       /* Store in alphabetical order */
-       AST_RWLIST_TRAVERSE_SAFE_BEGIN(&apps, cur, list) {
-               if (strcasecmp(tmp->name, cur->name) < 0) {
-                       AST_RWLIST_INSERT_BEFORE_CURRENT(tmp, list);
-                       break;
-               }
-       }
-       AST_RWLIST_TRAVERSE_SAFE_END;
-       if (!cur)
-               AST_RWLIST_INSERT_TAIL(&apps, tmp, list);
-
-       ast_verb(2, "Registered application '" COLORIZE_FMT "'\n", COLORIZE(COLOR_BRCYAN, 0, tmp->name));
-
-       AST_RWLIST_UNLOCK(&apps);
-
-       return 0;
-}
-
 /*
  * Help for CLI commands ...
  */
 
-static void print_app_docs(struct ast_app *aa, int fd)
-{
-#ifdef AST_XML_DOCS
-       char *synopsis = NULL, *description = NULL, *arguments = NULL, *seealso = NULL;
-       if (aa->docsrc == AST_XML_DOC) {
-               synopsis = ast_xmldoc_printable(S_OR(aa->synopsis, "Not available"), 1);
-               description = ast_xmldoc_printable(S_OR(aa->description, "Not available"), 1);
-               arguments = ast_xmldoc_printable(S_OR(aa->arguments, "Not available"), 1);
-               seealso = ast_xmldoc_printable(S_OR(aa->seealso, "Not available"), 1);
-               if (!synopsis || !description || !arguments || !seealso) {
-                       goto free_docs;
-               }
-               ast_cli(fd, "\n"
-                       "%s  -= Info about application '%s' =- %s\n\n"
-                       COLORIZE_FMT "\n"
-                       "%s\n\n"
-                       COLORIZE_FMT "\n"
-                       "%s\n\n"
-                       COLORIZE_FMT "\n"
-                       "%s%s%s\n\n"
-                       COLORIZE_FMT "\n"
-                       "%s\n\n"
-                       COLORIZE_FMT "\n"
-                       "%s\n",
-                       ast_term_color(COLOR_MAGENTA, 0), aa->name, ast_term_reset(),
-                       COLORIZE(COLOR_MAGENTA, 0, "[Synopsis]"), synopsis,
-                       COLORIZE(COLOR_MAGENTA, 0, "[Description]"), description,
-                       COLORIZE(COLOR_MAGENTA, 0, "[Syntax]"),
-                               ast_term_color(COLOR_CYAN, 0), S_OR(aa->syntax, "Not available"), ast_term_reset(),
-                       COLORIZE(COLOR_MAGENTA, 0, "[Arguments]"), arguments,
-                       COLORIZE(COLOR_MAGENTA, 0, "[See Also]"), seealso);
-free_docs:
-               ast_free(synopsis);
-               ast_free(description);
-               ast_free(arguments);
-               ast_free(seealso);
-       } else
-#endif
-       {
-               ast_cli(fd, "\n"
-                       "%s  -= Info about application '%s' =- %s\n\n"
-                       COLORIZE_FMT "\n"
-                       COLORIZE_FMT "\n\n"
-                       COLORIZE_FMT "\n"
-                       COLORIZE_FMT "\n\n"
-                       COLORIZE_FMT "\n"
-                       COLORIZE_FMT "\n\n"
-                       COLORIZE_FMT "\n"
-                       COLORIZE_FMT "\n\n"
-                       COLORIZE_FMT "\n"
-                       COLORIZE_FMT "\n",
-                       ast_term_color(COLOR_MAGENTA, 0), aa->name, ast_term_reset(),
-                       COLORIZE(COLOR_MAGENTA, 0, "[Synopsis]"),
-                       COLORIZE(COLOR_CYAN, 0, S_OR(aa->synopsis, "Not available")),
-                       COLORIZE(COLOR_MAGENTA, 0, "[Description]"),
-                       COLORIZE(COLOR_CYAN, 0, S_OR(aa->description, "Not available")),
-                       COLORIZE(COLOR_MAGENTA, 0, "[Syntax]"),
-                       COLORIZE(COLOR_CYAN, 0, S_OR(aa->syntax, "Not available")),
-                       COLORIZE(COLOR_MAGENTA, 0, "[Arguments]"),
-                       COLORIZE(COLOR_CYAN, 0, S_OR(aa->arguments, "Not available")),
-                       COLORIZE(COLOR_MAGENTA, 0, "[See Also]"),
-                       COLORIZE(COLOR_CYAN, 0, S_OR(aa->seealso, "Not available")));
-       }
-}
-
-/*
- * \brief 'show application' CLI command implementation function...
- */
-static char *handle_show_application(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
-{
-       struct ast_app *aa;
-       int app, no_registered_app = 1;
-
-       switch (cmd) {
-       case CLI_INIT:
-               e->command = "core show application";
-               e->usage =
-                       "Usage: core show application <application> [<application> [<application> [...]]]\n"
-                       "       Describes a particular application.\n";
-               return NULL;
-       case CLI_GENERATE:
-               /*
-                * There is a possibility to show informations about more than one
-                * application at one time. You can type 'show application Dial Echo' and
-                * you will see informations about these two applications ...
-                */
-               return ast_complete_applications(a->line, a->word, a->n);
-       }
-
-       if (a->argc < 4) {
-               return CLI_SHOWUSAGE;
-       }
-
-       AST_RWLIST_RDLOCK(&apps);
-       AST_RWLIST_TRAVERSE(&apps, aa, list) {
-               /* Check for each app that was supplied as an argument */
-               for (app = 3; app < a->argc; app++) {
-                       if (strcasecmp(aa->name, a->argv[app])) {
-                               continue;
-                       }
-
-                       /* We found it! */
-                       no_registered_app = 0;
-
-                       print_app_docs(aa, a->fd);
-               }
-       }
-       AST_RWLIST_UNLOCK(&apps);
-
-       /* we found at least one app? no? */
-       if (no_registered_app) {
-               ast_cli(a->fd, "Your application(s) is (are) not registered\n");
-               return CLI_FAILURE;
-       }
-
-       return CLI_SUCCESS;
-}
-
 /*! \brief  handle_show_hints: CLI support for listing registered dial plan hints */
 static char *handle_show_hints(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
@@ -5750,89 +5451,6 @@ static char *handle_eat_memory(struct ast_cli_entry *e, int cmd, struct ast_cli_
 }
 #endif
 
-static char *handle_show_applications(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
-{
-       struct ast_app *aa;
-       int like = 0, describing = 0;
-       int total_match = 0;    /* Number of matches in like clause */
-       int total_apps = 0;     /* Number of apps registered */
-       static const char * const choices[] = { "like", "describing", NULL };
-
-       switch (cmd) {
-       case CLI_INIT:
-               e->command = "core show applications [like|describing]";
-               e->usage =
-                       "Usage: core show applications [{like|describing} <text>]\n"
-                       "       List applications which are currently available.\n"
-                       "       If 'like', <text> will be a substring of the app name\n"
-                       "       If 'describing', <text> will be a substring of the description\n";
-               return NULL;
-       case CLI_GENERATE:
-               return (a->pos != 3) ? NULL : ast_cli_complete(a->word, choices, a->n);
-       }
-
-       AST_RWLIST_RDLOCK(&apps);
-
-       if (AST_RWLIST_EMPTY(&apps)) {
-               ast_cli(a->fd, "There are no registered applications\n");
-               AST_RWLIST_UNLOCK(&apps);
-               return CLI_SUCCESS;
-       }
-
-       /* core list applications like <keyword> */
-       if ((a->argc == 5) && (!strcmp(a->argv[3], "like"))) {
-               like = 1;
-       } else if ((a->argc > 4) && (!strcmp(a->argv[3], "describing"))) {
-               describing = 1;
-       }
-
-       /* core list applications describing <keyword1> [<keyword2>] [...] */
-       if ((!like) && (!describing)) {
-               ast_cli(a->fd, "    -= Registered Asterisk Applications =-\n");
-       } else {
-               ast_cli(a->fd, "    -= Matching Asterisk Applications =-\n");
-       }
-
-       AST_RWLIST_TRAVERSE(&apps, aa, list) {
-               int printapp = 0;
-               total_apps++;
-               if (like) {
-                       if (strcasestr(aa->name, a->argv[4])) {
-                               printapp = 1;
-                               total_match++;
-                       }
-               } else if (describing) {
-                       if (aa->description) {
-                               /* Match all words on command line */
-                               int i;
-                               printapp = 1;
-                               for (i = 4; i < a->argc; i++) {
-                                       if (!strcasestr(aa->description, a->argv[i])) {
-                                               printapp = 0;
-                                       } else {
-                                               total_match++;
-                                       }
-                               }
-                       }
-               } else {
-                       printapp = 1;
-               }
-
-               if (printapp) {
-                       ast_cli(a->fd,"  %20s: %s\n", aa->name, aa->synopsis ? aa->synopsis : "<Synopsis not available>");
-               }
-       }
-       if ((!like) && (!describing)) {
-               ast_cli(a->fd, "    -= %d Applications Registered =-\n",total_apps);
-       } else {
-               ast_cli(a->fd, "    -= %d Applications Matching =-\n",total_match);
-       }
-
-       AST_RWLIST_UNLOCK(&apps);
-
-       return CLI_SUCCESS;
-}
-
 /*
  * 'show dialplan' CLI command implementation functions ...
  */
@@ -6516,7 +6134,6 @@ static struct ast_cli_entry pbx_cli[] = {
 #if 0
        AST_CLI_DEFINE(handle_eat_memory, "Eats all available memory"),
 #endif
-       AST_CLI_DEFINE(handle_show_applications, "Shows registered dialplan applications"),
        AST_CLI_DEFINE(handle_show_hints, "Show dialplan hints"),
        AST_CLI_DEFINE(handle_show_hint, "Show dialplan hint"),
 #ifdef AST_DEVMODE
@@ -6524,14 +6141,13 @@ static struct ast_cli_entry pbx_cli[] = {
 #endif
        AST_CLI_DEFINE(handle_show_hangup_all, "Show hangup handlers of all channels"),
        AST_CLI_DEFINE(handle_show_hangup_channel, "Show hangup handlers of a specified channel"),
-       AST_CLI_DEFINE(handle_show_application, "Describe a specific dialplan application"),
        AST_CLI_DEFINE(handle_show_dialplan, "Show dialplan"),
        AST_CLI_DEFINE(handle_debug_dialplan, "Show fast extension pattern matching data structures"),
        AST_CLI_DEFINE(handle_unset_extenpatternmatchnew, "Use the Old extension pattern matching algorithm."),
        AST_CLI_DEFINE(handle_set_extenpatternmatchnew, "Use the New extension pattern matching algorithm."),
 };
 
-static void unreference_cached_app(struct ast_app *app)
+void unreference_cached_app(struct ast_app *app)
 {
        struct ast_context *context = NULL;
        struct ast_exten *eroot = NULL, *e = NULL;
@@ -6550,36 +6166,6 @@ static void unreference_cached_app(struct ast_app *app)
        return;
 }
 
-int ast_unregister_application(const char *app)
-{
-       struct ast_app *cur;
-       int cmp;
-
-       AST_RWLIST_WRLOCK(&apps);
-       AST_RWLIST_TRAVERSE_SAFE_BEGIN(&apps, cur, list) {
-               cmp = strcasecmp(app, cur->name);
-               if (cmp > 0) {
-                       continue;
-               }
-               if (!cmp) {
-                       /* Found it. */
-                       unreference_cached_app(cur);
-                       AST_RWLIST_REMOVE_CURRENT(list);
-                       ast_verb(2, "Unregistered application '%s'\n", cur->name);
-                       ast_string_field_free_memory(cur);
-                       ast_free(cur);
-                       break;
-               }
-               /* Not in container. */
-               cur = NULL;
-               break;
-       }
-       AST_RWLIST_TRAVERSE_SAFE_END;
-       AST_RWLIST_UNLOCK(&apps);
-
-       return cur ? 0 : -1;
-}
-
 struct ast_context *ast_context_find_or_create(struct ast_context **extcontexts, struct ast_hashtab *exttable, const char *name, const char *registrar)
 {
        struct ast_context *tmp, **local_contexts;
@@ -9179,37 +8765,6 @@ int ast_async_parseable_goto(struct ast_channel *chan, const char *goto_string)
        return pbx_parseable_goto(chan, goto_string, 1);
 }
 
-char *ast_complete_applications(const char *line, const char *word, int state)
-{
-       struct ast_app *app;
-       int which = 0;
-       int cmp;
-       char *ret = NULL;
-       size_t wordlen = strlen(word);
-
-       AST_RWLIST_RDLOCK(&apps);
-       AST_RWLIST_TRAVERSE(&apps, app, list) {
-               cmp = strncasecmp(word, app->name, wordlen);
-               if (cmp > 0) {
-                       continue;
-               }
-               if (!cmp) {
-                       /* Found match. */
-                       if (++which <= state) {
-                               /* Not enough matches. */
-                               continue;
-                       }
-                       ret = ast_strdup(app->name);
-                       break;
-               }
-               /* Not in container. */
-               break;
-       }
-       AST_RWLIST_UNLOCK(&apps);
-
-       return ret;
-}
-
 static int hint_hash(const void *obj, const int flags)
 {
        const struct ast_hint *hint = obj;
diff --git a/main/pbx_app.c b/main/pbx_app.c
new file mode 100644 (file)
index 0000000..b7e797d
--- /dev/null
@@ -0,0 +1,510 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2016, CFWare, LLC
+ *
+ * Corey Farrell <git@cfware.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Custom function management routines.
+ *
+ * \author Corey Farrell <git@cfware.com>
+ */
+
+/*** MODULEINFO
+       <support_level>core</support_level>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_REGISTER_FILE()
+
+#include "asterisk/_private.h"
+#include "asterisk/cli.h"
+#include "asterisk/linkedlists.h"
+#include "asterisk/module.h"
+#include "asterisk/pbx.h"
+#include "asterisk/stasis_channels.h"
+#include "asterisk/strings.h"
+#include "asterisk/term.h"
+#include "asterisk/utils.h"
+#include "asterisk/xmldoc.h"
+#include "pbx_private.h"
+
+/*! \brief ast_app: A registered application */
+struct ast_app {
+       int (*execute)(struct ast_channel *chan, const char *data);
+       AST_DECLARE_STRING_FIELDS(
+               AST_STRING_FIELD(synopsis);     /*!< Synopsis text for 'show applications' */
+               AST_STRING_FIELD(description);  /*!< Description (help text) for 'show application &lt;name&gt;' */
+               AST_STRING_FIELD(syntax);       /*!< Syntax text for 'core show applications' */
+               AST_STRING_FIELD(arguments);    /*!< Arguments description */
+               AST_STRING_FIELD(seealso);      /*!< See also */
+       );
+#ifdef AST_XML_DOCS
+       enum ast_doc_src docsrc;                /*!< Where the documentation come from. */
+#endif
+       AST_RWLIST_ENTRY(ast_app) list;         /*!< Next app in list */
+       struct ast_module *module;              /*!< Module this app belongs to */
+       char name[0];                           /*!< Name of the application */
+};
+
+/*!
+ * \brief Registered applications container.
+ *
+ * It is sorted by application name.
+ */
+static AST_RWLIST_HEAD_STATIC(apps, ast_app);
+
+static struct ast_app *pbx_findapp_nolock(const char *name)
+{
+       struct ast_app *cur;
+       int cmp;
+
+       AST_RWLIST_TRAVERSE(&apps, cur, list) {
+               cmp = strcasecmp(name, cur->name);
+               if (cmp > 0) {
+                       continue;
+               }
+               if (!cmp) {
+                       /* Found it. */
+                       break;
+               }
+               /* Not in container. */
+               cur = NULL;
+               break;
+       }
+
+       return cur;
+}
+
+struct ast_app *pbx_findapp(const char *app)
+{
+       struct ast_app *ret;
+
+       AST_RWLIST_RDLOCK(&apps);
+       ret = pbx_findapp_nolock(app);
+       AST_RWLIST_UNLOCK(&apps);
+
+       return ret;
+}
+
+/*! \brief Dynamically register a new dial plan application */
+int ast_register_application2(const char *app, int (*execute)(struct ast_channel *, const char *), const char *synopsis, const char *description, void *mod)
+{
+       struct ast_app *tmp;
+       struct ast_app *cur;
+       int length;
+#ifdef AST_XML_DOCS
+       char *tmpxml;
+#endif
+
+       AST_RWLIST_WRLOCK(&apps);
+       cur = pbx_findapp_nolock(app);
+       if (cur) {
+               ast_log(LOG_WARNING, "Already have an application '%s'\n", app);
+               AST_RWLIST_UNLOCK(&apps);
+               return -1;
+       }
+
+       length = sizeof(*tmp) + strlen(app) + 1;
+
+       if (!(tmp = ast_calloc(1, length))) {
+               AST_RWLIST_UNLOCK(&apps);
+               return -1;
+       }
+
+       if (ast_string_field_init(tmp, 128)) {
+               AST_RWLIST_UNLOCK(&apps);
+               ast_free(tmp);
+               return -1;
+       }
+
+       strcpy(tmp->name, app);
+       tmp->execute = execute;
+       tmp->module = mod;
+
+#ifdef AST_XML_DOCS
+       /* Try to lookup the docs in our XML documentation database */
+       if (ast_strlen_zero(synopsis) && ast_strlen_zero(description)) {
+               /* load synopsis */
+               tmpxml = ast_xmldoc_build_synopsis("application", app, ast_module_name(tmp->module));
+               ast_string_field_set(tmp, synopsis, tmpxml);
+               ast_free(tmpxml);
+
+               /* load description */
+               tmpxml = ast_xmldoc_build_description("application", app, ast_module_name(tmp->module));
+               ast_string_field_set(tmp, description, tmpxml);
+               ast_free(tmpxml);
+
+               /* load syntax */
+               tmpxml = ast_xmldoc_build_syntax("application", app, ast_module_name(tmp->module));
+               ast_string_field_set(tmp, syntax, tmpxml);
+               ast_free(tmpxml);
+
+               /* load arguments */
+               tmpxml = ast_xmldoc_build_arguments("application", app, ast_module_name(tmp->module));
+               ast_string_field_set(tmp, arguments, tmpxml);
+               ast_free(tmpxml);
+
+               /* load seealso */
+               tmpxml = ast_xmldoc_build_seealso("application", app, ast_module_name(tmp->module));
+               ast_string_field_set(tmp, seealso, tmpxml);
+               ast_free(tmpxml);
+               tmp->docsrc = AST_XML_DOC;
+       } else {
+#endif
+               ast_string_field_set(tmp, synopsis, synopsis);
+               ast_string_field_set(tmp, description, description);
+#ifdef AST_XML_DOCS
+               tmp->docsrc = AST_STATIC_DOC;
+       }
+#endif
+
+       /* Store in alphabetical order */
+       AST_RWLIST_TRAVERSE_SAFE_BEGIN(&apps, cur, list) {
+               if (strcasecmp(tmp->name, cur->name) < 0) {
+                       AST_RWLIST_INSERT_BEFORE_CURRENT(tmp, list);
+                       break;
+               }
+       }
+       AST_RWLIST_TRAVERSE_SAFE_END;
+       if (!cur)
+               AST_RWLIST_INSERT_TAIL(&apps, tmp, list);
+
+       ast_verb(2, "Registered application '" COLORIZE_FMT "'\n", COLORIZE(COLOR_BRCYAN, 0, tmp->name));
+
+       AST_RWLIST_UNLOCK(&apps);
+
+       return 0;
+}
+
+static void print_app_docs(struct ast_app *aa, int fd)
+{
+#ifdef AST_XML_DOCS
+       char *synopsis = NULL, *description = NULL, *arguments = NULL, *seealso = NULL;
+       if (aa->docsrc == AST_XML_DOC) {
+               synopsis = ast_xmldoc_printable(S_OR(aa->synopsis, "Not available"), 1);
+               description = ast_xmldoc_printable(S_OR(aa->description, "Not available"), 1);
+               arguments = ast_xmldoc_printable(S_OR(aa->arguments, "Not available"), 1);
+               seealso = ast_xmldoc_printable(S_OR(aa->seealso, "Not available"), 1);
+               if (!synopsis || !description || !arguments || !seealso) {
+                       goto free_docs;
+               }
+               ast_cli(fd, "\n"
+                       "%s  -= Info about application '%s' =- %s\n\n"
+                       COLORIZE_FMT "\n"
+                       "%s\n\n"
+                       COLORIZE_FMT "\n"
+                       "%s\n\n"
+                       COLORIZE_FMT "\n"
+                       "%s%s%s\n\n"
+                       COLORIZE_FMT "\n"
+                       "%s\n\n"
+                       COLORIZE_FMT "\n"
+                       "%s\n",
+                       ast_term_color(COLOR_MAGENTA, 0), aa->name, ast_term_reset(),
+                       COLORIZE(COLOR_MAGENTA, 0, "[Synopsis]"), synopsis,
+                       COLORIZE(COLOR_MAGENTA, 0, "[Description]"), description,
+                       COLORIZE(COLOR_MAGENTA, 0, "[Syntax]"),
+                               ast_term_color(COLOR_CYAN, 0), S_OR(aa->syntax, "Not available"), ast_term_reset(),
+                       COLORIZE(COLOR_MAGENTA, 0, "[Arguments]"), arguments,
+                       COLORIZE(COLOR_MAGENTA, 0, "[See Also]"), seealso);
+free_docs:
+               ast_free(synopsis);
+               ast_free(description);
+               ast_free(arguments);
+               ast_free(seealso);
+       } else
+#endif
+       {
+               ast_cli(fd, "\n"
+                       "%s  -= Info about application '%s' =- %s\n\n"
+                       COLORIZE_FMT "\n"
+                       COLORIZE_FMT "\n\n"
+                       COLORIZE_FMT "\n"
+                       COLORIZE_FMT "\n\n"
+                       COLORIZE_FMT "\n"
+                       COLORIZE_FMT "\n\n"
+                       COLORIZE_FMT "\n"
+                       COLORIZE_FMT "\n\n"
+                       COLORIZE_FMT "\n"
+                       COLORIZE_FMT "\n",
+                       ast_term_color(COLOR_MAGENTA, 0), aa->name, ast_term_reset(),
+                       COLORIZE(COLOR_MAGENTA, 0, "[Synopsis]"),
+                       COLORIZE(COLOR_CYAN, 0, S_OR(aa->synopsis, "Not available")),
+                       COLORIZE(COLOR_MAGENTA, 0, "[Description]"),
+                       COLORIZE(COLOR_CYAN, 0, S_OR(aa->description, "Not available")),
+                       COLORIZE(COLOR_MAGENTA, 0, "[Syntax]"),
+                       COLORIZE(COLOR_CYAN, 0, S_OR(aa->syntax, "Not available")),
+                       COLORIZE(COLOR_MAGENTA, 0, "[Arguments]"),
+                       COLORIZE(COLOR_CYAN, 0, S_OR(aa->arguments, "Not available")),
+                       COLORIZE(COLOR_MAGENTA, 0, "[See Also]"),
+                       COLORIZE(COLOR_CYAN, 0, S_OR(aa->seealso, "Not available")));
+       }
+}
+
+/*
+ * \brief 'show application' CLI command implementation function...
+ */
+static char *handle_show_application(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+       struct ast_app *aa;
+       int app, no_registered_app = 1;
+
+       switch (cmd) {
+       case CLI_INIT:
+               e->command = "core show application";
+               e->usage =
+                       "Usage: core show application <application> [<application> [<application> [...]]]\n"
+                       "       Describes a particular application.\n";
+               return NULL;
+       case CLI_GENERATE:
+               /*
+                * There is a possibility to show informations about more than one
+                * application at one time. You can type 'show application Dial Echo' and
+                * you will see informations about these two applications ...
+                */
+               return ast_complete_applications(a->line, a->word, a->n);
+       }
+
+       if (a->argc < 4) {
+               return CLI_SHOWUSAGE;
+       }
+
+       AST_RWLIST_RDLOCK(&apps);
+       AST_RWLIST_TRAVERSE(&apps, aa, list) {
+               /* Check for each app that was supplied as an argument */
+               for (app = 3; app < a->argc; app++) {
+                       if (strcasecmp(aa->name, a->argv[app])) {
+                               continue;
+                       }
+
+                       /* We found it! */
+                       no_registered_app = 0;
+
+                       print_app_docs(aa, a->fd);
+               }
+       }
+       AST_RWLIST_UNLOCK(&apps);
+
+       /* we found at least one app? no? */
+       if (no_registered_app) {
+               ast_cli(a->fd, "Your application(s) is (are) not registered\n");
+               return CLI_FAILURE;
+       }
+
+       return CLI_SUCCESS;
+}
+
+static char *handle_show_applications(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+       struct ast_app *aa;
+       int like = 0, describing = 0;
+       int total_match = 0;    /* Number of matches in like clause */
+       int total_apps = 0;     /* Number of apps registered */
+       static const char * const choices[] = { "like", "describing", NULL };
+
+       switch (cmd) {
+       case CLI_INIT:
+               e->command = "core show applications [like|describing]";
+               e->usage =
+                       "Usage: core show applications [{like|describing} <text>]\n"
+                       "       List applications which are currently available.\n"
+                       "       If 'like', <text> will be a substring of the app name\n"
+                       "       If 'describing', <text> will be a substring of the description\n";
+               return NULL;
+       case CLI_GENERATE:
+               return (a->pos != 3) ? NULL : ast_cli_complete(a->word, choices, a->n);
+       }
+
+       AST_RWLIST_RDLOCK(&apps);
+
+       if (AST_RWLIST_EMPTY(&apps)) {
+               ast_cli(a->fd, "There are no registered applications\n");
+               AST_RWLIST_UNLOCK(&apps);
+               return CLI_SUCCESS;
+       }
+
+       /* core list applications like <keyword> */
+       if ((a->argc == 5) && (!strcmp(a->argv[3], "like"))) {
+               like = 1;
+       } else if ((a->argc > 4) && (!strcmp(a->argv[3], "describing"))) {
+               describing = 1;
+       }
+
+       /* core list applications describing <keyword1> [<keyword2>] [...] */
+       if ((!like) && (!describing)) {
+               ast_cli(a->fd, "    -= Registered Asterisk Applications =-\n");
+       } else {
+               ast_cli(a->fd, "    -= Matching Asterisk Applications =-\n");
+       }
+
+       AST_RWLIST_TRAVERSE(&apps, aa, list) {
+               int printapp = 0;
+               total_apps++;
+               if (like) {
+                       if (strcasestr(aa->name, a->argv[4])) {
+                               printapp = 1;
+                               total_match++;
+                       }
+               } else if (describing) {
+                       if (aa->description) {
+                               /* Match all words on command line */
+                               int i;
+                               printapp = 1;
+                               for (i = 4; i < a->argc; i++) {
+                                       if (!strcasestr(aa->description, a->argv[i])) {
+                                               printapp = 0;
+                                       } else {
+                                               total_match++;
+                                       }
+                               }
+                       }
+               } else {
+                       printapp = 1;
+               }
+
+               if (printapp) {
+                       ast_cli(a->fd,"  %20s: %s\n", aa->name, aa->synopsis ? aa->synopsis : "<Synopsis not available>");
+               }
+       }
+       if ((!like) && (!describing)) {
+               ast_cli(a->fd, "    -= %d Applications Registered =-\n",total_apps);
+       } else {
+               ast_cli(a->fd, "    -= %d Applications Matching =-\n",total_match);
+       }
+
+       AST_RWLIST_UNLOCK(&apps);
+
+       return CLI_SUCCESS;
+}
+
+int ast_unregister_application(const char *app)
+{
+       struct ast_app *cur;
+       int cmp;
+
+       AST_RWLIST_WRLOCK(&apps);
+       AST_RWLIST_TRAVERSE_SAFE_BEGIN(&apps, cur, list) {
+               cmp = strcasecmp(app, cur->name);
+               if (cmp > 0) {
+                       continue;
+               }
+               if (!cmp) {
+                       /* Found it. */
+                       unreference_cached_app(cur);
+                       AST_RWLIST_REMOVE_CURRENT(list);
+                       ast_verb(2, "Unregistered application '%s'\n", cur->name);
+                       ast_string_field_free_memory(cur);
+                       ast_free(cur);
+                       break;
+               }
+               /* Not in container. */
+               cur = NULL;
+               break;
+       }
+       AST_RWLIST_TRAVERSE_SAFE_END;
+       AST_RWLIST_UNLOCK(&apps);
+
+       return cur ? 0 : -1;
+}
+
+char *ast_complete_applications(const char *line, const char *word, int state)
+{
+       struct ast_app *app;
+       int which = 0;
+       int cmp;
+       char *ret = NULL;
+       size_t wordlen = strlen(word);
+
+       AST_RWLIST_RDLOCK(&apps);
+       AST_RWLIST_TRAVERSE(&apps, app, list) {
+               cmp = strncasecmp(word, app->name, wordlen);
+               if (cmp > 0) {
+                       continue;
+               }
+               if (!cmp) {
+                       /* Found match. */
+                       if (++which <= state) {
+                               /* Not enough matches. */
+                               continue;
+                       }
+                       ret = ast_strdup(app->name);
+                       break;
+               }
+               /* Not in container. */
+               break;
+       }
+       AST_RWLIST_UNLOCK(&apps);
+
+       return ret;
+}
+
+const char *app_name(struct ast_app *app)
+{
+       return app->name;
+}
+
+/*
+   \note This function is special. It saves the stack so that no matter
+   how many times it is called, it returns to the same place */
+int pbx_exec(struct ast_channel *c,    /*!< Channel */
+            struct ast_app *app,       /*!< Application */
+            const char *data)          /*!< Data for execution */
+{
+       int res;
+       struct ast_module_user *u = NULL;
+       const char *saved_c_appl;
+       const char *saved_c_data;
+
+       /* save channel values */
+       saved_c_appl= ast_channel_appl(c);
+       saved_c_data= ast_channel_data(c);
+
+       ast_channel_lock(c);
+       ast_channel_appl_set(c, app->name);
+       ast_channel_data_set(c, data);
+       ast_channel_publish_snapshot(c);
+       ast_channel_unlock(c);
+
+       if (app->module)
+               u = __ast_module_user_add(app->module, c);
+       res = app->execute(c, S_OR(data, ""));
+       if (app->module && u)
+               __ast_module_user_remove(app->module, u);
+       /* restore channel values */
+       ast_channel_appl_set(c, saved_c_appl);
+       ast_channel_data_set(c, saved_c_data);
+       return res;
+}
+
+static struct ast_cli_entry app_cli[] = {
+       AST_CLI_DEFINE(handle_show_applications, "Shows registered dialplan applications"),
+       AST_CLI_DEFINE(handle_show_application, "Describe a specific dialplan application"),
+};
+
+static void unload_pbx_app(void)
+{
+       ast_cli_unregister_multiple(app_cli, ARRAY_LEN(app_cli));
+}
+
+int load_pbx_app(void)
+{
+       ast_cli_register_multiple(app_cli, ARRAY_LEN(app_cli));
+       ast_register_cleanup(unload_pbx_app);
+
+       return 0;
+}
index 87abefa..e091941 100644 (file)
@@ -28,6 +28,9 @@ int raise_exception(struct ast_channel *chan, const char *reason, int priority);
 void wait_for_hangup(struct ast_channel *chan, const void *data);
 void set_ext_pri(struct ast_channel *c, const char *exten, int pri);
 
+/*! pbx.c function needed by pbx_app.c */
+void unreference_cached_app(struct ast_app *app);
+
 /*! pbx_builtins.c functions needed by pbx.c */
 int indicate_congestion(struct ast_channel *, const char *);
 int indicate_busy(struct ast_channel *, const char *);
@@ -35,6 +38,8 @@ int indicate_busy(struct ast_channel *, const char *);
 /*! pbx_switch.c functions needed by pbx.c */
 struct ast_switch *pbx_findswitch(const char *sw);
 
+/*! pbx_app.c functions needed by pbx.c */
+const char *app_name(struct ast_app *app);
 
 #define VAR_BUF_SIZE 4096