summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES13
-rw-r--r--apps/app_dial.c18
-rw-r--r--apps/app_queue.c20
-rw-r--r--apps/app_voicemail.c45
-rw-r--r--cel/cel_radius.c4
-rw-r--r--include/asterisk.h20
-rw-r--r--include/asterisk/_private.h4
-rw-r--r--include/asterisk/app.h2
-rw-r--r--include/asterisk/module.h2
-rw-r--r--include/asterisk/stasis_cache_pattern.h21
-rw-r--r--main/app.c4
-rw-r--r--main/asterisk.c20
-rw-r--r--main/endpoints.c12
-rw-r--r--main/format_cap.c7
-rw-r--r--main/pbx.c2080
-rw-r--r--main/pbx_app.c510
-rw-r--r--main/pbx_builtins.c66
-rw-r--r--main/pbx_functions.c2
-rw-r--r--main/pbx_hangup_handler.c300
-rw-r--r--main/pbx_private.h9
-rw-r--r--main/pbx_switch.c133
-rw-r--r--main/pbx_timing.c294
-rw-r--r--main/pbx_variables.c1180
-rw-r--r--main/stasis_cache_pattern.c21
-rw-r--r--res/res_mwi_external.c13
25 files changed, 2596 insertions, 2204 deletions
diff --git a/CHANGES b/CHANGES
index fed0dd33f..d9150a932 100644
--- a/CHANGES
+++ b/CHANGES
@@ -33,6 +33,19 @@ res_pjsip_history
information on filtering, view the current CLI help for the
'pjsip show history' command.
+Voicemail
+------------------
+ * app_voicemail and res_mwi_external can now be built together. The default
+ remains to build app_voicemail and not res_mwi_external but if they are
+ both built, the load order will cause res_mwi_external to load first and
+ app_voicemail will be skipped. Use 'preload=app_voicemail.so' in
+ modules.conf to force app_voicemail to be the voicemail provider.
+
+Queue
+-------------------
+ * Added field ReasonPause on QueueMemberStatus if set when paused, the reason
+ the queue member was paused.
+
------------------------------------------------------------------------------
--- Functionality changes from Asterisk 13.6.0 to Asterisk 13.7.0 ------------
------------------------------------------------------------------------------
diff --git a/apps/app_dial.c b/apps/app_dial.c
index d65dcae0a..bc4f8a574 100644
--- a/apps/app_dial.c
+++ b/apps/app_dial.c
@@ -2151,6 +2151,24 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
return -1;
}
+ if (ast_check_hangup_locked(chan)) {
+ /*
+ * Caller hung up before we could dial. If dial is executed
+ * within an AGI then the AGI has likely eaten all queued
+ * frames before executing the dial in DeadAGI mode. With
+ * the caller hung up and no pending frames from the caller's
+ * read queue, dial would not know that the call has hung up
+ * until a called channel answers. It is rather annoying to
+ * whoever just answered the non-existent call.
+ *
+ * Dial should not continue execution in DeadAGI mode, hangup
+ * handlers, or the h exten.
+ */
+ ast_verb(3, "Caller hung up before dial.\n");
+ pbx_builtin_setvar_helper(chan, "DIALSTATUS", "CANCEL");
+ return -1;
+ }
+
parse = ast_strdupa(data);
AST_STANDARD_APP_ARGS(args, parse);
diff --git a/apps/app_queue.c b/apps/app_queue.c
index 45d00939c..c2450765c 100644
--- a/apps/app_queue.c
+++ b/apps/app_queue.c
@@ -1002,6 +1002,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<enum name="1"/>
</enumlist>
</parameter>
+ <parameter name="PausedReason">
+ <para>If set when paused, the reason the queue member was paused.</para>
+ </parameter>
<parameter name="Ringinuse">
<enumlist>
<enum name="0"/>
@@ -1493,6 +1496,7 @@ struct member {
int realtime; /*!< Is this member realtime? */
int status; /*!< Status of queue member */
int paused; /*!< Are we paused (not accepting calls)? */
+ char reason_paused[80]; /*!< Reason of paused if member is paused */
int queuepos; /*!< In what order (pertains to certain strategies) should this member be called? */
time_t lastcall; /*!< When last successful call was hungup */
struct call_queue *lastqueue; /*!< Last queue we received a call */
@@ -2155,7 +2159,7 @@ static void queue_publish_member_blob(struct stasis_message_type *type, struct a
static struct ast_json *queue_member_blob_create(struct call_queue *q, struct member *mem)
{
- return ast_json_pack("{s: s, s: s, s: s, s: s, s: s, s: i, s: i, s: i, s: i, s: i, s: i}",
+ return ast_json_pack("{s: s, s: s, s: s, s: s, s: s, s: i, s: i, s: i, s: i, s: i, s: s, s: i}",
"Queue", q->name,
"MemberName", mem->membername,
"Interface", mem->interface,
@@ -2166,6 +2170,7 @@ static struct ast_json *queue_member_blob_create(struct call_queue *q, struct me
"LastCall", (int)mem->lastcall,
"Status", mem->status,
"Paused", mem->paused,
+ "PausedReason", mem->reason_paused,
"Ringinuse", mem->ringinuse);
}
@@ -7055,6 +7060,14 @@ static void set_queue_member_pause(struct call_queue *q, struct member *mem, con
}
mem->paused = paused;
+ if (paused) {
+ if (!ast_strlen_zero(reason)) {
+ ast_copy_string(mem->reason_paused, reason, sizeof(mem->reason_paused));
+ }
+ } else {
+ ast_copy_string(mem->reason_paused, "", sizeof(mem->reason_paused));
+ }
+
ast_devstate_changed(mem->paused ? QUEUE_PAUSED_DEVSTATE : QUEUE_UNPAUSED_DEVSTATE,
AST_DEVSTATE_CACHABLE, "Queue:%s_pause_%s", q->name, mem->interface);
@@ -9513,10 +9526,11 @@ static int manager_queues_status(struct mansession *s, const struct message *m)
"LastCall: %d\r\n"
"Status: %d\r\n"
"Paused: %d\r\n"
+ "PausedReason: %s\r\n"
"%s"
"\r\n",
q->name, mem->membername, mem->interface, mem->state_interface, mem->dynamic ? "dynamic" : "static",
- mem->penalty, mem->calls, (int)mem->lastcall, mem->status, mem->paused, idText);
+ mem->penalty, mem->calls, (int)mem->lastcall, mem->status, mem->paused, mem->reason_paused, idText);
++q_items;
}
ao2_ref(mem, -1);
@@ -9670,7 +9684,7 @@ static int manager_pause_queue_member(struct mansession *s, const struct message
interface = astman_get_header(m, "Interface");
paused_s = astman_get_header(m, "Paused");
queuename = astman_get_header(m, "Queue"); /* Optional - if not supplied, pause the given Interface in all queues */
- reason = astman_get_header(m, "Reason"); /* Optional - Only used for logging purposes */
+ reason = astman_get_header(m, "Reason"); /* Optional */
if (ast_strlen_zero(interface) || ast_strlen_zero(paused_s)) {
astman_send_error(s, m, "Need 'Interface' and 'Paused' parameters.");
diff --git a/apps/app_voicemail.c b/apps/app_voicemail.c
index 19bd896af..0755c44b0 100644
--- a/apps/app_voicemail.c
+++ b/apps/app_voicemail.c
@@ -48,7 +48,6 @@
/*** MODULEINFO
<defaultenabled>yes</defaultenabled>
- <conflict>res_mwi_external</conflict>
<use type="module">res_adsi</use>
<use type="module">res_smdi</use>
<support_level>core</support_level>
@@ -14702,10 +14701,14 @@ static int unload_module(void)
*
* Module loading including tests for configuration or dependencies.
* This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE,
- * or AST_MODULE_LOAD_SUCCESS. If a dependency or environment variable fails
- * tests return AST_MODULE_LOAD_FAILURE. If the module can not load the
- * configuration file or other non-critical problem return
- * AST_MODULE_LOAD_DECLINE. On success return AST_MODULE_LOAD_SUCCESS.
+ * or AST_MODULE_LOAD_SUCCESS.
+ *
+ * If a dependency, allocation or environment variable fails tests, return AST_MODULE_LOAD_FAILURE.
+ *
+ * If the module can't load the configuration file, can't register as a provider or
+ * has another issue not fatal to Asterisk itself, return AST_MODULE_LOAD_DECLINE.
+ *
+ * On success return AST_MODULE_LOAD_SUCCESS.
*/
static int load_module(void)
{
@@ -14714,7 +14717,7 @@ static int load_module(void)
umask(my_umask);
if (!(inprocess_container = ao2_container_alloc(573, inprocess_hash_fn, inprocess_cmp_fn))) {
- return AST_MODULE_LOAD_DECLINE;
+ return AST_MODULE_LOAD_FAILURE;
}
/* compute the location of the voicemail spool directory */
@@ -14724,8 +14727,10 @@ static int load_module(void)
ast_log(AST_LOG_WARNING, "failed to reference mwi subscription taskprocessor. MWI will not work\n");
}
- if ((res = load_config(0)))
- return res;
+ if ((res = load_config(0))) {
+ unload_module();
+ return AST_MODULE_LOAD_DECLINE;
+ }
res = ast_register_application_xml(app, vm_exec);
res |= ast_register_application_xml(app2, vm_execmain);
@@ -14746,10 +14751,26 @@ static int load_module(void)
res |= AST_TEST_REGISTER(test_voicemail_vm_info);
#endif
- res |= ast_vm_register(&vm_table);
- res |= ast_vm_greeter_register(&vm_greeter_table);
if (res) {
- return res;
+ ast_log(LOG_ERROR, "Failure registering applications, functions or tests\n");
+ unload_module();
+ return AST_MODULE_LOAD_DECLINE;
+ }
+
+ /* ast_vm_register may return DECLINE if another module registered for vm */
+ res = ast_vm_register(&vm_table);
+ if (res) {
+ ast_log(LOG_ERROR, "Failure registering as a voicemail provider\n");
+ unload_module();
+ return AST_MODULE_LOAD_DECLINE;
+ }
+
+ /* ast_vm_greeter_register may return DECLINE if another module registered as a greeter */
+ res = ast_vm_greeter_register(&vm_greeter_table);
+ if (res) {
+ ast_log(LOG_ERROR, "Failure registering as a greeter provider\n");
+ unload_module();
+ return AST_MODULE_LOAD_DECLINE;
}
ast_cli_register_multiple(cli_voicemail, ARRAY_LEN(cli_voicemail));
@@ -14762,7 +14783,7 @@ static int load_module(void)
ast_realtime_require_field("voicemail", "uniqueid", RQ_UINTEGER3, 11, "password", RQ_CHAR, 10, SENTINEL);
ast_realtime_require_field("voicemail_data", "filename", RQ_CHAR, 30, "duration", RQ_UINTEGER3, 5, SENTINEL);
- return res;
+ return AST_MODULE_LOAD_SUCCESS;
}
static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context)
diff --git a/cel/cel_radius.c b/cel/cel_radius.c
index 79caf22f5..7b418879e 100644
--- a/cel/cel_radius.c
+++ b/cel/cel_radius.c
@@ -95,7 +95,7 @@ static rc_handle *rh = NULL;
#define RADIUS_BACKEND_NAME "CEL Radius Logging"
-#define ADD_VENDOR_CODE(x,y) (rc_avpair_add(rh, send, x, &y, strlen(y), VENDOR_CODE))
+#define ADD_VENDOR_CODE(x,y) (rc_avpair_add(rh, send, x, (void *)y, strlen(y), VENDOR_CODE))
static int build_radius_record(VALUE_PAIR **send, struct ast_cel_event_record *record)
{
@@ -176,7 +176,7 @@ static int build_radius_record(VALUE_PAIR **send, struct ast_cel_event_record *r
/* Setting Acct-Session-Id & User-Name attributes for proper generation
of Acct-Unique-Session-Id on server side */
/* Channel */
- if (!rc_avpair_add(rh, send, PW_USER_NAME, &record->channel_name,
+ if (!rc_avpair_add(rh, send, PW_USER_NAME, (void *)record->channel_name,
strlen(record->channel_name), 0)) {
return -1;
}
diff --git a/include/asterisk.h b/include/asterisk.h
index c501c44ce..873ed5c34 100644
--- a/include/asterisk.h
+++ b/include/asterisk.h
@@ -222,6 +222,9 @@ char *ast_complete_source_filename(const char *partial, int n);
* SVN from modifying them in this file; under normal circumstances they would
* not be present and SVN would expand the Revision keyword into the file's
* revision number.
+ *
+ * \deprecated All new files should use ASTERISK_REGISTER_FILE instead.
+ * \version 11.22.0 deprecated
*/
#ifdef MTX_PROFILE
#define HAVE_MTX_PROFILE /* used in lock.h */
@@ -251,6 +254,23 @@ char *ast_complete_source_filename(const char *partial, int n);
#define ASTERISK_FILE_VERSION(file, x)
#endif /* LOW_MEMORY */
+/*!
+ * \since 11.22.0
+ * \brief Register/unregister a source code file with the core.
+ *
+ * This macro will place a file-scope constructor and destructor into the
+ * source of the module using it; this will cause the file to be
+ * registered with the Asterisk core (and unregistered) at the appropriate
+ * times.
+ *
+ * Example:
+ *
+ * \code
+ * ASTERISK_REGISTER_FILE()
+ * \endcode
+ */
+#define ASTERISK_REGISTER_FILE() ASTERISK_FILE_VERSION(__FILE__, NULL)
+
#if !defined(LOW_MEMORY)
/*!
* \brief support for event profiling
diff --git a/include/asterisk/_private.h b/include/asterisk/_private.h
index 55c92fb00..b59f7ca15 100644
--- a/include/asterisk/_private.h
+++ b/include/asterisk/_private.h
@@ -19,6 +19,10 @@ int load_modules(unsigned int); /*!< Provided by loader.c */
int load_pbx(void); /*!< Provided by pbx.c */
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 load_pbx_hangup_handler(void); /*!< Provided by pbx_hangup_handler.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 */
diff --git a/include/asterisk/app.h b/include/asterisk/app.h
index 6171dd4f2..86336e32b 100644
--- a/include/asterisk/app.h
+++ b/include/asterisk/app.h
@@ -580,6 +580,7 @@ int ast_vm_is_registered(void);
*
* \retval 0 on success.
* \retval -1 on error.
+ * \retval AST_MODULE_LOAD_DECLINE if there's already another provider registered.
*/
int __ast_vm_register(const struct ast_vm_functions *vm_table, struct ast_module *module);
@@ -648,6 +649,7 @@ int ast_vm_greeter_is_registered(void);
*
* \retval 0 on success.
* \retval -1 on error.
+ * \retval AST_MODULE_LOAD_DECLINE if there's already another greeter registered.
*/
int __ast_vm_greeter_register(const struct ast_vm_greeter_functions *vm_table, struct ast_module *module);
diff --git a/include/asterisk/module.h b/include/asterisk/module.h
index d201aacc8..35ee8bbd7 100644
--- a/include/asterisk/module.h
+++ b/include/asterisk/module.h
@@ -68,7 +68,7 @@ enum ast_module_unload_mode {
enum ast_module_load_result {
AST_MODULE_LOAD_SUCCESS = 0, /*!< Module loaded and configured */
AST_MODULE_LOAD_DECLINE = 1, /*!< Module is not configured */
- AST_MODULE_LOAD_SKIP = 2, /*!< Module was skipped for some reason */
+ AST_MODULE_LOAD_SKIP = 2, /*!< Module was skipped for some reason (For loader.c use only. Should never be returned by modules)*/
AST_MODULE_LOAD_PRIORITY = 3, /*!< Module is not loaded yet, but is added to prioity heap */
AST_MODULE_LOAD_FAILURE = -1, /*!< Module could not be loaded properly */
};
diff --git a/include/asterisk/stasis_cache_pattern.h b/include/asterisk/stasis_cache_pattern.h
index 27761351a..e61d3e931 100644
--- a/include/asterisk/stasis_cache_pattern.h
+++ b/include/asterisk/stasis_cache_pattern.h
@@ -121,9 +121,12 @@ struct stasis_cp_single *stasis_cp_single_create(struct stasis_cp_all *all,
const char *name);
/*!
- * \brief Create the 'one' side of the cache pattern.
+ * \brief Create a sink in the cache pattern
*
- * Create the 'one' but do not automatically forward.
+ * Create the 'one' but do not automatically forward to the all's topic.
+ * This is useful when aggregating other topic's messages created with
+ * \c stasis_cp_single_create in another caching topic without replicating
+ * those messages in the all's topics.
*
* Dispose of using stasis_cp_single_unsubscribe().
*
@@ -131,22 +134,10 @@ struct stasis_cp_single *stasis_cp_single_create(struct stasis_cp_all *all,
* \param name Base name for the topics.
* \return One side instance
*/
-struct stasis_cp_single *stasis_cp_single_create_only(struct stasis_cp_all *all,
+struct stasis_cp_single *stasis_cp_sink_create(struct stasis_cp_all *all,
const char *name);
/*!
- * \brief Set up a topic and topic cache forward.
- *
- * Forward 'from' to 'to'.
- *
- * \param from Source 'one' side instance.
- * \param to Destination 'one' side instance.
- * \retval 0 Success
- * \retval -1 Failure
- */
-int stasis_cp_single_forward(struct stasis_cp_single *from, struct stasis_cp_single *to);
-
-/*!
* \brief Stops caching and forwarding messages.
*
* \param one One side of the cache pattern.
diff --git a/main/app.c b/main/app.c
index 8c33b9146..c4143d840 100644
--- a/main/app.c
+++ b/main/app.c
@@ -494,7 +494,7 @@ int __ast_vm_register(const struct ast_vm_functions *vm_table, struct ast_module
if (table) {
ast_log(LOG_WARNING, "Voicemail provider already registered by %s.\n",
table->module_name);
- return -1;
+ return AST_MODULE_LOAD_DECLINE;
}
table = ao2_alloc_options(sizeof(*table), NULL, AO2_ALLOC_OPT_LOCK_NOLOCK);
@@ -605,7 +605,7 @@ int __ast_vm_greeter_register(const struct ast_vm_greeter_functions *vm_table, s
if (table) {
ast_log(LOG_WARNING, "Voicemail greeter provider already registered by %s.\n",
table->module_name);
- return -1;
+ return AST_MODULE_LOAD_DECLINE;
}
table = ao2_alloc_options(sizeof(*table), NULL, AO2_ALLOC_OPT_LOCK_NOLOCK);
diff --git a/main/asterisk.c b/main/asterisk.c
index 0e179a397..c576c16f6 100644
--- a/main/asterisk.c
+++ b/main/asterisk.c
@@ -4653,6 +4653,26 @@ static void asterisk_daemon(int isroot, const char *runuser, const char *rungrou
exit(1);
}
+ if (load_pbx_variables()) {
+ printf("Failed: load_pbx_variables\n%s", term_quit());
+ exit(1);
+ }
+
+ if (load_pbx_switch()) {
+ printf("Failed: load_pbx_switch\n%s", term_quit());
+ exit(1);
+ }
+
+ if (load_pbx_app()) {
+ printf("Failed: load_pbx_app\n%s", term_quit());
+ exit(1);
+ }
+
+ if (load_pbx_hangup_handler()) {
+ printf("Failed: load_pbx_hangup_handler\n%s", term_quit());
+ exit(1);
+ }
+
if (ast_local_init()) {
printf("Failed: ast_local_init\n%s", term_quit());
exit(1);
diff --git a/main/endpoints.c b/main/endpoints.c
index 8f3ae366e..80e7f87fd 100644
--- a/main/endpoints.c
+++ b/main/endpoints.c
@@ -74,6 +74,8 @@ struct ast_endpoint {
struct stasis_message_router *router;
/*! ast_str_container of channels associated with this endpoint */
struct ao2_container *channel_ids;
+ /*! Forwarding subscription from an endpoint to its tech endpoint */
+ struct stasis_forward *tech_forward;
};
static int endpoint_hash(const void *obj, int flags)
@@ -303,7 +305,7 @@ static struct ast_endpoint *endpoint_internal_create(const char *tech, const cha
if (!ast_strlen_zero(resource)) {
- endpoint->topics = stasis_cp_single_create_only(ast_endpoint_cache_all(),
+ endpoint->topics = stasis_cp_single_create(ast_endpoint_cache_all(),
endpoint->id);
if (!endpoint->topics) {
return NULL;
@@ -322,14 +324,13 @@ static struct ast_endpoint *endpoint_internal_create(const char *tech, const cha
return NULL;
}
- if (stasis_cp_single_forward(endpoint->topics, tech_endpoint->topics)) {
- return NULL;
- }
+ endpoint->tech_forward = stasis_forward_all(stasis_cp_single_topic(endpoint->topics),
+ stasis_cp_single_topic(tech_endpoint->topics));
endpoint_publish_snapshot(endpoint);
ao2_link(endpoints, endpoint);
} else {
- endpoint->topics = stasis_cp_single_create(ast_endpoint_cache_all(),
+ endpoint->topics = stasis_cp_sink_create(ast_endpoint_cache_all(),
endpoint->id);
if (!endpoint->topics) {
return NULL;
@@ -382,6 +383,7 @@ void ast_endpoint_shutdown(struct ast_endpoint *endpoint)
}
ao2_unlink(endpoints, endpoint);
+ endpoint->tech_forward = stasis_forward_cancel(endpoint->tech_forward);
clear_msg = create_endpoint_snapshot_message(endpoint);
if (clear_msg) {
diff --git a/main/format_cap.c b/main/format_cap.c
index 8d6046ab2..17ae18cd4 100644
--- a/main/format_cap.c
+++ b/main/format_cap.c
@@ -252,6 +252,7 @@ int ast_format_cap_append_by_type(struct ast_format_cap *cap, enum ast_media_typ
for (id = 1; id < ast_codec_get_max(); ++id) {
struct ast_codec *codec = ast_codec_get_by_id(id);
+ struct ast_codec *codec2 = NULL;
struct ast_format *format;
int res;
@@ -272,10 +273,14 @@ int ast_format_cap_append_by_type(struct ast_format_cap *cap, enum ast_media_typ
continue;
}
- if (!format || (codec != ast_format_get_codec(format))) {
+ if (format) {
+ codec2 = ast_format_get_codec(format);
+ }
+ if (codec != codec2) {
ao2_cleanup(format);
format = ast_format_create(codec);
}
+ ao2_cleanup(codec2);
ao2_ref(codec, -1);
if (!format) {
diff --git a/main/pbx.c b/main/pbx.c
index 7c58e613a..08ae5b6c3 100644
--- a/main/pbx.c
+++ b/main/pbx.c
@@ -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. */
@@ -731,10 +713,6 @@ static unsigned int hashtab_hash_labels(const void *obj)
return ast_hashtab_hash_string(S_OR(ac->label, ""));
}
-
-AST_RWLOCK_DEFINE_STATIC(globalslock);
-static struct varshead globals = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
-
static int autofallthrough = 1;
static int extenpatternmatchnew = 0;
static char *overrideswitch = NULL;
@@ -764,15 +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 AST_RWLIST_HEAD_STATIC(switches, ast_switch);
-
static int stateid = 1;
/*!
* \note When holding this container's lock, do _not_ do
@@ -962,86 +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 struct ast_switch *pbx_findswitch(const char *sw)
-{
- struct ast_switch *asw;
-
- AST_RWLIST_RDLOCK(&switches);
- AST_RWLIST_TRAVERSE(&switches, asw, list) {
- if (!strcasecmp(asw->name, sw))
- break;
- }
- AST_RWLIST_UNLOCK(&switches);
-
- return asw;
-}
-
static inline int include_valid(struct ast_include *i)
{
if (!i->hastime)
@@ -2783,291 +2672,6 @@ struct ast_exten *pbx_find_extension(struct ast_channel *chan,
return NULL;
}
-/*!
- * \brief extract offset:length from variable name.
- * \return 1 if there is a offset:length part, which is
- * trimmed off (values go into variables)
- */
-static int parse_variable_name(char *var, int *offset, int *length, int *isfunc)
-{
- int parens = 0;
-
- *offset = 0;
- *length = INT_MAX;
- *isfunc = 0;
- for (; *var; var++) {
- if (*var == '(') {
- (*isfunc)++;
- parens++;
- } else if (*var == ')') {
- parens--;
- } else if (*var == ':' && parens == 0) {
- *var++ = '\0';
- sscanf(var, "%30d:%30d", offset, length);
- return 1; /* offset:length valid */
- }
- }
- return 0;
-}
-
-/*!
- *\brief takes a substring. It is ok to call with value == workspace.
- * \param value
- * \param offset < 0 means start from the end of the string and set the beginning
- * to be that many characters back.
- * \param length is the length of the substring, a value less than 0 means to leave
- * that many off the end.
- * \param workspace
- * \param workspace_len
- * Always return a copy in workspace.
- */
-static char *substring(const char *value, int offset, int length, char *workspace, size_t workspace_len)
-{
- char *ret = workspace;
- int lr; /* length of the input string after the copy */
-
- ast_copy_string(workspace, value, workspace_len); /* always make a copy */
-
- lr = strlen(ret); /* compute length after copy, so we never go out of the workspace */
-
- /* Quick check if no need to do anything */
- if (offset == 0 && length >= lr) /* take the whole string */
- return ret;
-
- if (offset < 0) { /* translate negative offset into positive ones */
- offset = lr + offset;
- if (offset < 0) /* If the negative offset was greater than the length of the string, just start at the beginning */
- offset = 0;
- }
-
- /* too large offset result in empty string so we know what to return */
- if (offset >= lr)
- return ret + lr; /* the final '\0' */
-
- ret += offset; /* move to the start position */
- if (length >= 0 && length < lr - offset) /* truncate if necessary */
- ret[length] = '\0';
- else if (length < 0) {
- if (lr > offset - length) /* After we remove from the front and from the rear, is there anything left? */
- ret[lr + length - offset] = '\0';
- else
- ret[0] = '\0';
- }
-
- return ret;
-}
-
-static const char *ast_str_substring(struct ast_str *value, int offset, int length)
-{
- int lr; /* length of the input string after the copy */
-
- lr = ast_str_strlen(value); /* compute length after copy, so we never go out of the workspace */
-
- /* Quick check if no need to do anything */
- if (offset == 0 && length >= lr) /* take the whole string */
- return ast_str_buffer(value);
-
- if (offset < 0) { /* translate negative offset into positive ones */
- offset = lr + offset;
- if (offset < 0) /* If the negative offset was greater than the length of the string, just start at the beginning */
- offset = 0;
- }
-
- /* too large offset result in empty string so we know what to return */
- if (offset >= lr) {
- ast_str_reset(value);
- return ast_str_buffer(value);
- }
-
- if (offset > 0) {
- /* Go ahead and chop off the beginning */
- memmove(ast_str_buffer(value), ast_str_buffer(value) + offset, ast_str_strlen(value) - offset + 1);
- lr -= offset;
- }
-
- if (length >= 0 && length < lr) { /* truncate if necessary */
- char *tmp = ast_str_buffer(value);
- tmp[length] = '\0';
- ast_str_update(value);
- } else if (length < 0) {
- if (lr > -length) { /* After we remove from the front and from the rear, is there anything left? */
- char *tmp = ast_str_buffer(value);
- tmp[lr + length] = '\0';
- ast_str_update(value);
- } else {
- ast_str_reset(value);
- }
- } else {
- /* Nothing to do, but update the buffer length */
- ast_str_update(value);
- }
-
- return ast_str_buffer(value);
-}
-
-/*! \brief Support for Asterisk built-in variables in the dialplan
-
-\note See also
- - \ref AstVar Channel variables
- - \ref AstCauses The HANGUPCAUSE variable
- */
-void pbx_retrieve_variable(struct ast_channel *c, const char *var, char **ret, char *workspace, int workspacelen, struct varshead *headp)
-{
- struct ast_str *str = ast_str_create(16);
- const char *cret;
-
- cret = ast_str_retrieve_variable(&str, 0, c, headp, var);
- ast_copy_string(workspace, ast_str_buffer(str), workspacelen);
- *ret = cret ? workspace : NULL;
- ast_free(str);
-}
-
-const char *ast_str_retrieve_variable(struct ast_str **str, ssize_t maxlen, struct ast_channel *c, struct varshead *headp, const char *var)
-{
- const char not_found = '\0';
- char *tmpvar;
- const char *ret;
- const char *s; /* the result */
- int offset, length;
- int i, need_substring;
- struct varshead *places[2] = { headp, &globals }; /* list of places where we may look */
- char workspace[20];
-
- if (c) {
- ast_channel_lock(c);
- places[0] = ast_channel_varshead(c);
- }
- /*
- * Make a copy of var because parse_variable_name() modifies the string.
- * Then if called directly, we might need to run substring() on the result;
- * remember this for later in 'need_substring', 'offset' and 'length'
- */
- tmpvar = ast_strdupa(var); /* parse_variable_name modifies the string */
- need_substring = parse_variable_name(tmpvar, &offset, &length, &i /* ignored */);
-
- /*
- * Look first into predefined variables, then into variable lists.
- * Variable 's' points to the result, according to the following rules:
- * s == &not_found (set at the beginning) means that we did not find a
- * matching variable and need to look into more places.
- * If s != &not_found, s is a valid result string as follows:
- * s = NULL if the variable does not have a value;
- * you typically do this when looking for an unset predefined variable.
- * s = workspace if the result has been assembled there;
- * typically done when the result is built e.g. with an snprintf(),
- * so we don't need to do an additional copy.
- * s != workspace in case we have a string, that needs to be copied
- * (the ast_copy_string is done once for all at the end).
- * Typically done when the result is already available in some string.
- */
- s = &not_found; /* default value */
- if (c) { /* This group requires a valid channel */
- /* Names with common parts are looked up a piece at a time using strncmp. */
- if (!strncmp(var, "CALL", 4)) {
- if (!strncmp(var + 4, "ING", 3)) {
- if (!strcmp(var + 7, "PRES")) { /* CALLINGPRES */
- ast_str_set(str, maxlen, "%d",
- ast_party_id_presentation(&ast_channel_caller(c)->id));
- s = ast_str_buffer(*str);
- } else if (!strcmp(var + 7, "ANI2")) { /* CALLINGANI2 */
- ast_str_set(str, maxlen, "%d", ast_channel_caller(c)->ani2);
- s = ast_str_buffer(*str);
- } else if (!strcmp(var + 7, "TON")) { /* CALLINGTON */
- ast_str_set(str, maxlen, "%d", ast_channel_caller(c)->id.number.plan);
- s = ast_str_buffer(*str);
- } else if (!strcmp(var + 7, "TNS")) { /* CALLINGTNS */
- ast_str_set(str, maxlen, "%d", ast_channel_dialed(c)->transit_network_select);
- s = ast_str_buffer(*str);
- }
- }
- } else if (!strcmp(var, "HINT")) {
- s = ast_str_get_hint(str, maxlen, NULL, 0, c, ast_channel_context(c), ast_channel_exten(c)) ? ast_str_buffer(*str) : NULL;
- } else if (!strcmp(var, "HINTNAME")) {
- s = ast_str_get_hint(NULL, 0, str, maxlen, c, ast_channel_context(c), ast_channel_exten(c)) ? ast_str_buffer(*str) : NULL;
- } else if (!strcmp(var, "EXTEN")) {
- s = ast_channel_exten(c);
- } else if (!strcmp(var, "CONTEXT")) {
- s = ast_channel_context(c);
- } else if (!strcmp(var, "PRIORITY")) {
- ast_str_set(str, maxlen, "%d", ast_channel_priority(c));
- s = ast_str_buffer(*str);
- } else if (!strcmp(var, "CHANNEL")) {
- s = ast_channel_name(c);
- } else if (!strcmp(var, "UNIQUEID")) {
- s = ast_channel_uniqueid(c);
- } else if (!strcmp(var, "HANGUPCAUSE")) {
- ast_str_set(str, maxlen, "%d", ast_channel_hangupcause(c));
- s = ast_str_buffer(*str);
- }
- }
- if (s == &not_found) { /* look for more */
- if (!strcmp(var, "EPOCH")) {
- ast_str_set(str, maxlen, "%d", (int) time(NULL));
- s = ast_str_buffer(*str);
- } else if (!strcmp(var, "SYSTEMNAME")) {
- s = ast_config_AST_SYSTEM_NAME;
- } else if (!strcmp(var, "ASTETCDIR")) {
- s = ast_config_AST_CONFIG_DIR;
- } else if (!strcmp(var, "ASTMODDIR")) {
- s = ast_config_AST_MODULE_DIR;
- } else if (!strcmp(var, "ASTVARLIBDIR")) {
- s = ast_config_AST_VAR_DIR;
- } else if (!strcmp(var, "ASTDBDIR")) {
- s = ast_config_AST_DB;
- } else if (!strcmp(var, "ASTKEYDIR")) {
- s = ast_config_AST_KEY_DIR;
- } else if (!strcmp(var, "ASTDATADIR")) {
- s = ast_config_AST_DATA_DIR;
- } else if (!strcmp(var, "ASTAGIDIR")) {
- s = ast_config_AST_AGI_DIR;
- } else if (!strcmp(var, "ASTSPOOLDIR")) {
- s = ast_config_AST_SPOOL_DIR;
- } else if (!strcmp(var, "ASTRUNDIR")) {
- s = ast_config_AST_RUN_DIR;
- } else if (!strcmp(var, "ASTLOGDIR")) {
- s = ast_config_AST_LOG_DIR;
- } else if (!strcmp(var, "ENTITYID")) {
- ast_eid_to_str(workspace, sizeof(workspace), &ast_eid_default);
- s = workspace;
- }
- }
- /* if not found, look into chanvars or global vars */
- for (i = 0; s == &not_found && i < ARRAY_LEN(places); i++) {
- struct ast_var_t *variables;
- if (!places[i])
- continue;
- if (places[i] == &globals)
- ast_rwlock_rdlock(&globalslock);
- AST_LIST_TRAVERSE(places[i], variables, entries) {
- if (!strcmp(ast_var_name(variables), var)) {
- s = ast_var_value(variables);
- break;
- }
- }
- if (places[i] == &globals)
- ast_rwlock_unlock(&globalslock);
- }
- if (s == &not_found || s == NULL) {
- ast_debug(5, "Result of '%s' is NULL\n", var);
- ret = NULL;
- } else {
- ast_debug(5, "Result of '%s' is '%s'\n", var, s);
- if (s != ast_str_buffer(*str)) {
- ast_str_set(str, maxlen, "%s", s);
- }
- ret = ast_str_buffer(*str);
- if (need_substring) {
- ret = ast_str_substring(*str, offset, length);
- ast_debug(2, "Final result of '%s' is '%s'\n", var, ret);
- }
- }
-
- if (c) {
- ast_channel_unlock(c);
- }
- return ret;
-}
-
static void exception_store_free(void *data)
{
struct pbx_exception *exception = data;
@@ -3142,407 +2746,6 @@ static struct ast_custom_function exception_function = {
.read = acf_exception_read,
};
-void ast_str_substitute_variables_full(struct ast_str **buf, ssize_t maxlen, struct ast_channel *c, struct varshead *headp, const char *templ, size_t *used)
-{
- /* Substitutes variables into buf, based on string templ */
- char *cp4 = NULL;
- const char *whereweare;
- int orig_size = 0;
- int offset, offset2, isfunction;
- const char *nextvar, *nextexp, *nextthing;
- const char *vars, *vare;
- char *finalvars;
- int pos, brackets, needsub, len;
- struct ast_str *substr1 = ast_str_create(16), *substr2 = NULL, *substr3 = ast_str_create(16);
-
- ast_str_reset(*buf);
- whereweare = templ;
- while (!ast_strlen_zero(whereweare)) {
- /* reset our buffer */
- ast_str_reset(substr3);
-
- /* Assume we're copying the whole remaining string */
- pos = strlen(whereweare);
- nextvar = NULL;
- nextexp = NULL;
- nextthing = strchr(whereweare, '$');
- if (nextthing) {
- switch (nextthing[1]) {
- case '{':
- nextvar = nextthing;
- pos = nextvar - whereweare;
- break;
- case '[':
- nextexp = nextthing;
- pos = nextexp - whereweare;
- break;
- default:
- pos = 1;
- }
- }
-
- if (pos) {
- /* Copy that many bytes */
- ast_str_append_substr(buf, maxlen, whereweare, pos);
-
- templ += pos;
- whereweare += pos;
- }
-
- if (nextvar) {
- /* We have a variable. Find the start and end, and determine
- if we are going to have to recursively call ourselves on the
- contents */
- vars = vare = nextvar + 2;
- brackets = 1;
- needsub = 0;
-
- /* Find the end of it */
- while (brackets && *vare) {
- if ((vare[0] == '$') && (vare[1] == '{')) {
- needsub++;
- } else if (vare[0] == '{') {
- brackets++;
- } else if (vare[0] == '}') {
- brackets--;
- } else if ((vare[0] == '$') && (vare[1] == '['))
- needsub++;
- vare++;
- }
- if (brackets)
- ast_log(LOG_WARNING, "Error in extension logic (missing '}')\n");
- len = vare - vars - 1;
-
- /* Skip totally over variable string */
- whereweare += (len + 3);
-
- /* Store variable name (and truncate) */
- ast_str_set_substr(&substr1, 0, vars, len);
- ast_debug(5, "Evaluating '%s' (from '%s' len %d)\n", ast_str_buffer(substr1), vars, len);
-
- /* Substitute if necessary */
- if (needsub) {
- size_t my_used;
-
- if (!substr2) {
- substr2 = ast_str_create(16);
- }
- ast_str_substitute_variables_full(&substr2, 0, c, headp, ast_str_buffer(substr1), &my_used);
- finalvars = ast_str_buffer(substr2);
- } else {
- finalvars = ast_str_buffer(substr1);
- }
-
- parse_variable_name(finalvars, &offset, &offset2, &isfunction);
- if (isfunction) {
- /* Evaluate function */
- if (c || !headp) {
- cp4 = ast_func_read2(c, finalvars, &substr3, 0) ? NULL : ast_str_buffer(substr3);
- } else {
- struct varshead old;
- struct ast_channel *bogus = ast_dummy_channel_alloc();
- if (bogus) {
- memcpy(&old, ast_channel_varshead(bogus), sizeof(old));
- memcpy(ast_channel_varshead(bogus), headp, sizeof(*ast_channel_varshead(bogus)));
- cp4 = ast_func_read2(c, finalvars, &substr3, 0) ? NULL : ast_str_buffer(substr3);
- /* Don't deallocate the varshead that was passed in */
- memcpy(ast_channel_varshead(bogus), &old, sizeof(*ast_channel_varshead(bogus)));
- ast_channel_unref(bogus);
- } else {
- ast_log(LOG_ERROR, "Unable to allocate bogus channel for variable substitution. Function results may be blank.\n");
- }
- }
- ast_debug(2, "Function %s result is '%s'\n", finalvars, cp4 ? cp4 : "(null)");
- } else {
- /* Retrieve variable value */
- ast_str_retrieve_variable(&substr3, 0, c, headp, finalvars);
- cp4 = ast_str_buffer(substr3);
- }
- if (cp4) {
- ast_str_substring(substr3, offset, offset2);
- ast_str_append(buf, maxlen, "%s", ast_str_buffer(substr3));
- }
- } else if (nextexp) {
- /* We have an expression. Find the start and end, and determine
- if we are going to have to recursively call ourselves on the
- contents */
- vars = vare = nextexp + 2;
- brackets = 1;
- needsub = 0;
-
- /* Find the end of it */
- while (brackets && *vare) {
- if ((vare[0] == '$') && (vare[1] == '[')) {
- needsub++;
- brackets++;
- vare++;
- } else if (vare[0] == '[') {
- brackets++;
- } else if (vare[0] == ']') {
- brackets--;
- } else if ((vare[0] == '$') && (vare[1] == '{')) {
- needsub++;
- vare++;
- }
- vare++;
- }
- if (brackets)
- ast_log(LOG_WARNING, "Error in extension logic (missing ']')\n");
- len = vare - vars - 1;
-
- /* Skip totally over expression */
- whereweare += (len + 3);
-
- /* Store variable name (and truncate) */
- ast_str_set_substr(&substr1, 0, vars, len);
-
- /* Substitute if necessary */
- if (needsub) {
- size_t my_used;
-
- if (!substr2) {
- substr2 = ast_str_create(16);
- }
- ast_str_substitute_variables_full(&substr2, 0, c, headp, ast_str_buffer(substr1), &my_used);
- finalvars = ast_str_buffer(substr2);
- } else {
- finalvars = ast_str_buffer(substr1);
- }
-
- if (ast_str_expr(&substr3, 0, c, finalvars)) {
- ast_debug(2, "Expression result is '%s'\n", ast_str_buffer(substr3));
- }
- ast_str_append(buf, maxlen, "%s", ast_str_buffer(substr3));
- }
- }
- *used = ast_str_strlen(*buf) - orig_size;
- ast_free(substr1);
- ast_free(substr2);
- ast_free(substr3);
-}
-
-void ast_str_substitute_variables(struct ast_str **buf, ssize_t maxlen, struct ast_channel *chan, const char *templ)
-{
- size_t used;
- ast_str_substitute_variables_full(buf, maxlen, chan, NULL, templ, &used);
-}
-
-void ast_str_substitute_variables_varshead(struct ast_str **buf, ssize_t maxlen, struct varshead *headp, const char *templ)
-{
- size_t used;
- ast_str_substitute_variables_full(buf, maxlen, NULL, headp, templ, &used);
-}
-
-void pbx_substitute_variables_helper_full(struct ast_channel *c, struct varshead *headp, const char *cp1, char *cp2, int count, size_t *used)
-{
- /* Substitutes variables into cp2, based on string cp1, cp2 NO LONGER NEEDS TO BE ZEROED OUT!!!! */
- char *cp4 = NULL;
- const char *whereweare, *orig_cp2 = cp2;
- int length, offset, offset2, isfunction;
- char *workspace = NULL;
- char *ltmp = NULL, *var = NULL;
- char *nextvar, *nextexp, *nextthing;
- char *vars, *vare;
- int pos, brackets, needsub, len;
-
- *cp2 = 0; /* just in case nothing ends up there */
- whereweare = cp1;
- while (!ast_strlen_zero(whereweare) && count) {
- /* Assume we're copying the whole remaining string */
- pos = strlen(whereweare);
- nextvar = NULL;
- nextexp = NULL;
- nextthing = strchr(whereweare, '$');
- if (nextthing) {
- switch (nextthing[1]) {
- case '{':
- nextvar = nextthing;
- pos = nextvar - whereweare;
- break;
- case '[':
- nextexp = nextthing;
- pos = nextexp - whereweare;
- break;
- default:
- pos = 1;
- }
- }
-
- if (pos) {
- /* Can't copy more than 'count' bytes */
- if (pos > count)
- pos = count;
-
- /* Copy that many bytes */
- memcpy(cp2, whereweare, pos);
-
- count -= pos;
- cp2 += pos;
- whereweare += pos;
- *cp2 = 0;
- }
-
- if (nextvar) {
- /* We have a variable. Find the start and end, and determine
- if we are going to have to recursively call ourselves on the
- contents */
- vars = vare = nextvar + 2;
- brackets = 1;
- needsub = 0;
-
- /* Find the end of it */
- while (brackets && *vare) {
- if ((vare[0] == '$') && (vare[1] == '{')) {
- needsub++;
- } else if (vare[0] == '{') {
- brackets++;
- } else if (vare[0] == '}') {
- brackets--;
- } else if ((vare[0] == '$') && (vare[1] == '['))
- needsub++;
- vare++;
- }
- if (brackets)
- ast_log(LOG_WARNING, "Error in extension logic (missing '}')\n");
- len = vare - vars - 1;
-
- /* Skip totally over variable string */
- whereweare += (len + 3);
-
- if (!var)
- var = ast_alloca(VAR_BUF_SIZE);
-
- /* Store variable name (and truncate) */
- ast_copy_string(var, vars, len + 1);
-
- /* Substitute if necessary */
- if (needsub) {
- size_t my_used;
-
- if (!ltmp) {
- ltmp = ast_alloca(VAR_BUF_SIZE);
- }
- pbx_substitute_variables_helper_full(c, headp, var, ltmp, VAR_BUF_SIZE - 1, &my_used);
- vars = ltmp;
- } else {
- vars = var;
- }
-
- if (!workspace)
- workspace = ast_alloca(VAR_BUF_SIZE);
-
- workspace[0] = '\0';
-
- parse_variable_name(vars, &offset, &offset2, &isfunction);
- if (isfunction) {
- /* Evaluate function */
- if (c || !headp)
- cp4 = ast_func_read(c, vars, workspace, VAR_BUF_SIZE) ? NULL : workspace;
- else {
- struct varshead old;
- struct ast_channel *c = ast_dummy_channel_alloc();
- if (c) {
- memcpy(&old, ast_channel_varshead(c), sizeof(old));
- memcpy(ast_channel_varshead(c), headp, sizeof(*ast_channel_varshead(c)));
- cp4 = ast_func_read(c, vars, workspace, VAR_BUF_SIZE) ? NULL : workspace;
- /* Don't deallocate the varshead that was passed in */
- memcpy(ast_channel_varshead(c), &old, sizeof(*ast_channel_varshead(c)));
- c = ast_channel_unref(c);
- } else {
- ast_log(LOG_ERROR, "Unable to allocate bogus channel for variable substitution. Function results may be blank.\n");
- }
- }
- ast_debug(2, "Function %s result is '%s'\n", vars, cp4 ? cp4 : "(null)");
- } else {
- /* Retrieve variable value */
- pbx_retrieve_variable(c, vars, &cp4, workspace, VAR_BUF_SIZE, headp);
- }
- if (cp4) {
- cp4 = substring(cp4, offset, offset2, workspace, VAR_BUF_SIZE);
-
- length = strlen(cp4);
- if (length > count)
- length = count;
- memcpy(cp2, cp4, length);
- count -= length;
- cp2 += length;
- *cp2 = 0;
- }
- } else if (nextexp) {
- /* We have an expression. Find the start and end, and determine
- if we are going to have to recursively call ourselves on the
- contents */
- vars = vare = nextexp + 2;
- brackets = 1;
- needsub = 0;
-
- /* Find the end of it */
- while (brackets && *vare) {
- if ((vare[0] == '$') && (vare[1] == '[')) {
- needsub++;
- brackets++;
- vare++;
- } else if (vare[0] == '[') {
- brackets++;
- } else if (vare[0] == ']') {
- brackets--;
- } else if ((vare[0] == '$') && (vare[1] == '{')) {
- needsub++;
- vare++;
- }
- vare++;
- }
- if (brackets)
- ast_log(LOG_WARNING, "Error in extension logic (missing ']')\n");
- len = vare - vars - 1;
-
- /* Skip totally over expression */
- whereweare += (len + 3);
-
- if (!var)
- var = ast_alloca(VAR_BUF_SIZE);
-
- /* Store variable name (and truncate) */
- ast_copy_string(var, vars, len + 1);
-
- /* Substitute if necessary */
- if (needsub) {
- size_t my_used;
-
- if (!ltmp) {
- ltmp = ast_alloca(VAR_BUF_SIZE);
- }
- pbx_substitute_variables_helper_full(c, headp, var, ltmp, VAR_BUF_SIZE - 1, &my_used);
- vars = ltmp;
- } else {
- vars = var;
- }
-
- length = ast_expr(vars, cp2, count, c);
-
- if (length) {
- ast_debug(1, "Expression result is '%s'\n", cp2);
- count -= length;
- cp2 += length;
- *cp2 = 0;
- }
- }
- }
- *used = cp2 - orig_cp2;
-}
-
-void pbx_substitute_variables_helper(struct ast_channel *c, const char *cp1, char *cp2, int count)
-{
- size_t used;
- pbx_substitute_variables_helper_full(c, (c) ? ast_channel_varshead(c) : NULL, cp1, cp2, count, &used);
-}
-
-void pbx_substitute_variables_varshead(struct varshead *headp, const char *cp1, char *cp2, int count)
-{
- size_t used;
- pbx_substitute_variables_helper_full(NULL, headp, cp1, cp2, count, &used);
-}
-
/*!
* \brief The return value depends on the action:
*
@@ -3617,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");
@@ -4857,248 +4060,6 @@ void ast_pbx_h_exten_run(struct ast_channel *chan, const char *context)
ast_channel_unlock(chan);
}
-/*!
- * \internal
- * \brief Publish a hangup handler related message to \ref stasis
- */
-static void publish_hangup_handler_message(const char *action, struct ast_channel *chan, const char *handler)
-{
- RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
-
- blob = ast_json_pack("{s: s, s: s}",
- "type", action,
- "handler", S_OR(handler, ""));
- if (!blob) {
- return;
- }
-
- ast_channel_publish_blob(chan, ast_channel_hangup_handler_type(), blob);
-}
-
-int ast_pbx_hangup_handler_run(struct ast_channel *chan)
-{
- struct ast_hangup_handler_list *handlers;
- struct ast_hangup_handler *h_handler;
-
- ast_channel_lock(chan);
- handlers = ast_channel_hangup_handlers(chan);
- if (AST_LIST_EMPTY(handlers)) {
- ast_channel_unlock(chan);
- return 0;
- }
-
- /*
- * Make sure that the channel is marked as hungup since we are
- * going to run the hangup handlers on it.
- */
- ast_softhangup_nolock(chan, AST_SOFTHANGUP_HANGUP_EXEC);
-
- for (;;) {
- handlers = ast_channel_hangup_handlers(chan);
- h_handler = AST_LIST_REMOVE_HEAD(handlers, node);
- if (!h_handler) {
- break;
- }
-
- publish_hangup_handler_message("run", chan, h_handler->args);
- ast_channel_unlock(chan);
-
- ast_app_exec_sub(NULL, chan, h_handler->args, 1);
- ast_free(h_handler);
-
- ast_channel_lock(chan);
- }
- ast_channel_unlock(chan);
- return 1;
-}
-
-void ast_pbx_hangup_handler_init(struct ast_channel *chan)
-{
- struct ast_hangup_handler_list *handlers;
-
- handlers = ast_channel_hangup_handlers(chan);
- AST_LIST_HEAD_INIT_NOLOCK(handlers);
-}
-
-void ast_pbx_hangup_handler_destroy(struct ast_channel *chan)
-{
- struct ast_hangup_handler_list *handlers;
- struct ast_hangup_handler *h_handler;
-
- ast_channel_lock(chan);
-
- /* Get rid of each of the hangup handlers on the channel */
- handlers = ast_channel_hangup_handlers(chan);
- while ((h_handler = AST_LIST_REMOVE_HEAD(handlers, node))) {
- ast_free(h_handler);
- }
-
- ast_channel_unlock(chan);
-}
-
-int ast_pbx_hangup_handler_pop(struct ast_channel *chan)
-{
- struct ast_hangup_handler_list *handlers;
- struct ast_hangup_handler *h_handler;
-
- ast_channel_lock(chan);
- handlers = ast_channel_hangup_handlers(chan);
- h_handler = AST_LIST_REMOVE_HEAD(handlers, node);
- if (h_handler) {
- publish_hangup_handler_message("pop", chan, h_handler->args);
- }
- ast_channel_unlock(chan);
- if (h_handler) {
- ast_free(h_handler);
- return 1;
- }
- return 0;
-}
-
-void ast_pbx_hangup_handler_push(struct ast_channel *chan, const char *handler)
-{
- struct ast_hangup_handler_list *handlers;
- struct ast_hangup_handler *h_handler;
- const char *expanded_handler;
-
- if (ast_strlen_zero(handler)) {
- return;
- }
-
- expanded_handler = ast_app_expand_sub_args(chan, handler);
- if (!expanded_handler) {
- return;
- }
- h_handler = ast_malloc(sizeof(*h_handler) + 1 + strlen(expanded_handler));
- if (!h_handler) {
- ast_free((char *) expanded_handler);
- return;
- }
- strcpy(h_handler->args, expanded_handler);/* Safe */
- ast_free((char *) expanded_handler);
-
- ast_channel_lock(chan);
-
- handlers = ast_channel_hangup_handlers(chan);
- AST_LIST_INSERT_HEAD(handlers, h_handler, node);
- publish_hangup_handler_message("push", chan, h_handler->args);
- ast_channel_unlock(chan);
-}
-
-#define HANDLER_FORMAT "%-30s %s\n"
-
-/*!
- * \internal
- * \brief CLI output the hangup handler headers.
- * \since 11.0
- *
- * \param fd CLI file descriptor to use.
- *
- * \return Nothing
- */
-static void ast_pbx_hangup_handler_headers(int fd)
-{
- ast_cli(fd, HANDLER_FORMAT, "Channel", "Handler");
-}
-
-/*!
- * \internal
- * \brief CLI output the channel hangup handlers.
- * \since 11.0
- *
- * \param fd CLI file descriptor to use.
- * \param chan Channel to show hangup handlers.
- *
- * \return Nothing
- */
-static void ast_pbx_hangup_handler_show(int fd, struct ast_channel *chan)
-{
- struct ast_hangup_handler_list *handlers;
- struct ast_hangup_handler *h_handler;
- int first = 1;
-
- ast_channel_lock(chan);
- handlers = ast_channel_hangup_handlers(chan);
- AST_LIST_TRAVERSE(handlers, h_handler, node) {
- ast_cli(fd, HANDLER_FORMAT, first ? ast_channel_name(chan) : "", h_handler->args);
- first = 0;
- }
- ast_channel_unlock(chan);
-}
-
-/*
- * \brief 'show hanguphandlers <channel>' CLI command implementation function...
- */
-static char *handle_show_hangup_channel(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
-{
- struct ast_channel *chan;
-
- switch (cmd) {
- case CLI_INIT:
- e->command = "core show hanguphandlers";
- e->usage =
- "Usage: core show hanguphandlers <channel>\n"
- " Show hangup handlers of a specified channel.\n";
- return NULL;
- case CLI_GENERATE:
- return ast_complete_channels(a->line, a->word, a->pos, a->n, e->args);
- }
-
- if (a->argc < 4) {
- return CLI_SHOWUSAGE;
- }
-
- chan = ast_channel_get_by_name(a->argv[3]);
- if (!chan) {
- ast_cli(a->fd, "Channel does not exist.\n");
- return CLI_FAILURE;
- }
-
- ast_pbx_hangup_handler_headers(a->fd);
- ast_pbx_hangup_handler_show(a->fd, chan);
-
- ast_channel_unref(chan);
-
- return CLI_SUCCESS;
-}
-
-/*
- * \brief 'show hanguphandlers all' CLI command implementation function...
- */
-static char *handle_show_hangup_all(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
-{
- struct ast_channel_iterator *iter;
- struct ast_channel *chan;
-
- switch (cmd) {
- case CLI_INIT:
- e->command = "core show hanguphandlers all";
- e->usage =
- "Usage: core show hanguphandlers all\n"
- " Show hangup handlers for all channels.\n";
- return NULL;
- case CLI_GENERATE:
- return ast_complete_channels(a->line, a->word, a->pos, a->n, e->args);
- }
-
- if (a->argc < 4) {
- return CLI_SHOWUSAGE;
- }
-
- iter = ast_channel_iterator_all_new();
- if (!iter) {
- return CLI_FAILURE;
- }
-
- ast_pbx_hangup_handler_headers(a->fd);
- for (; (chan = ast_channel_iterator_next(iter)); ast_channel_unref(chan)) {
- ast_pbx_hangup_handler_show(a->fd, chan);
- }
- ast_channel_iterator_destroy(iter);
-
- return CLI_SUCCESS;
-}
-
/*! helper function to set extension and priority */
void set_ext_pri(struct ast_channel *c, const char *exten, int pri)
{
@@ -6007,247 +4968,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;
-}
-
-/*
- * Append to the list. We don't have a tail pointer because we need
- * to scan the list anyways to check for duplicates during insertion.
- */
-int ast_register_switch(struct ast_switch *sw)
-{
- struct ast_switch *tmp;
-
- AST_RWLIST_WRLOCK(&switches);
- AST_RWLIST_TRAVERSE(&switches, tmp, list) {
- if (!strcasecmp(tmp->name, sw->name)) {
- AST_RWLIST_UNLOCK(&switches);
- ast_log(LOG_WARNING, "Switch '%s' already found\n", sw->name);
- return -1;
- }
- }
- AST_RWLIST_INSERT_TAIL(&switches, sw, list);
- AST_RWLIST_UNLOCK(&switches);
-
- return 0;
-}
-
-void ast_unregister_switch(struct ast_switch *sw)
-{
- AST_RWLIST_WRLOCK(&switches);
- AST_RWLIST_REMOVE(&switches, sw, list);
- AST_RWLIST_UNLOCK(&switches);
-}
-
/*
* 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)
{
@@ -6413,40 +5137,6 @@ static char *handle_show_hint(struct ast_cli_entry *e, int cmd, struct ast_cli_a
return CLI_SUCCESS;
}
-
-/*! \brief handle_show_switches: CLI support for listing registered dial plan switches */
-static char *handle_show_switches(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
-{
- struct ast_switch *sw;
-
- switch (cmd) {
- case CLI_INIT:
- e->command = "core show switches";
- e->usage =
- "Usage: core show switches\n"
- " List registered switches\n";
- return NULL;
- case CLI_GENERATE:
- return NULL;
- }
-
- AST_RWLIST_RDLOCK(&switches);
-
- if (AST_RWLIST_EMPTY(&switches)) {
- AST_RWLIST_UNLOCK(&switches);
- ast_cli(a->fd, "There are no registered alternative switches\n");
- return CLI_SUCCESS;
- }
-
- ast_cli(a->fd, "\n -= Registered Asterisk Alternative Switches =-\n");
- AST_RWLIST_TRAVERSE(&switches, sw, list)
- ast_cli(a->fd, "%s: %s\n", sw->name, sw->description);
-
- AST_RWLIST_UNLOCK(&switches);
-
- return CLI_SUCCESS;
-}
-
#if 0
/* This code can be used to test if the system survives running out of memory.
* It might be an idea to put this in only if ENABLE_AUTODESTRUCT_TESTS is enabled.
@@ -6522,89 +5212,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 ...
*/
@@ -7195,34 +5802,6 @@ static int manager_show_dialplan(struct mansession *s, const struct message *m)
return 0;
}
-/*! \brief CLI support for listing global variables in a parseable way */
-static char *handle_show_globals(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
-{
- int i = 0;
- struct ast_var_t *newvariable;
-
- switch (cmd) {
- case CLI_INIT:
- e->command = "dialplan show globals";
- e->usage =
- "Usage: dialplan show globals\n"
- " List current global dialplan variables and their values\n";
- return NULL;
- case CLI_GENERATE:
- return NULL;
- }
-
- ast_rwlock_rdlock(&globalslock);
- AST_LIST_TRAVERSE (&globals, newvariable, entries) {
- i++;
- ast_cli(a->fd, " %s=%s\n", ast_var_name(newvariable), ast_var_value(newvariable));
- }
- ast_rwlock_unlock(&globalslock);
- ast_cli(a->fd, "\n -- %d variable(s)\n", i);
-
- return CLI_SUCCESS;
-}
-
#ifdef AST_DEVMODE
static char *handle_show_device2extenstate(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
@@ -7253,102 +5832,6 @@ static char *handle_show_device2extenstate(struct ast_cli_entry *e, int cmd, str
}
#endif
-/*! \brief CLI support for listing chanvar's variables in a parseable way */
-static char *handle_show_chanvar(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
-{
- struct ast_channel *chan;
- struct ast_var_t *var;
-
- switch (cmd) {
- case CLI_INIT:
- e->command = "dialplan show chanvar";
- e->usage =
- "Usage: dialplan show chanvar <channel>\n"
- " List current channel variables and their values\n";
- return NULL;
- case CLI_GENERATE:
- return ast_complete_channels(a->line, a->word, a->pos, a->n, 3);
- }
-
- if (a->argc != e->args + 1) {
- return CLI_SHOWUSAGE;
- }
-
- chan = ast_channel_get_by_name(a->argv[e->args]);
- if (!chan) {
- ast_cli(a->fd, "Channel '%s' not found\n", a->argv[e->args]);
- return CLI_FAILURE;
- }
-
- ast_channel_lock(chan);
- AST_LIST_TRAVERSE(ast_channel_varshead(chan), var, entries) {
- ast_cli(a->fd, "%s=%s\n", ast_var_name(var), ast_var_value(var));
- }
- ast_channel_unlock(chan);
-
- ast_channel_unref(chan);
- return CLI_SUCCESS;
-}
-
-static char *handle_set_global(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
-{
- switch (cmd) {
- case CLI_INIT:
- e->command = "dialplan set global";
- e->usage =
- "Usage: dialplan set global <name> <value>\n"
- " Set global dialplan variable <name> to <value>\n";
- return NULL;
- case CLI_GENERATE:
- return NULL;
- }
-
- if (a->argc != e->args + 2)
- return CLI_SHOWUSAGE;
-
- pbx_builtin_setvar_helper(NULL, a->argv[3], a->argv[4]);
- ast_cli(a->fd, "\n -- Global variable '%s' set to '%s'\n", a->argv[3], a->argv[4]);
-
- return CLI_SUCCESS;
-}
-
-static char *handle_set_chanvar(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
-{
- struct ast_channel *chan;
- const char *chan_name, *var_name, *var_value;
-
- switch (cmd) {
- case CLI_INIT:
- e->command = "dialplan set chanvar";
- e->usage =
- "Usage: dialplan set chanvar <channel> <varname> <value>\n"
- " Set channel variable <varname> to <value>\n";
- return NULL;
- case CLI_GENERATE:
- return ast_complete_channels(a->line, a->word, a->pos, a->n, 3);
- }
-
- if (a->argc != e->args + 3)
- return CLI_SHOWUSAGE;
-
- chan_name = a->argv[e->args];
- var_name = a->argv[e->args + 1];
- var_value = a->argv[e->args + 2];
-
- if (!(chan = ast_channel_get_by_name(chan_name))) {
- ast_cli(a->fd, "Channel '%s' not found\n", chan_name);
- return CLI_FAILURE;
- }
-
- pbx_builtin_setvar_helper(chan, var_name, var_value);
-
- chan = ast_channel_unref(chan);
-
- ast_cli(a->fd, "\n -- Channel variable '%s' set to '%s' for '%s'\n", var_name, var_value, chan_name);
-
- return CLI_SUCCESS;
-}
-
static char *handle_set_extenpatternmatchnew(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
int oldval = 0;
@@ -7412,27 +5895,18 @@ 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_switches, "Show alternative switches"),
AST_CLI_DEFINE(handle_show_hints, "Show dialplan hints"),
AST_CLI_DEFINE(handle_show_hint, "Show dialplan hint"),
- AST_CLI_DEFINE(handle_show_globals, "Show global dialplan variables"),
#ifdef AST_DEVMODE
AST_CLI_DEFINE(handle_show_device2extenstate, "Show expected exten state from multiple device states"),
#endif
- AST_CLI_DEFINE(handle_show_chanvar, "Show channel variables"),
- 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_set_global, "Set global dialplan variable"),
- AST_CLI_DEFINE(handle_set_chanvar, "Set a channel variable"),
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;
@@ -7451,36 +5925,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;
@@ -7935,260 +6379,6 @@ int ast_context_add_include(const char *context, const char *include, const char
return ret;
}
-/*! \brief Helper for get_range.
- * return the index of the matching entry, starting from 1.
- * If names is not supplied, try numeric values.
- */
-static int lookup_name(const char *s, const char * const names[], int max)
-{
- int i;
-
- if (names && *s > '9') {
- for (i = 0; names[i]; i++) {
- if (!strcasecmp(s, names[i])) {
- return i;
- }
- }
- }
-
- /* Allow months and weekdays to be specified as numbers, as well */
- if (sscanf(s, "%2d", &i) == 1 && i >= 1 && i <= max) {
- /* What the array offset would have been: "1" would be at offset 0 */
- return i - 1;
- }
- return -1; /* error return */
-}
-
-/*! \brief helper function to return a range up to max (7, 12, 31 respectively).
- * names, if supplied, is an array of names that should be mapped to numbers.
- */
-static unsigned get_range(char *src, int max, const char * const names[], const char *msg)
-{
- int start, end; /* start and ending position */
- unsigned int mask = 0;
- char *part;
-
- /* Check for whole range */
- if (ast_strlen_zero(src) || !strcmp(src, "*")) {
- return (1 << max) - 1;
- }
-
- while ((part = strsep(&src, "&"))) {
- /* Get start and ending position */
- char *endpart = strchr(part, '-');
- if (endpart) {
- *endpart++ = '\0';
- }
- /* Find the start */
- if ((start = lookup_name(part, names, max)) < 0) {
- ast_log(LOG_WARNING, "Invalid %s '%s', skipping element\n", msg, part);
- continue;
- }
- if (endpart) { /* find end of range */
- if ((end = lookup_name(endpart, names, max)) < 0) {
- ast_log(LOG_WARNING, "Invalid end %s '%s', skipping element\n", msg, endpart);
- continue;
- }
- } else {
- end = start;
- }
- /* Fill the mask. Remember that ranges are cyclic */
- mask |= (1 << end); /* initialize with last element */
- while (start != end) {
- mask |= (1 << start);
- if (++start >= max) {
- start = 0;
- }
- }
- }
- return mask;
-}
-
-/*! \brief store a bitmask of valid times, one bit each 1 minute */
-static void get_timerange(struct ast_timing *i, char *times)
-{
- char *endpart, *part;
- int x;
- int st_h, st_m;
- int endh, endm;
- int minute_start, minute_end;
-
- /* start disabling all times, fill the fields with 0's, as they may contain garbage */
- memset(i->minmask, 0, sizeof(i->minmask));
-
- /* 1-minute per bit */
- /* Star is all times */
- if (ast_strlen_zero(times) || !strcmp(times, "*")) {
- /* 48, because each hour takes 2 integers; 30 bits each */
- for (x = 0; x < 48; x++) {
- i->minmask[x] = 0x3fffffff; /* 30 bits */
- }
- return;
- }
- /* Otherwise expect a range */
- while ((part = strsep(&times, "&"))) {
- if (!(endpart = strchr(part, '-'))) {
- if (sscanf(part, "%2d:%2d", &st_h, &st_m) != 2 || st_h < 0 || st_h > 23 || st_m < 0 || st_m > 59) {
- ast_log(LOG_WARNING, "%s isn't a valid time.\n", part);
- continue;
- }
- i->minmask[st_h * 2 + (st_m >= 30 ? 1 : 0)] |= (1 << (st_m % 30));
- continue;
- }
- *endpart++ = '\0';
- /* why skip non digits? Mostly to skip spaces */
- while (*endpart && !isdigit(*endpart)) {
- endpart++;
- }
- if (!*endpart) {
- ast_log(LOG_WARNING, "Invalid time range starting with '%s-'.\n", part);
- continue;
- }
- if (sscanf(part, "%2d:%2d", &st_h, &st_m) != 2 || st_h < 0 || st_h > 23 || st_m < 0 || st_m > 59) {
- ast_log(LOG_WARNING, "'%s' isn't a valid start time.\n", part);
- continue;
- }
- if (sscanf(endpart, "%2d:%2d", &endh, &endm) != 2 || endh < 0 || endh > 23 || endm < 0 || endm > 59) {
- ast_log(LOG_WARNING, "'%s' isn't a valid end time.\n", endpart);
- continue;
- }
- minute_start = st_h * 60 + st_m;
- minute_end = endh * 60 + endm;
- /* Go through the time and enable each appropriate bit */
- for (x = minute_start; x != minute_end; x = (x + 1) % (24 * 60)) {
- i->minmask[x / 30] |= (1 << (x % 30));
- }
- /* Do the last one */
- i->minmask[x / 30] |= (1 << (x % 30));
- }
- /* All done */
- return;
-}
-
-static const char * const days[] =
-{
- "sun",
- "mon",
- "tue",
- "wed",
- "thu",
- "fri",
- "sat",
- NULL,
-};
-
-static const char * const months[] =
-{
- "jan",
- "feb",
- "mar",
- "apr",
- "may",
- "jun",
- "jul",
- "aug",
- "sep",
- "oct",
- "nov",
- "dec",
- NULL,
-};
-/*! /brief Build timing
- *
- * /param i info
- * /param info_in
- *
- */
-int ast_build_timing(struct ast_timing *i, const char *info_in)
-{
- char *info;
- int j, num_fields, last_sep = -1;
-
- i->timezone = NULL;
-
- /* Check for empty just in case */
- if (ast_strlen_zero(info_in)) {
- return 0;
- }
-
- /* make a copy just in case we were passed a static string */
- info = ast_strdupa(info_in);
-
- /* count the number of fields in the timespec */
- for (j = 0, num_fields = 1; info[j] != '\0'; j++) {
- if (info[j] == ',') {
- last_sep = j;
- num_fields++;
- }
- }
-
- /* save the timezone, if it is specified */
- if (num_fields == 5) {
- i->timezone = ast_strdup(info + last_sep + 1);
- }
-
- /* Assume everything except time */
- i->monthmask = 0xfff; /* 12 bits */
- i->daymask = 0x7fffffffU; /* 31 bits */
- i->dowmask = 0x7f; /* 7 bits */
- /* on each call, use strsep() to move info to the next argument */
- get_timerange(i, strsep(&info, "|,"));
- if (info)
- i->dowmask = get_range(strsep(&info, "|,"), 7, days, "day of week");
- if (info)
- i->daymask = get_range(strsep(&info, "|,"), 31, NULL, "day");
- if (info)
- i->monthmask = get_range(strsep(&info, "|,"), 12, months, "month");
- return 1;
-}
-
-int ast_check_timing(const struct ast_timing *i)
-{
- return ast_check_timing2(i, ast_tvnow());
-}
-
-int ast_check_timing2(const struct ast_timing *i, const struct timeval tv)
-{
- struct ast_tm tm;
-
- ast_localtime(&tv, &tm, i->timezone);
-
- /* If it's not the right month, return */
- if (!(i->monthmask & (1 << tm.tm_mon)))
- return 0;
-
- /* If it's not that time of the month.... */
- /* Warning, tm_mday has range 1..31! */
- if (!(i->daymask & (1 << (tm.tm_mday-1))))
- return 0;
-
- /* If it's not the right day of the week */
- if (!(i->dowmask & (1 << tm.tm_wday)))
- return 0;
-
- /* Sanity check the hour just to be safe */
- if ((tm.tm_hour < 0) || (tm.tm_hour > 23)) {
- ast_log(LOG_WARNING, "Insane time...\n");
- return 0;
- }
-
- /* Now the tough part, we calculate if it fits
- in the right time based on min/hour */
- if (!(i->minmask[tm.tm_hour * 2 + (tm.tm_min >= 30 ? 1 : 0)] & (1 << (tm.tm_min >= 30 ? tm.tm_min - 30 : tm.tm_min))))
- return 0;
-
- /* If we got this far, then we're good */
- return 1;
-}
-
-int ast_destroy_timing(struct ast_timing *i)
-{
- if (i->timezone) {
- ast_free(i->timezone);
- i->timezone = NULL;
- }
- return 0;
-}
/*
* errno values
* ENOMEM - out of memory
@@ -9739,238 +7929,6 @@ static struct ast_custom_function testtime_function = {
.write = testtime_write,
};
-int pbx_builtin_serialize_variables(struct ast_channel *chan, struct ast_str **buf)
-{
- struct ast_var_t *variables;
- const char *var, *val;
- int total = 0;
-
- if (!chan)
- return 0;
-
- ast_str_reset(*buf);
-
- ast_channel_lock(chan);
-
- AST_LIST_TRAVERSE(ast_channel_varshead(chan), variables, entries) {
- if ((var = ast_var_name(variables)) && (val = ast_var_value(variables))
- /* && !ast_strlen_zero(var) && !ast_strlen_zero(val) */
- ) {
- if (ast_str_append(buf, 0, "%s=%s\n", var, val) < 0) {
- ast_log(LOG_ERROR, "Data Buffer Size Exceeded!\n");
- break;
- } else
- total++;
- } else
- break;
- }
-
- ast_channel_unlock(chan);
-
- return total;
-}
-
-const char *pbx_builtin_getvar_helper(struct ast_channel *chan, const char *name)
-{
- struct ast_var_t *variables;
- const char *ret = NULL;
- int i;
- struct varshead *places[2] = { NULL, &globals };
-
- if (!name)
- return NULL;
-
- if (chan) {
- ast_channel_lock(chan);
- places[0] = ast_channel_varshead(chan);
- }
-
- for (i = 0; i < 2; i++) {
- if (!places[i])
- continue;
- if (places[i] == &globals)
- ast_rwlock_rdlock(&globalslock);
- AST_LIST_TRAVERSE(places[i], variables, entries) {
- if (!strcmp(name, ast_var_name(variables))) {
- ret = ast_var_value(variables);
- break;
- }
- }
- if (places[i] == &globals)
- ast_rwlock_unlock(&globalslock);
- if (ret)
- break;
- }
-
- if (chan)
- ast_channel_unlock(chan);
-
- return ret;
-}
-
-void pbx_builtin_pushvar_helper(struct ast_channel *chan, const char *name, const char *value)
-{
- struct ast_var_t *newvariable;
- struct varshead *headp;
-
- if (name[strlen(name)-1] == ')') {
- char *function = ast_strdupa(name);
-
- ast_log(LOG_WARNING, "Cannot push a value onto a function\n");
- ast_func_write(chan, function, value);
- return;
- }
-
- if (chan) {
- ast_channel_lock(chan);
- headp = ast_channel_varshead(chan);
- } else {
- ast_rwlock_wrlock(&globalslock);
- headp = &globals;
- }
-
- if (value && (newvariable = ast_var_assign(name, value))) {
- if (headp == &globals)
- ast_verb(2, "Setting global variable '%s' to '%s'\n", name, value);
- AST_LIST_INSERT_HEAD(headp, newvariable, entries);
- }
-
- if (chan)
- ast_channel_unlock(chan);
- else
- ast_rwlock_unlock(&globalslock);
-}
-
-int pbx_builtin_setvar_helper(struct ast_channel *chan, const char *name, const char *value)
-{
- struct ast_var_t *newvariable;
- struct varshead *headp;
- const char *nametail = name;
- /*! True if the old value was not an empty string. */
- int old_value_existed = 0;
-
- if (name[strlen(name) - 1] == ')') {
- char *function = ast_strdupa(name);
-
- return ast_func_write(chan, function, value);
- }
-
- if (chan) {
- ast_channel_lock(chan);
- headp = ast_channel_varshead(chan);
- } else {
- ast_rwlock_wrlock(&globalslock);
- headp = &globals;
- }
-
- /* For comparison purposes, we have to strip leading underscores */
- if (*nametail == '_') {
- nametail++;
- if (*nametail == '_')
- nametail++;
- }
-
- AST_LIST_TRAVERSE_SAFE_BEGIN(headp, newvariable, entries) {
- if (strcmp(ast_var_name(newvariable), nametail) == 0) {
- /* there is already such a variable, delete it */
- AST_LIST_REMOVE_CURRENT(entries);
- old_value_existed = !ast_strlen_zero(ast_var_value(newvariable));
- ast_var_delete(newvariable);
- break;
- }
- }
- AST_LIST_TRAVERSE_SAFE_END;
-
- if (value && (newvariable = ast_var_assign(name, value))) {
- if (headp == &globals) {
- ast_verb(2, "Setting global variable '%s' to '%s'\n", name, value);
- }
- AST_LIST_INSERT_HEAD(headp, newvariable, entries);
- ast_channel_publish_varset(chan, name, value);
- } else if (old_value_existed) {
- /* We just deleted a non-empty dialplan variable. */
- ast_channel_publish_varset(chan, name, "");
- }
-
- if (chan)
- ast_channel_unlock(chan);
- else
- ast_rwlock_unlock(&globalslock);
- return 0;
-}
-
-int pbx_builtin_setvar(struct ast_channel *chan, const char *data)
-{
- char *name, *value, *mydata;
-
- if (ast_strlen_zero(data)) {
- ast_log(LOG_WARNING, "Set requires one variable name/value pair.\n");
- return 0;
- }
-
- mydata = ast_strdupa(data);
- name = strsep(&mydata, "=");
- value = mydata;
- if (!value) {
- ast_log(LOG_WARNING, "Set requires an '=' to be a valid assignment.\n");
- return 0;
- }
-
- if (strchr(name, ' ')) {
- ast_log(LOG_WARNING, "Please avoid unnecessary spaces on variables as it may lead to unexpected results ('%s' set to '%s').\n", name, mydata);
- }
-
- pbx_builtin_setvar_helper(chan, name, value);
-
- return 0;
-}
-
-int pbx_builtin_setvar_multiple(struct ast_channel *chan, const char *vdata)
-{
- char *data;
- int x;
- AST_DECLARE_APP_ARGS(args,
- AST_APP_ARG(pair)[24];
- );
- AST_DECLARE_APP_ARGS(pair,
- AST_APP_ARG(name);
- AST_APP_ARG(value);
- );
-
- if (ast_strlen_zero(vdata)) {
- ast_log(LOG_WARNING, "MSet requires at least one variable name/value pair.\n");
- return 0;
- }
-
- data = ast_strdupa(vdata);
- AST_STANDARD_APP_ARGS(args, data);
-
- for (x = 0; x < args.argc; x++) {
- AST_NONSTANDARD_APP_ARGS(pair, args.pair[x], '=');
- if (pair.argc == 2) {
- pbx_builtin_setvar_helper(chan, pair.name, pair.value);
- if (strchr(pair.name, ' '))
- ast_log(LOG_WARNING, "Please avoid unnecessary spaces on variables as it may lead to unexpected results ('%s' set to '%s').\n", pair.name, pair.value);
- } else if (!chan) {
- ast_log(LOG_WARNING, "MSet: ignoring entry '%s' with no '='\n", pair.name);
- } else {
- ast_log(LOG_WARNING, "MSet: ignoring entry '%s' with no '=' (in %s@%s:%d\n", pair.name, ast_channel_exten(chan), ast_channel_context(chan), ast_channel_priority(chan));
- }
- }
-
- return 0;
-}
-
-void pbx_builtin_clear_globals(void)
-{
- struct ast_var_t *vardata;
-
- ast_rwlock_wrlock(&globalslock);
- while ((vardata = AST_LIST_REMOVE_HEAD(&globals, entries)))
- ast_var_delete(vardata);
- ast_rwlock_unlock(&globalslock);
-}
-
int pbx_checkcondition(const char *condition)
{
int res;
@@ -10568,37 +8526,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;
@@ -10659,7 +8586,6 @@ static void pbx_shutdown(void)
if (contexts_table) {
ast_hashtab_destroy(contexts_table, NULL);
}
- pbx_builtin_clear_globals();
}
static void print_hints_key(void *v_obj, void *where, ao2_prnt_fn *prnt)
diff --git a/main/pbx_app.c b/main/pbx_app.c
new file mode 100644
index 000000000..bb60b8e78
--- /dev/null
+++ b/main/pbx_app.c
@@ -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_FILE_VERSION(__FILE__, "$Revision$")
+
+#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;
+}
diff --git a/main/pbx_builtins.c b/main/pbx_builtins.c
index 351e28c8d..fa155888a 100644
--- a/main/pbx_builtins.c
+++ b/main/pbx_builtins.c
@@ -29,7 +29,7 @@
#include "asterisk.h"
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+ASTERISK_REGISTER_FILE()
#include "asterisk/_private.h"
#include "asterisk/pbx.h"
@@ -41,7 +41,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/module.h"
#include "pbx_private.h"
- /*** DOCUMENTATION
+/*** DOCUMENTATION
<application name="Answer" language="en_US">
<synopsis>
Answer a channel if ringing.
@@ -552,66 +552,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<ref type="application">SayNumber</ref>
</see-also>
</application>
- <application name="Set" language="en_US">
- <synopsis>
- Set channel variable or function value.
- </synopsis>
- <syntax argsep="=">
- <parameter name="name" required="true" />
- <parameter name="value" required="true" />
- </syntax>
- <description>
- <para>This function can be used to set the value of channel variables or dialplan functions.
- When setting variables, if the variable name is prefixed with <literal>_</literal>,
- the variable will be inherited into channels created from the current channel.
- If the variable name is prefixed with <literal>__</literal>, the variable will be
- inherited into channels created from the current channel and all children channels.</para>
- <note><para>If (and only if), in <filename>/etc/asterisk/asterisk.conf</filename>, you have
- a <literal>[compat]</literal> category, and you have <literal>app_set = 1.4</literal> under that, then
- the behavior of this app changes, and strips surrounding quotes from the right hand side as
- it did previously in 1.4.
- The advantages of not stripping out quoting, and not caring about the separator characters (comma and vertical bar)
- were sufficient to make these changes in 1.6. Confusion about how many backslashes would be needed to properly
- protect separators and quotes in various database access strings has been greatly
- reduced by these changes.</para></note>
- </description>
- <see-also>
- <ref type="application">MSet</ref>
- <ref type="function">GLOBAL</ref>
- <ref type="function">SET</ref>
- <ref type="function">ENV</ref>
- </see-also>
- </application>
- <application name="MSet" language="en_US">
- <synopsis>
- Set channel variable(s) or function value(s).
- </synopsis>
- <syntax>
- <parameter name="set1" required="true" argsep="=">
- <argument name="name1" required="true" />
- <argument name="value1" required="true" />
- </parameter>
- <parameter name="set2" multiple="true" argsep="=">
- <argument name="name2" required="true" />
- <argument name="value2" required="true" />
- </parameter>
- </syntax>
- <description>
- <para>This function can be used to set the value of channel variables or dialplan functions.
- When setting variables, if the variable name is prefixed with <literal>_</literal>,
- the variable will be inherited into channels created from the current channel
- If the variable name is prefixed with <literal>__</literal>, the variable will be
- inherited into channels created from the current channel and all children channels.
- MSet behaves in a similar fashion to the way Set worked in 1.2/1.4 and is thus
- prone to doing things that you may not expect. For example, it strips surrounding
- double-quotes from the right-hand side (value). If you need to put a separator
- character (comma or vert-bar), you will need to escape them by inserting a backslash
- before them. Avoid its use if possible.</para>
- </description>
- <see-also>
- <ref type="application">Set</ref>
- </see-also>
- </application>
<application name="SetAMAFlags" language="en_US">
<synopsis>
Set the AMA Flags.
@@ -1464,8 +1404,6 @@ struct pbx_builtin {
{ "SayDigits", pbx_builtin_saydigits },
{ "SayNumber", pbx_builtin_saynumber },
{ "SayPhonetic", pbx_builtin_sayphonetic },
- { "Set", pbx_builtin_setvar },
- { "MSet", pbx_builtin_setvar_multiple },
{ "SetAMAFlags", pbx_builtin_setamaflags },
{ "Wait", pbx_builtin_wait },
{ "WaitExten", pbx_builtin_waitexten }
diff --git a/main/pbx_functions.c b/main/pbx_functions.c
index b8be2bc40..bc738b043 100644
--- a/main/pbx_functions.c
+++ b/main/pbx_functions.c
@@ -29,7 +29,7 @@
#include "asterisk.h"
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+ASTERISK_REGISTER_FILE()
#include "asterisk/_private.h"
#include "asterisk/cli.h"
diff --git a/main/pbx_hangup_handler.c b/main/pbx_hangup_handler.c
new file mode 100644
index 000000000..3b8254732
--- /dev/null
+++ b/main/pbx_hangup_handler.c
@@ -0,0 +1,300 @@
+/*
+ * 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 PBX Hangup Handler management routines.
+ *
+ * \author Corey Farrell <git@cfware.com>
+ */
+
+/*** MODULEINFO
+ <support_level>core</support_level>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/_private.h"
+#include "asterisk/app.h"
+#include "asterisk/cli.h"
+#include "asterisk/linkedlists.h"
+#include "asterisk/pbx.h"
+#include "asterisk/stasis_channels.h"
+#include "asterisk/utils.h"
+
+/*!
+ * \internal
+ * \brief Publish a hangup handler related message to \ref stasis
+ */
+static void publish_hangup_handler_message(const char *action, struct ast_channel *chan, const char *handler)
+{
+ RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
+
+ blob = ast_json_pack("{s: s, s: s}",
+ "type", action,
+ "handler", S_OR(handler, ""));
+ if (!blob) {
+ return;
+ }
+
+ ast_channel_publish_blob(chan, ast_channel_hangup_handler_type(), blob);
+}
+
+int ast_pbx_hangup_handler_run(struct ast_channel *chan)
+{
+ struct ast_hangup_handler_list *handlers;
+ struct ast_hangup_handler *h_handler;
+
+ ast_channel_lock(chan);
+ handlers = ast_channel_hangup_handlers(chan);
+ if (AST_LIST_EMPTY(handlers)) {
+ ast_channel_unlock(chan);
+ return 0;
+ }
+
+ /*
+ * Make sure that the channel is marked as hungup since we are
+ * going to run the hangup handlers on it.
+ */
+ ast_softhangup_nolock(chan, AST_SOFTHANGUP_HANGUP_EXEC);
+
+ for (;;) {
+ handlers = ast_channel_hangup_handlers(chan);
+ h_handler = AST_LIST_REMOVE_HEAD(handlers, node);
+ if (!h_handler) {
+ break;
+ }
+
+ publish_hangup_handler_message("run", chan, h_handler->args);
+ ast_channel_unlock(chan);
+
+ ast_app_exec_sub(NULL, chan, h_handler->args, 1);
+ ast_free(h_handler);
+
+ ast_channel_lock(chan);
+ }
+ ast_channel_unlock(chan);
+ return 1;
+}
+
+void ast_pbx_hangup_handler_init(struct ast_channel *chan)
+{
+ struct ast_hangup_handler_list *handlers;
+
+ handlers = ast_channel_hangup_handlers(chan);
+ AST_LIST_HEAD_INIT_NOLOCK(handlers);
+}
+
+void ast_pbx_hangup_handler_destroy(struct ast_channel *chan)
+{
+ struct ast_hangup_handler_list *handlers;
+ struct ast_hangup_handler *h_handler;
+
+ ast_channel_lock(chan);
+
+ /* Get rid of each of the hangup handlers on the channel */
+ handlers = ast_channel_hangup_handlers(chan);
+ while ((h_handler = AST_LIST_REMOVE_HEAD(handlers, node))) {
+ ast_free(h_handler);
+ }
+
+ ast_channel_unlock(chan);
+}
+
+int ast_pbx_hangup_handler_pop(struct ast_channel *chan)
+{
+ struct ast_hangup_handler_list *handlers;
+ struct ast_hangup_handler *h_handler;
+
+ ast_channel_lock(chan);
+ handlers = ast_channel_hangup_handlers(chan);
+ h_handler = AST_LIST_REMOVE_HEAD(handlers, node);
+ if (h_handler) {
+ publish_hangup_handler_message("pop", chan, h_handler->args);
+ }
+ ast_channel_unlock(chan);
+ if (h_handler) {
+ ast_free(h_handler);
+ return 1;
+ }
+ return 0;
+}
+
+void ast_pbx_hangup_handler_push(struct ast_channel *chan, const char *handler)
+{
+ struct ast_hangup_handler_list *handlers;
+ struct ast_hangup_handler *h_handler;
+ const char *expanded_handler;
+
+ if (ast_strlen_zero(handler)) {
+ return;
+ }
+
+ expanded_handler = ast_app_expand_sub_args(chan, handler);
+ if (!expanded_handler) {
+ return;
+ }
+ h_handler = ast_malloc(sizeof(*h_handler) + 1 + strlen(expanded_handler));
+ if (!h_handler) {
+ ast_free((char *) expanded_handler);
+ return;
+ }
+ strcpy(h_handler->args, expanded_handler);/* Safe */
+ ast_free((char *) expanded_handler);
+
+ ast_channel_lock(chan);
+
+ handlers = ast_channel_hangup_handlers(chan);
+ AST_LIST_INSERT_HEAD(handlers, h_handler, node);
+ publish_hangup_handler_message("push", chan, h_handler->args);
+ ast_channel_unlock(chan);
+}
+
+#define HANDLER_FORMAT "%-30s %s\n"
+
+/*!
+ * \internal
+ * \brief CLI output the hangup handler headers.
+ * \since 11.0
+ *
+ * \param fd CLI file descriptor to use.
+ *
+ * \return Nothing
+ */
+static void ast_pbx_hangup_handler_headers(int fd)
+{
+ ast_cli(fd, HANDLER_FORMAT, "Channel", "Handler");
+}
+
+/*!
+ * \internal
+ * \brief CLI output the channel hangup handlers.
+ * \since 11.0
+ *
+ * \param fd CLI file descriptor to use.
+ * \param chan Channel to show hangup handlers.
+ *
+ * \return Nothing
+ */
+static void ast_pbx_hangup_handler_show(int fd, struct ast_channel *chan)
+{
+ struct ast_hangup_handler_list *handlers;
+ struct ast_hangup_handler *h_handler;
+ int first = 1;
+
+ ast_channel_lock(chan);
+ handlers = ast_channel_hangup_handlers(chan);
+ AST_LIST_TRAVERSE(handlers, h_handler, node) {
+ ast_cli(fd, HANDLER_FORMAT, first ? ast_channel_name(chan) : "", h_handler->args);
+ first = 0;
+ }
+ ast_channel_unlock(chan);
+}
+
+/*
+ * \brief 'show hanguphandlers <channel>' CLI command implementation function...
+ */
+static char *handle_show_hangup_channel(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct ast_channel *chan;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "core show hanguphandlers";
+ e->usage =
+ "Usage: core show hanguphandlers <channel>\n"
+ " Show hangup handlers of a specified channel.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return ast_complete_channels(a->line, a->word, a->pos, a->n, e->args);
+ }
+
+ if (a->argc < 4) {
+ return CLI_SHOWUSAGE;
+ }
+
+ chan = ast_channel_get_by_name(a->argv[3]);
+ if (!chan) {
+ ast_cli(a->fd, "Channel does not exist.\n");
+ return CLI_FAILURE;
+ }
+
+ ast_pbx_hangup_handler_headers(a->fd);
+ ast_pbx_hangup_handler_show(a->fd, chan);
+
+ ast_channel_unref(chan);
+
+ return CLI_SUCCESS;
+}
+
+/*
+ * \brief 'show hanguphandlers all' CLI command implementation function...
+ */
+static char *handle_show_hangup_all(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct ast_channel_iterator *iter;
+ struct ast_channel *chan;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "core show hanguphandlers all";
+ e->usage =
+ "Usage: core show hanguphandlers all\n"
+ " Show hangup handlers for all channels.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return ast_complete_channels(a->line, a->word, a->pos, a->n, e->args);
+ }
+
+ if (a->argc < 4) {
+ return CLI_SHOWUSAGE;
+ }
+
+ iter = ast_channel_iterator_all_new();
+ if (!iter) {
+ return CLI_FAILURE;
+ }
+
+ ast_pbx_hangup_handler_headers(a->fd);
+ for (; (chan = ast_channel_iterator_next(iter)); ast_channel_unref(chan)) {
+ ast_pbx_hangup_handler_show(a->fd, chan);
+ }
+ ast_channel_iterator_destroy(iter);
+
+ return CLI_SUCCESS;
+}
+
+static struct ast_cli_entry cli[] = {
+ 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"),
+};
+
+static void unload_pbx_hangup_handler(void)
+{
+ ast_cli_unregister_multiple(cli, ARRAY_LEN(cli));
+}
+
+int load_pbx_hangup_handler(void)
+{
+ ast_cli_register_multiple(cli, ARRAY_LEN(cli));
+ ast_register_cleanup(unload_pbx_hangup_handler);
+
+ return 0;
+}
diff --git a/main/pbx_private.h b/main/pbx_private.h
index e1711796c..e09194188 100644
--- a/main/pbx_private.h
+++ b/main/pbx_private.h
@@ -28,10 +28,19 @@ 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 *);
+/*! 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
#endif /* _PBX_PRIVATE_H */
diff --git a/main/pbx_switch.c b/main/pbx_switch.c
new file mode 100644
index 000000000..4ac7f8ce7
--- /dev/null
+++ b/main/pbx_switch.c
@@ -0,0 +1,133 @@
+/*
+ * 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 PBX switch routines.
+ *
+ * \author Corey Farrell <git@cfware.com>
+ */
+
+/*** MODULEINFO
+ <support_level>core</support_level>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/_private.h"
+#include "asterisk/cli.h"
+#include "asterisk/linkedlists.h"
+#include "asterisk/pbx.h"
+#include "pbx_private.h"
+
+static AST_RWLIST_HEAD_STATIC(switches, ast_switch);
+
+struct ast_switch *pbx_findswitch(const char *sw)
+{
+ struct ast_switch *asw;
+
+ AST_RWLIST_RDLOCK(&switches);
+ AST_RWLIST_TRAVERSE(&switches, asw, list) {
+ if (!strcasecmp(asw->name, sw))
+ break;
+ }
+ AST_RWLIST_UNLOCK(&switches);
+
+ return asw;
+}
+
+/*
+ * Append to the list. We don't have a tail pointer because we need
+ * to scan the list anyways to check for duplicates during insertion.
+ */
+int ast_register_switch(struct ast_switch *sw)
+{
+ struct ast_switch *tmp;
+
+ AST_RWLIST_WRLOCK(&switches);
+ AST_RWLIST_TRAVERSE(&switches, tmp, list) {
+ if (!strcasecmp(tmp->name, sw->name)) {
+ AST_RWLIST_UNLOCK(&switches);
+ ast_log(LOG_WARNING, "Switch '%s' already found\n", sw->name);
+ return -1;
+ }
+ }
+ AST_RWLIST_INSERT_TAIL(&switches, sw, list);
+ AST_RWLIST_UNLOCK(&switches);
+
+ return 0;
+}
+
+void ast_unregister_switch(struct ast_switch *sw)
+{
+ AST_RWLIST_WRLOCK(&switches);
+ AST_RWLIST_REMOVE(&switches, sw, list);
+ AST_RWLIST_UNLOCK(&switches);
+}
+
+/*! \brief handle_show_switches: CLI support for listing registered dial plan switches */
+static char *handle_show_switches(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct ast_switch *sw;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "core show switches";
+ e->usage =
+ "Usage: core show switches\n"
+ " List registered switches\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ AST_RWLIST_RDLOCK(&switches);
+
+ if (AST_RWLIST_EMPTY(&switches)) {
+ AST_RWLIST_UNLOCK(&switches);
+ ast_cli(a->fd, "There are no registered alternative switches\n");
+ return CLI_SUCCESS;
+ }
+
+ ast_cli(a->fd, "\n -= Registered Asterisk Alternative Switches =-\n");
+ AST_RWLIST_TRAVERSE(&switches, sw, list)
+ ast_cli(a->fd, "%s: %s\n", sw->name, sw->description);
+
+ AST_RWLIST_UNLOCK(&switches);
+
+ return CLI_SUCCESS;
+}
+
+static struct ast_cli_entry sw_cli[] = {
+ AST_CLI_DEFINE(handle_show_switches, "Show alternative switches"),
+};
+
+static void unload_pbx_switch(void)
+{
+ ast_cli_unregister_multiple(sw_cli, ARRAY_LEN(sw_cli));
+}
+
+int load_pbx_switch(void)
+{
+ ast_cli_register_multiple(sw_cli, ARRAY_LEN(sw_cli));
+ ast_register_cleanup(unload_pbx_switch);
+
+ return 0;
+}
diff --git a/main/pbx_timing.c b/main/pbx_timing.c
new file mode 100644
index 000000000..53529bb83
--- /dev/null
+++ b/main/pbx_timing.c
@@ -0,0 +1,294 @@
+/*
+ * 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 PBX timing routines.
+ *
+ * \author Corey Farrell <git@cfware.com>
+ */
+
+/*** MODULEINFO
+ <support_level>core</support_level>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/localtime.h"
+#include "asterisk/logger.h"
+#include "asterisk/pbx.h"
+#include "asterisk/strings.h"
+#include "asterisk/utils.h"
+
+/*! \brief Helper for get_range.
+ * return the index of the matching entry, starting from 1.
+ * If names is not supplied, try numeric values.
+ */
+static int lookup_name(const char *s, const char * const names[], int max)
+{
+ int i;
+
+ if (names && *s > '9') {
+ for (i = 0; names[i]; i++) {
+ if (!strcasecmp(s, names[i])) {
+ return i;
+ }
+ }
+ }
+
+ /* Allow months and weekdays to be specified as numbers, as well */
+ if (sscanf(s, "%2d", &i) == 1 && i >= 1 && i <= max) {
+ /* What the array offset would have been: "1" would be at offset 0 */
+ return i - 1;
+ }
+ return -1; /* error return */
+}
+
+/*! \brief helper function to return a range up to max (7, 12, 31 respectively).
+ * names, if supplied, is an array of names that should be mapped to numbers.
+ */
+static unsigned get_range(char *src, int max, const char * const names[], const char *msg)
+{
+ int start, end; /* start and ending position */
+ unsigned int mask = 0;
+ char *part;
+
+ /* Check for whole range */
+ if (ast_strlen_zero(src) || !strcmp(src, "*")) {
+ return (1 << max) - 1;
+ }
+
+ while ((part = strsep(&src, "&"))) {
+ /* Get start and ending position */
+ char *endpart = strchr(part, '-');
+ if (endpart) {
+ *endpart++ = '\0';
+ }
+ /* Find the start */
+ if ((start = lookup_name(part, names, max)) < 0) {
+ ast_log(LOG_WARNING, "Invalid %s '%s', skipping element\n", msg, part);
+ continue;
+ }
+ if (endpart) { /* find end of range */
+ if ((end = lookup_name(endpart, names, max)) < 0) {
+ ast_log(LOG_WARNING, "Invalid end %s '%s', skipping element\n", msg, endpart);
+ continue;
+ }
+ } else {
+ end = start;
+ }
+ /* Fill the mask. Remember that ranges are cyclic */
+ mask |= (1 << end); /* initialize with last element */
+ while (start != end) {
+ mask |= (1 << start);
+ if (++start >= max) {
+ start = 0;
+ }
+ }
+ }
+ return mask;
+}
+
+/*! \brief store a bitmask of valid times, one bit each 1 minute */
+static void get_timerange(struct ast_timing *i, char *times)
+{
+ char *endpart, *part;
+ int x;
+ int st_h, st_m;
+ int endh, endm;
+ int minute_start, minute_end;
+
+ /* start disabling all times, fill the fields with 0's, as they may contain garbage */
+ memset(i->minmask, 0, sizeof(i->minmask));
+
+ /* 1-minute per bit */
+ /* Star is all times */
+ if (ast_strlen_zero(times) || !strcmp(times, "*")) {
+ /* 48, because each hour takes 2 integers; 30 bits each */
+ for (x = 0; x < 48; x++) {
+ i->minmask[x] = 0x3fffffff; /* 30 bits */
+ }
+ return;
+ }
+ /* Otherwise expect a range */
+ while ((part = strsep(&times, "&"))) {
+ if (!(endpart = strchr(part, '-'))) {
+ if (sscanf(part, "%2d:%2d", &st_h, &st_m) != 2 || st_h < 0 || st_h > 23 || st_m < 0 || st_m > 59) {
+ ast_log(LOG_WARNING, "%s isn't a valid time.\n", part);
+ continue;
+ }
+ i->minmask[st_h * 2 + (st_m >= 30 ? 1 : 0)] |= (1 << (st_m % 30));
+ continue;
+ }
+ *endpart++ = '\0';
+ /* why skip non digits? Mostly to skip spaces */
+ while (*endpart && !isdigit(*endpart)) {
+ endpart++;
+ }
+ if (!*endpart) {
+ ast_log(LOG_WARNING, "Invalid time range starting with '%s-'.\n", part);
+ continue;
+ }
+ if (sscanf(part, "%2d:%2d", &st_h, &st_m) != 2 || st_h < 0 || st_h > 23 || st_m < 0 || st_m > 59) {
+ ast_log(LOG_WARNING, "'%s' isn't a valid start time.\n", part);
+ continue;
+ }
+ if (sscanf(endpart, "%2d:%2d", &endh, &endm) != 2 || endh < 0 || endh > 23 || endm < 0 || endm > 59) {
+ ast_log(LOG_WARNING, "'%s' isn't a valid end time.\n", endpart);
+ continue;
+ }
+ minute_start = st_h * 60 + st_m;
+ minute_end = endh * 60 + endm;
+ /* Go through the time and enable each appropriate bit */
+ for (x = minute_start; x != minute_end; x = (x + 1) % (24 * 60)) {
+ i->minmask[x / 30] |= (1 << (x % 30));
+ }
+ /* Do the last one */
+ i->minmask[x / 30] |= (1 << (x % 30));
+ }
+ /* All done */
+ return;
+}
+
+static const char * const days[] =
+{
+ "sun",
+ "mon",
+ "tue",
+ "wed",
+ "thu",
+ "fri",
+ "sat",
+ NULL,
+};
+
+static const char * const months[] =
+{
+ "jan",
+ "feb",
+ "mar",
+ "apr",
+ "may",
+ "jun",
+ "jul",
+ "aug",
+ "sep",
+ "oct",
+ "nov",
+ "dec",
+ NULL,
+};
+
+/*! /brief Build timing
+ *
+ * /param i info
+ * /param info_in
+ *
+ */
+int ast_build_timing(struct ast_timing *i, const char *info_in)
+{
+ char *info;
+ int j, num_fields, last_sep = -1;
+
+ i->timezone = NULL;
+
+ /* Check for empty just in case */
+ if (ast_strlen_zero(info_in)) {
+ return 0;
+ }
+
+ /* make a copy just in case we were passed a static string */
+ info = ast_strdupa(info_in);
+
+ /* count the number of fields in the timespec */
+ for (j = 0, num_fields = 1; info[j] != '\0'; j++) {
+ if (info[j] == ',') {
+ last_sep = j;
+ num_fields++;
+ }
+ }
+
+ /* save the timezone, if it is specified */
+ if (num_fields == 5) {
+ i->timezone = ast_strdup(info + last_sep + 1);
+ }
+
+ /* Assume everything except time */
+ i->monthmask = 0xfff; /* 12 bits */
+ i->daymask = 0x7fffffffU; /* 31 bits */
+ i->dowmask = 0x7f; /* 7 bits */
+ /* on each call, use strsep() to move info to the next argument */
+ get_timerange(i, strsep(&info, "|,"));
+ if (info)
+ i->dowmask = get_range(strsep(&info, "|,"), 7, days, "day of week");
+ if (info)
+ i->daymask = get_range(strsep(&info, "|,"), 31, NULL, "day");
+ if (info)
+ i->monthmask = get_range(strsep(&info, "|,"), 12, months, "month");
+ return 1;
+}
+
+int ast_check_timing(const struct ast_timing *i)
+{
+ return ast_check_timing2(i, ast_tvnow());
+}
+
+int ast_check_timing2(const struct ast_timing *i, const struct timeval tv)
+{
+ struct ast_tm tm;
+
+ ast_localtime(&tv, &tm, i->timezone);
+
+ /* If it's not the right month, return */
+ if (!(i->monthmask & (1 << tm.tm_mon)))
+ return 0;
+
+ /* If it's not that time of the month.... */
+ /* Warning, tm_mday has range 1..31! */
+ if (!(i->daymask & (1 << (tm.tm_mday-1))))
+ return 0;
+
+ /* If it's not the right day of the week */
+ if (!(i->dowmask & (1 << tm.tm_wday)))
+ return 0;
+
+ /* Sanity check the hour just to be safe */
+ if ((tm.tm_hour < 0) || (tm.tm_hour > 23)) {
+ ast_log(LOG_WARNING, "Insane time...\n");
+ return 0;
+ }
+
+ /* Now the tough part, we calculate if it fits
+ in the right time based on min/hour */
+ if (!(i->minmask[tm.tm_hour * 2 + (tm.tm_min >= 30 ? 1 : 0)] & (1 << (tm.tm_min >= 30 ? tm.tm_min - 30 : tm.tm_min))))
+ return 0;
+
+ /* If we got this far, then we're good */
+ return 1;
+}
+
+int ast_destroy_timing(struct ast_timing *i)
+{
+ if (i->timezone) {
+ ast_free(i->timezone);
+ i->timezone = NULL;
+ }
+ return 0;
+}
diff --git a/main/pbx_variables.c b/main/pbx_variables.c
new file mode 100644
index 000000000..16ddbd0ca
--- /dev/null
+++ b/main/pbx_variables.c
@@ -0,0 +1,1180 @@
+/*
+ * 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 PBX variables routines.
+ *
+ * \author Corey Farrell <git@cfware.com>
+ */
+
+/*** MODULEINFO
+ <support_level>core</support_level>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/_private.h"
+#include "asterisk/app.h"
+#include "asterisk/ast_expr.h"
+#include "asterisk/chanvars.h"
+#include "asterisk/cli.h"
+#include "asterisk/linkedlists.h"
+#include "asterisk/lock.h"
+#include "asterisk/module.h"
+#include "asterisk/paths.h"
+#include "asterisk/pbx.h"
+#include "asterisk/stasis_channels.h"
+#include "pbx_private.h"
+
+/*** DOCUMENTATION
+ <application name="Set" language="en_US">
+ <synopsis>
+ Set channel variable or function value.
+ </synopsis>
+ <syntax argsep="=">
+ <parameter name="name" required="true" />
+ <parameter name="value" required="true" />
+ </syntax>
+ <description>
+ <para>This function can be used to set the value of channel variables or dialplan functions.
+ When setting variables, if the variable name is prefixed with <literal>_</literal>,
+ the variable will be inherited into channels created from the current channel.
+ If the variable name is prefixed with <literal>__</literal>, the variable will be
+ inherited into channels created from the current channel and all children channels.</para>
+ <note><para>If (and only if), in <filename>/etc/asterisk/asterisk.conf</filename>, you have
+ a <literal>[compat]</literal> category, and you have <literal>app_set = 1.4</literal> under that, then
+ the behavior of this app changes, and strips surrounding quotes from the right hand side as
+ it did previously in 1.4.
+ The advantages of not stripping out quoting, and not caring about the separator characters (comma and vertical bar)
+ were sufficient to make these changes in 1.6. Confusion about how many backslashes would be needed to properly
+ protect separators and quotes in various database access strings has been greatly
+ reduced by these changes.</para></note>
+ </description>
+ <see-also>
+ <ref type="application">MSet</ref>
+ <ref type="function">GLOBAL</ref>
+ <ref type="function">SET</ref>
+ <ref type="function">ENV</ref>
+ </see-also>
+ </application>
+ <application name="MSet" language="en_US">
+ <synopsis>
+ Set channel variable(s) or function value(s).
+ </synopsis>
+ <syntax>
+ <parameter name="set1" required="true" argsep="=">
+ <argument name="name1" required="true" />
+ <argument name="value1" required="true" />
+ </parameter>
+ <parameter name="set2" multiple="true" argsep="=">
+ <argument name="name2" required="true" />
+ <argument name="value2" required="true" />
+ </parameter>
+ </syntax>
+ <description>
+ <para>This function can be used to set the value of channel variables or dialplan functions.
+ When setting variables, if the variable name is prefixed with <literal>_</literal>,
+ the variable will be inherited into channels created from the current channel
+ If the variable name is prefixed with <literal>__</literal>, the variable will be
+ inherited into channels created from the current channel and all children channels.
+ MSet behaves in a similar fashion to the way Set worked in 1.2/1.4 and is thus
+ prone to doing things that you may not expect. For example, it strips surrounding
+ double-quotes from the right-hand side (value). If you need to put a separator
+ character (comma or vert-bar), you will need to escape them by inserting a backslash
+ before them. Avoid its use if possible.</para>
+ </description>
+ <see-also>
+ <ref type="application">Set</ref>
+ </see-also>
+ </application>
+ ***/
+
+AST_RWLOCK_DEFINE_STATIC(globalslock);
+static struct varshead globals = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
+
+/*!
+ * \brief extract offset:length from variable name.
+ * \return 1 if there is a offset:length part, which is
+ * trimmed off (values go into variables)
+ */
+static int parse_variable_name(char *var, int *offset, int *length, int *isfunc)
+{
+ int parens = 0;
+
+ *offset = 0;
+ *length = INT_MAX;
+ *isfunc = 0;
+ for (; *var; var++) {
+ if (*var == '(') {
+ (*isfunc)++;
+ parens++;
+ } else if (*var == ')') {
+ parens--;
+ } else if (*var == ':' && parens == 0) {
+ *var++ = '\0';
+ sscanf(var, "%30d:%30d", offset, length);
+ return 1; /* offset:length valid */
+ }
+ }
+ return 0;
+}
+
+/*!
+ *\brief takes a substring. It is ok to call with value == workspace.
+ * \param value
+ * \param offset < 0 means start from the end of the string and set the beginning
+ * to be that many characters back.
+ * \param length is the length of the substring, a value less than 0 means to leave
+ * that many off the end.
+ * \param workspace
+ * \param workspace_len
+ * Always return a copy in workspace.
+ */
+static char *substring(const char *value, int offset, int length, char *workspace, size_t workspace_len)
+{
+ char *ret = workspace;
+ int lr; /* length of the input string after the copy */
+
+ ast_copy_string(workspace, value, workspace_len); /* always make a copy */
+
+ lr = strlen(ret); /* compute length after copy, so we never go out of the workspace */
+
+ /* Quick check if no need to do anything */
+ if (offset == 0 && length >= lr) /* take the whole string */
+ return ret;
+
+ if (offset < 0) { /* translate negative offset into positive ones */
+ offset = lr + offset;
+ if (offset < 0) /* If the negative offset was greater than the length of the string, just start at the beginning */
+ offset = 0;
+ }
+
+ /* too large offset result in empty string so we know what to return */
+ if (offset >= lr)
+ return ret + lr; /* the final '\0' */
+
+ ret += offset; /* move to the start position */
+ if (length >= 0 && length < lr - offset) /* truncate if necessary */
+ ret[length] = '\0';
+ else if (length < 0) {
+ if (lr > offset - length) /* After we remove from the front and from the rear, is there anything left? */
+ ret[lr + length - offset] = '\0';
+ else
+ ret[0] = '\0';
+ }
+
+ return ret;
+}
+
+static const char *ast_str_substring(struct ast_str *value, int offset, int length)
+{
+ int lr; /* length of the input string after the copy */
+
+ lr = ast_str_strlen(value); /* compute length after copy, so we never go out of the workspace */
+
+ /* Quick check if no need to do anything */
+ if (offset == 0 && length >= lr) /* take the whole string */
+ return ast_str_buffer(value);
+
+ if (offset < 0) { /* translate negative offset into positive ones */
+ offset = lr + offset;
+ if (offset < 0) /* If the negative offset was greater than the length of the string, just start at the beginning */
+ offset = 0;
+ }
+
+ /* too large offset result in empty string so we know what to return */
+ if (offset >= lr) {
+ ast_str_reset(value);
+ return ast_str_buffer(value);
+ }
+
+ if (offset > 0) {
+ /* Go ahead and chop off the beginning */
+ memmove(ast_str_buffer(value), ast_str_buffer(value) + offset, ast_str_strlen(value) - offset + 1);
+ lr -= offset;
+ }
+
+ if (length >= 0 && length < lr) { /* truncate if necessary */
+ char *tmp = ast_str_buffer(value);
+ tmp[length] = '\0';
+ ast_str_update(value);
+ } else if (length < 0) {
+ if (lr > -length) { /* After we remove from the front and from the rear, is there anything left? */
+ char *tmp = ast_str_buffer(value);
+ tmp[lr + length] = '\0';
+ ast_str_update(value);
+ } else {
+ ast_str_reset(value);
+ }
+ } else {
+ /* Nothing to do, but update the buffer length */
+ ast_str_update(value);
+ }
+
+ return ast_str_buffer(value);
+}
+
+/*! \brief Support for Asterisk built-in variables in the dialplan
+
+\note See also
+ - \ref AstVar Channel variables
+ - \ref AstCauses The HANGUPCAUSE variable
+ */
+void pbx_retrieve_variable(struct ast_channel *c, const char *var, char **ret, char *workspace, int workspacelen, struct varshead *headp)
+{
+ struct ast_str *str = ast_str_create(16);
+ const char *cret;
+
+ cret = ast_str_retrieve_variable(&str, 0, c, headp, var);
+ ast_copy_string(workspace, ast_str_buffer(str), workspacelen);
+ *ret = cret ? workspace : NULL;
+ ast_free(str);
+}
+
+const char *ast_str_retrieve_variable(struct ast_str **str, ssize_t maxlen, struct ast_channel *c, struct varshead *headp, const char *var)
+{
+ const char not_found = '\0';
+ char *tmpvar;
+ const char *ret;
+ const char *s; /* the result */
+ int offset, length;
+ int i, need_substring;
+ struct varshead *places[2] = { headp, &globals }; /* list of places where we may look */
+ char workspace[20];
+
+ if (c) {
+ ast_channel_lock(c);
+ places[0] = ast_channel_varshead(c);
+ }
+ /*
+ * Make a copy of var because parse_variable_name() modifies the string.
+ * Then if called directly, we might need to run substring() on the result;
+ * remember this for later in 'need_substring', 'offset' and 'length'
+ */
+ tmpvar = ast_strdupa(var); /* parse_variable_name modifies the string */
+ need_substring = parse_variable_name(tmpvar, &offset, &length, &i /* ignored */);
+
+ /*
+ * Look first into predefined variables, then into variable lists.
+ * Variable 's' points to the result, according to the following rules:
+ * s == &not_found (set at the beginning) means that we did not find a
+ * matching variable and need to look into more places.
+ * If s != &not_found, s is a valid result string as follows:
+ * s = NULL if the variable does not have a value;
+ * you typically do this when looking for an unset predefined variable.
+ * s = workspace if the result has been assembled there;
+ * typically done when the result is built e.g. with an snprintf(),
+ * so we don't need to do an additional copy.
+ * s != workspace in case we have a string, that needs to be copied
+ * (the ast_copy_string is done once for all at the end).
+ * Typically done when the result is already available in some string.
+ */
+ s = &not_found; /* default value */
+ if (c) { /* This group requires a valid channel */
+ /* Names with common parts are looked up a piece at a time using strncmp. */
+ if (!strncmp(var, "CALL", 4)) {
+ if (!strncmp(var + 4, "ING", 3)) {
+ if (!strcmp(var + 7, "PRES")) { /* CALLINGPRES */
+ ast_str_set(str, maxlen, "%d",
+ ast_party_id_presentation(&ast_channel_caller(c)->id));
+ s = ast_str_buffer(*str);
+ } else if (!strcmp(var + 7, "ANI2")) { /* CALLINGANI2 */
+ ast_str_set(str, maxlen, "%d", ast_channel_caller(c)->ani2);
+ s = ast_str_buffer(*str);
+ } else if (!strcmp(var + 7, "TON")) { /* CALLINGTON */
+ ast_str_set(str, maxlen, "%d", ast_channel_caller(c)->id.number.plan);
+ s = ast_str_buffer(*str);
+ } else if (!strcmp(var + 7, "TNS")) { /* CALLINGTNS */
+ ast_str_set(str, maxlen, "%d", ast_channel_dialed(c)->transit_network_select);
+ s = ast_str_buffer(*str);
+ }
+ }
+ } else if (!strcmp(var, "HINT")) {
+ s = ast_str_get_hint(str, maxlen, NULL, 0, c, ast_channel_context(c), ast_channel_exten(c)) ? ast_str_buffer(*str) : NULL;
+ } else if (!strcmp(var, "HINTNAME")) {
+ s = ast_str_get_hint(NULL, 0, str, maxlen, c, ast_channel_context(c), ast_channel_exten(c)) ? ast_str_buffer(*str) : NULL;
+ } else if (!strcmp(var, "EXTEN")) {
+ s = ast_channel_exten(c);
+ } else if (!strcmp(var, "CONTEXT")) {
+ s = ast_channel_context(c);
+ } else if (!strcmp(var, "PRIORITY")) {
+ ast_str_set(str, maxlen, "%d", ast_channel_priority(c));
+ s = ast_str_buffer(*str);
+ } else if (!strcmp(var, "CHANNEL")) {
+ s = ast_channel_name(c);
+ } else if (!strcmp(var, "UNIQUEID")) {
+ s = ast_channel_uniqueid(c);
+ } else if (!strcmp(var, "HANGUPCAUSE")) {
+ ast_str_set(str, maxlen, "%d", ast_channel_hangupcause(c));
+ s = ast_str_buffer(*str);
+ }
+ }
+ if (s == &not_found) { /* look for more */
+ if (!strcmp(var, "EPOCH")) {
+ ast_str_set(str, maxlen, "%d", (int) time(NULL));
+ s = ast_str_buffer(*str);
+ } else if (!strcmp(var, "SYSTEMNAME")) {
+ s = ast_config_AST_SYSTEM_NAME;
+ } else if (!strcmp(var, "ASTETCDIR")) {
+ s = ast_config_AST_CONFIG_DIR;
+ } else if (!strcmp(var, "ASTMODDIR")) {
+ s = ast_config_AST_MODULE_DIR;
+ } else if (!strcmp(var, "ASTVARLIBDIR")) {
+ s = ast_config_AST_VAR_DIR;
+ } else if (!strcmp(var, "ASTDBDIR")) {
+ s = ast_config_AST_DB;
+ } else if (!strcmp(var, "ASTKEYDIR")) {
+ s = ast_config_AST_KEY_DIR;
+ } else if (!strcmp(var, "ASTDATADIR")) {
+ s = ast_config_AST_DATA_DIR;
+ } else if (!strcmp(var, "ASTAGIDIR")) {
+ s = ast_config_AST_AGI_DIR;
+ } else if (!strcmp(var, "ASTSPOOLDIR")) {
+ s = ast_config_AST_SPOOL_DIR;
+ } else if (!strcmp(var, "ASTRUNDIR")) {
+ s = ast_config_AST_RUN_DIR;
+ } else if (!strcmp(var, "ASTLOGDIR")) {
+ s = ast_config_AST_LOG_DIR;
+ } else if (!strcmp(var, "ENTITYID")) {
+ ast_eid_to_str(workspace, sizeof(workspace), &ast_eid_default);
+ s = workspace;
+ }
+ }
+ /* if not found, look into chanvars or global vars */
+ for (i = 0; s == &not_found && i < ARRAY_LEN(places); i++) {
+ struct ast_var_t *variables;
+ if (!places[i])
+ continue;
+ if (places[i] == &globals)
+ ast_rwlock_rdlock(&globalslock);
+ AST_LIST_TRAVERSE(places[i], variables, entries) {
+ if (!strcmp(ast_var_name(variables), var)) {
+ s = ast_var_value(variables);
+ break;
+ }
+ }
+ if (places[i] == &globals)
+ ast_rwlock_unlock(&globalslock);
+ }
+ if (s == &not_found || s == NULL) {
+ ast_debug(5, "Result of '%s' is NULL\n", var);
+ ret = NULL;
+ } else {
+ ast_debug(5, "Result of '%s' is '%s'\n", var, s);
+ if (s != ast_str_buffer(*str)) {
+ ast_str_set(str, maxlen, "%s", s);
+ }
+ ret = ast_str_buffer(*str);
+ if (need_substring) {
+ ret = ast_str_substring(*str, offset, length);
+ ast_debug(2, "Final result of '%s' is '%s'\n", var, ret);
+ }
+ }
+
+ if (c) {
+ ast_channel_unlock(c);
+ }
+ return ret;
+}
+
+void ast_str_substitute_variables_full(struct ast_str **buf, ssize_t maxlen, struct ast_channel *c, struct varshead *headp, const char *templ, size_t *used)
+{
+ /* Substitutes variables into buf, based on string templ */
+ char *cp4 = NULL;
+ const char *whereweare;
+ int orig_size = 0;
+ int offset, offset2, isfunction;
+ const char *nextvar, *nextexp, *nextthing;
+ const char *vars, *vare;
+ char *finalvars;
+ int pos, brackets, needsub, len;
+ struct ast_str *substr1 = ast_str_create(16), *substr2 = NULL, *substr3 = ast_str_create(16);
+
+ ast_str_reset(*buf);
+ whereweare = templ;
+ while (!ast_strlen_zero(whereweare)) {
+ /* reset our buffer */
+ ast_str_reset(substr3);
+
+ /* Assume we're copying the whole remaining string */
+ pos = strlen(whereweare);
+ nextvar = NULL;
+ nextexp = NULL;
+ nextthing = strchr(whereweare, '$');
+ if (nextthing) {
+ switch (nextthing[1]) {
+ case '{':
+ nextvar = nextthing;
+ pos = nextvar - whereweare;
+ break;
+ case '[':
+ nextexp = nextthing;
+ pos = nextexp - whereweare;
+ break;
+ default:
+ pos = 1;
+ }
+ }
+
+ if (pos) {
+ /* Copy that many bytes */
+ ast_str_append_substr(buf, maxlen, whereweare, pos);
+
+ templ += pos;
+ whereweare += pos;
+ }
+
+ if (nextvar) {
+ /* We have a variable. Find the start and end, and determine
+ if we are going to have to recursively call ourselves on the
+ contents */
+ vars = vare = nextvar + 2;
+ brackets = 1;
+ needsub = 0;
+
+ /* Find the end of it */
+ while (brackets && *vare) {
+ if ((vare[0] == '$') && (vare[1] == '{')) {
+ needsub++;
+ } else if (vare[0] == '{') {
+ brackets++;
+ } else if (vare[0] == '}') {
+ brackets--;
+ } else if ((vare[0] == '$') && (vare[1] == '['))
+ needsub++;
+ vare++;
+ }
+ if (brackets)
+ ast_log(LOG_WARNING, "Error in extension logic (missing '}')\n");
+ len = vare - vars - 1;
+
+ /* Skip totally over variable string */
+ whereweare += (len + 3);
+
+ /* Store variable name (and truncate) */
+ ast_str_set_substr(&substr1, 0, vars, len);
+ ast_debug(5, "Evaluating '%s' (from '%s' len %d)\n", ast_str_buffer(substr1), vars, len);
+
+ /* Substitute if necessary */
+ if (needsub) {
+ size_t my_used;
+
+ if (!substr2) {
+ substr2 = ast_str_create(16);
+ }
+ ast_str_substitute_variables_full(&substr2, 0, c, headp, ast_str_buffer(substr1), &my_used);
+ finalvars = ast_str_buffer(substr2);
+ } else {
+ finalvars = ast_str_buffer(substr1);
+ }
+
+ parse_variable_name(finalvars, &offset, &offset2, &isfunction);
+ if (isfunction) {
+ /* Evaluate function */
+ if (c || !headp) {
+ cp4 = ast_func_read2(c, finalvars, &substr3, 0) ? NULL : ast_str_buffer(substr3);
+ } else {
+ struct varshead old;
+ struct ast_channel *bogus = ast_dummy_channel_alloc();
+ if (bogus) {
+ memcpy(&old, ast_channel_varshead(bogus), sizeof(old));
+ memcpy(ast_channel_varshead(bogus), headp, sizeof(*ast_channel_varshead(bogus)));
+ cp4 = ast_func_read2(c, finalvars, &substr3, 0) ? NULL : ast_str_buffer(substr3);
+ /* Don't deallocate the varshead that was passed in */
+ memcpy(ast_channel_varshead(bogus), &old, sizeof(*ast_channel_varshead(bogus)));
+ ast_channel_unref(bogus);
+ } else {
+ ast_log(LOG_ERROR, "Unable to allocate bogus channel for variable substitution. Function results may be blank.\n");
+ }
+ }
+ ast_debug(2, "Function %s result is '%s'\n", finalvars, cp4 ? cp4 : "(null)");
+ } else {
+ /* Retrieve variable value */
+ ast_str_retrieve_variable(&substr3, 0, c, headp, finalvars);
+ cp4 = ast_str_buffer(substr3);
+ }
+ if (cp4) {
+ ast_str_substring(substr3, offset, offset2);
+ ast_str_append(buf, maxlen, "%s", ast_str_buffer(substr3));
+ }
+ } else if (nextexp) {
+ /* We have an expression. Find the start and end, and determine
+ if we are going to have to recursively call ourselves on the
+ contents */
+ vars = vare = nextexp + 2;
+ brackets = 1;
+ needsub = 0;
+
+ /* Find the end of it */
+ while (brackets && *vare) {
+ if ((vare[0] == '$') && (vare[1] == '[')) {
+ needsub++;
+ brackets++;
+ vare++;
+ } else if (vare[0] == '[') {
+ brackets++;
+ } else if (vare[0] == ']') {
+ brackets--;
+ } else if ((vare[0] == '$') && (vare[1] == '{')) {
+ needsub++;
+ vare++;
+ }
+ vare++;
+ }
+ if (brackets)
+ ast_log(LOG_WARNING, "Error in extension logic (missing ']')\n");
+ len = vare - vars - 1;
+
+ /* Skip totally over expression */
+ whereweare += (len + 3);
+
+ /* Store variable name (and truncate) */
+ ast_str_set_substr(&substr1, 0, vars, len);
+
+ /* Substitute if necessary */
+ if (needsub) {
+ size_t my_used;
+
+ if (!substr2) {
+ substr2 = ast_str_create(16);
+ }
+ ast_str_substitute_variables_full(&substr2, 0, c, headp, ast_str_buffer(substr1), &my_used);
+ finalvars = ast_str_buffer(substr2);
+ } else {
+ finalvars = ast_str_buffer(substr1);
+ }
+
+ if (ast_str_expr(&substr3, 0, c, finalvars)) {
+ ast_debug(2, "Expression result is '%s'\n", ast_str_buffer(substr3));
+ }
+ ast_str_append(buf, maxlen, "%s", ast_str_buffer(substr3));
+ }
+ }
+ *used = ast_str_strlen(*buf) - orig_size;
+ ast_free(substr1);
+ ast_free(substr2);
+ ast_free(substr3);
+}
+
+void ast_str_substitute_variables(struct ast_str **buf, ssize_t maxlen, struct ast_channel *chan, const char *templ)
+{
+ size_t used;
+ ast_str_substitute_variables_full(buf, maxlen, chan, NULL, templ, &used);
+}
+
+void ast_str_substitute_variables_varshead(struct ast_str **buf, ssize_t maxlen, struct varshead *headp, const char *templ)
+{
+ size_t used;
+ ast_str_substitute_variables_full(buf, maxlen, NULL, headp, templ, &used);
+}
+
+void pbx_substitute_variables_helper_full(struct ast_channel *c, struct varshead *headp, const char *cp1, char *cp2, int count, size_t *used)
+{
+ /* Substitutes variables into cp2, based on string cp1, cp2 NO LONGER NEEDS TO BE ZEROED OUT!!!! */
+ char *cp4 = NULL;
+ const char *whereweare, *orig_cp2 = cp2;
+ int length, offset, offset2, isfunction;
+ char *workspace = NULL;
+ char *ltmp = NULL, *var = NULL;
+ char *nextvar, *nextexp, *nextthing;
+ char *vars, *vare;
+ int pos, brackets, needsub, len;
+
+ *cp2 = 0; /* just in case nothing ends up there */
+ whereweare = cp1;
+ while (!ast_strlen_zero(whereweare) && count) {
+ /* Assume we're copying the whole remaining string */
+ pos = strlen(whereweare);
+ nextvar = NULL;
+ nextexp = NULL;
+ nextthing = strchr(whereweare, '$');
+ if (nextthing) {
+ switch (nextthing[1]) {
+ case '{':
+ nextvar = nextthing;
+ pos = nextvar - whereweare;
+ break;
+ case '[':
+ nextexp = nextthing;
+ pos = nextexp - whereweare;
+ break;
+ default:
+ pos = 1;
+ }
+ }
+
+ if (pos) {
+ /* Can't copy more than 'count' bytes */
+ if (pos > count)
+ pos = count;
+
+ /* Copy that many bytes */
+ memcpy(cp2, whereweare, pos);
+
+ count -= pos;
+ cp2 += pos;
+ whereweare += pos;
+ *cp2 = 0;
+ }
+
+ if (nextvar) {
+ /* We have a variable. Find the start and end, and determine
+ if we are going to have to recursively call ourselves on the
+ contents */
+ vars = vare = nextvar + 2;
+ brackets = 1;
+ needsub = 0;
+
+ /* Find the end of it */
+ while (brackets && *vare) {
+ if ((vare[0] == '$') && (vare[1] == '{')) {
+ needsub++;
+ } else if (vare[0] == '{') {
+ brackets++;
+ } else if (vare[0] == '}') {
+ brackets--;
+ } else if ((vare[0] == '$') && (vare[1] == '['))
+ needsub++;
+ vare++;
+ }
+ if (brackets)
+ ast_log(LOG_WARNING, "Error in extension logic (missing '}')\n");
+ len = vare - vars - 1;
+
+ /* Skip totally over variable string */
+ whereweare += (len + 3);
+
+ if (!var)
+ var = ast_alloca(VAR_BUF_SIZE);
+
+ /* Store variable name (and truncate) */
+ ast_copy_string(var, vars, len + 1);
+
+ /* Substitute if necessary */
+ if (needsub) {
+ size_t my_used;
+
+ if (!ltmp) {
+ ltmp = ast_alloca(VAR_BUF_SIZE);
+ }
+ pbx_substitute_variables_helper_full(c, headp, var, ltmp, VAR_BUF_SIZE - 1, &my_used);
+ vars = ltmp;
+ } else {
+ vars = var;
+ }
+
+ if (!workspace)
+ workspace = ast_alloca(VAR_BUF_SIZE);
+
+ workspace[0] = '\0';
+
+ parse_variable_name(vars, &offset, &offset2, &isfunction);
+ if (isfunction) {
+ /* Evaluate function */
+ if (c || !headp)
+ cp4 = ast_func_read(c, vars, workspace, VAR_BUF_SIZE) ? NULL : workspace;
+ else {
+ struct varshead old;
+ struct ast_channel *c = ast_dummy_channel_alloc();
+ if (c) {
+ memcpy(&old, ast_channel_varshead(c), sizeof(old));
+ memcpy(ast_channel_varshead(c), headp, sizeof(*ast_channel_varshead(c)));
+ cp4 = ast_func_read(c, vars, workspace, VAR_BUF_SIZE) ? NULL : workspace;
+ /* Don't deallocate the varshead that was passed in */
+ memcpy(ast_channel_varshead(c), &old, sizeof(*ast_channel_varshead(c)));
+ c = ast_channel_unref(c);
+ } else {
+ ast_log(LOG_ERROR, "Unable to allocate bogus channel for variable substitution. Function results may be blank.\n");
+ }
+ }
+ ast_debug(2, "Function %s result is '%s'\n", vars, cp4 ? cp4 : "(null)");
+ } else {
+ /* Retrieve variable value */
+ pbx_retrieve_variable(c, vars, &cp4, workspace, VAR_BUF_SIZE, headp);
+ }
+ if (cp4) {
+ cp4 = substring(cp4, offset, offset2, workspace, VAR_BUF_SIZE);
+
+ length = strlen(cp4);
+ if (length > count)
+ length = count;
+ memcpy(cp2, cp4, length);
+ count -= length;
+ cp2 += length;
+ *cp2 = 0;
+ }
+ } else if (nextexp) {
+ /* We have an expression. Find the start and end, and determine
+ if we are going to have to recursively call ourselves on the
+ contents */
+ vars = vare = nextexp + 2;
+ brackets = 1;
+ needsub = 0;
+
+ /* Find the end of it */
+ while (brackets && *vare) {
+ if ((vare[0] == '$') && (vare[1] == '[')) {
+ needsub++;
+ brackets++;
+ vare++;
+ } else if (vare[0] == '[') {
+ brackets++;
+ } else if (vare[0] == ']') {
+ brackets--;
+ } else if ((vare[0] == '$') && (vare[1] == '{')) {
+ needsub++;
+ vare++;
+ }
+ vare++;
+ }
+ if (brackets)
+ ast_log(LOG_WARNING, "Error in extension logic (missing ']')\n");
+ len = vare - vars - 1;
+
+ /* Skip totally over expression */
+ whereweare += (len + 3);
+
+ if (!var)
+ var = ast_alloca(VAR_BUF_SIZE);
+
+ /* Store variable name (and truncate) */
+ ast_copy_string(var, vars, len + 1);
+
+ /* Substitute if necessary */
+ if (needsub) {
+ size_t my_used;
+
+ if (!ltmp) {
+ ltmp = ast_alloca(VAR_BUF_SIZE);
+ }
+ pbx_substitute_variables_helper_full(c, headp, var, ltmp, VAR_BUF_SIZE - 1, &my_used);
+ vars = ltmp;
+ } else {
+ vars = var;
+ }
+
+ length = ast_expr(vars, cp2, count, c);
+
+ if (length) {
+ ast_debug(1, "Expression result is '%s'\n", cp2);
+ count -= length;
+ cp2 += length;
+ *cp2 = 0;
+ }
+ }
+ }
+ *used = cp2 - orig_cp2;
+}
+
+void pbx_substitute_variables_helper(struct ast_channel *c, const char *cp1, char *cp2, int count)
+{
+ size_t used;
+ pbx_substitute_variables_helper_full(c, (c) ? ast_channel_varshead(c) : NULL, cp1, cp2, count, &used);
+}
+
+void pbx_substitute_variables_varshead(struct varshead *headp, const char *cp1, char *cp2, int count)
+{
+ size_t used;
+ pbx_substitute_variables_helper_full(NULL, headp, cp1, cp2, count, &used);
+}
+
+/*! \brief CLI support for listing global variables in a parseable way */
+static char *handle_show_globals(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int i = 0;
+ struct ast_var_t *newvariable;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "dialplan show globals";
+ e->usage =
+ "Usage: dialplan show globals\n"
+ " List current global dialplan variables and their values\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ ast_rwlock_rdlock(&globalslock);
+ AST_LIST_TRAVERSE (&globals, newvariable, entries) {
+ i++;
+ ast_cli(a->fd, " %s=%s\n", ast_var_name(newvariable), ast_var_value(newvariable));
+ }
+ ast_rwlock_unlock(&globalslock);
+ ast_cli(a->fd, "\n -- %d variable(s)\n", i);
+
+ return CLI_SUCCESS;
+}
+
+/*! \brief CLI support for listing chanvar's variables in a parseable way */
+static char *handle_show_chanvar(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct ast_channel *chan;
+ struct ast_var_t *var;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "dialplan show chanvar";
+ e->usage =
+ "Usage: dialplan show chanvar <channel>\n"
+ " List current channel variables and their values\n";
+ return NULL;
+ case CLI_GENERATE:
+ return ast_complete_channels(a->line, a->word, a->pos, a->n, 3);
+ }
+
+ if (a->argc != e->args + 1) {
+ return CLI_SHOWUSAGE;
+ }
+
+ chan = ast_channel_get_by_name(a->argv[e->args]);
+ if (!chan) {
+ ast_cli(a->fd, "Channel '%s' not found\n", a->argv[e->args]);
+ return CLI_FAILURE;
+ }
+
+ ast_channel_lock(chan);
+ AST_LIST_TRAVERSE(ast_channel_varshead(chan), var, entries) {
+ ast_cli(a->fd, "%s=%s\n", ast_var_name(var), ast_var_value(var));
+ }
+ ast_channel_unlock(chan);
+
+ ast_channel_unref(chan);
+ return CLI_SUCCESS;
+}
+
+static char *handle_set_global(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "dialplan set global";
+ e->usage =
+ "Usage: dialplan set global <name> <value>\n"
+ " Set global dialplan variable <name> to <value>\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != e->args + 2)
+ return CLI_SHOWUSAGE;
+
+ pbx_builtin_setvar_helper(NULL, a->argv[3], a->argv[4]);
+ ast_cli(a->fd, "\n -- Global variable '%s' set to '%s'\n", a->argv[3], a->argv[4]);
+
+ return CLI_SUCCESS;
+}
+
+static char *handle_set_chanvar(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct ast_channel *chan;
+ const char *chan_name, *var_name, *var_value;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "dialplan set chanvar";
+ e->usage =
+ "Usage: dialplan set chanvar <channel> <varname> <value>\n"
+ " Set channel variable <varname> to <value>\n";
+ return NULL;
+ case CLI_GENERATE:
+ return ast_complete_channels(a->line, a->word, a->pos, a->n, 3);
+ }
+
+ if (a->argc != e->args + 3)
+ return CLI_SHOWUSAGE;
+
+ chan_name = a->argv[e->args];
+ var_name = a->argv[e->args + 1];
+ var_value = a->argv[e->args + 2];
+
+ if (!(chan = ast_channel_get_by_name(chan_name))) {
+ ast_cli(a->fd, "Channel '%s' not found\n", chan_name);
+ return CLI_FAILURE;
+ }
+
+ pbx_builtin_setvar_helper(chan, var_name, var_value);
+
+ chan = ast_channel_unref(chan);
+
+ ast_cli(a->fd, "\n -- Channel variable '%s' set to '%s' for '%s'\n", var_name, var_value, chan_name);
+
+ return CLI_SUCCESS;
+}
+
+int pbx_builtin_serialize_variables(struct ast_channel *chan, struct ast_str **buf)
+{
+ struct ast_var_t *variables;
+ const char *var, *val;
+ int total = 0;
+
+ if (!chan)
+ return 0;
+
+ ast_str_reset(*buf);
+
+ ast_channel_lock(chan);
+
+ AST_LIST_TRAVERSE(ast_channel_varshead(chan), variables, entries) {
+ if ((var = ast_var_name(variables)) && (val = ast_var_value(variables))
+ /* && !ast_strlen_zero(var) && !ast_strlen_zero(val) */
+ ) {
+ if (ast_str_append(buf, 0, "%s=%s\n", var, val) < 0) {
+ ast_log(LOG_ERROR, "Data Buffer Size Exceeded!\n");
+ break;
+ } else
+ total++;
+ } else
+ break;
+ }
+
+ ast_channel_unlock(chan);
+
+ return total;
+}
+
+const char *pbx_builtin_getvar_helper(struct ast_channel *chan, const char *name)
+{
+ struct ast_var_t *variables;
+ const char *ret = NULL;
+ int i;
+ struct varshead *places[2] = { NULL, &globals };
+
+ if (!name)
+ return NULL;
+
+ if (chan) {
+ ast_channel_lock(chan);
+ places[0] = ast_channel_varshead(chan);
+ }
+
+ for (i = 0; i < 2; i++) {
+ if (!places[i])
+ continue;
+ if (places[i] == &globals)
+ ast_rwlock_rdlock(&globalslock);
+ AST_LIST_TRAVERSE(places[i], variables, entries) {
+ if (!strcmp(name, ast_var_name(variables))) {
+ ret = ast_var_value(variables);
+ break;
+ }
+ }
+ if (places[i] == &globals)
+ ast_rwlock_unlock(&globalslock);
+ if (ret)
+ break;
+ }
+
+ if (chan)
+ ast_channel_unlock(chan);
+
+ return ret;
+}
+
+void pbx_builtin_pushvar_helper(struct ast_channel *chan, const char *name, const char *value)
+{
+ struct ast_var_t *newvariable;
+ struct varshead *headp;
+
+ if (name[strlen(name)-1] == ')') {
+ char *function = ast_strdupa(name);
+
+ ast_log(LOG_WARNING, "Cannot push a value onto a function\n");
+ ast_func_write(chan, function, value);
+ return;
+ }
+
+ if (chan) {
+ ast_channel_lock(chan);
+ headp = ast_channel_varshead(chan);
+ } else {
+ ast_rwlock_wrlock(&globalslock);
+ headp = &globals;
+ }
+
+ if (value && (newvariable = ast_var_assign(name, value))) {
+ if (headp == &globals)
+ ast_verb(2, "Setting global variable '%s' to '%s'\n", name, value);
+ AST_LIST_INSERT_HEAD(headp, newvariable, entries);
+ }
+
+ if (chan)
+ ast_channel_unlock(chan);
+ else
+ ast_rwlock_unlock(&globalslock);
+}
+
+int pbx_builtin_setvar_helper(struct ast_channel *chan, const char *name, const char *value)
+{
+ struct ast_var_t *newvariable;
+ struct varshead *headp;
+ const char *nametail = name;
+ /*! True if the old value was not an empty string. */
+ int old_value_existed = 0;
+
+ if (name[strlen(name) - 1] == ')') {
+ char *function = ast_strdupa(name);
+
+ return ast_func_write(chan, function, value);
+ }
+
+ if (chan) {
+ ast_channel_lock(chan);
+ headp = ast_channel_varshead(chan);
+ } else {
+ ast_rwlock_wrlock(&globalslock);
+ headp = &globals;
+ }
+
+ /* For comparison purposes, we have to strip leading underscores */
+ if (*nametail == '_') {
+ nametail++;
+ if (*nametail == '_')
+ nametail++;
+ }
+
+ AST_LIST_TRAVERSE_SAFE_BEGIN(headp, newvariable, entries) {
+ if (strcmp(ast_var_name(newvariable), nametail) == 0) {
+ /* there is already such a variable, delete it */
+ AST_LIST_REMOVE_CURRENT(entries);
+ old_value_existed = !ast_strlen_zero(ast_var_value(newvariable));
+ ast_var_delete(newvariable);
+ break;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+
+ if (value && (newvariable = ast_var_assign(name, value))) {
+ if (headp == &globals) {
+ ast_verb(2, "Setting global variable '%s' to '%s'\n", name, value);
+ }
+ AST_LIST_INSERT_HEAD(headp, newvariable, entries);
+ ast_channel_publish_varset(chan, name, value);
+ } else if (old_value_existed) {
+ /* We just deleted a non-empty dialplan variable. */
+ ast_channel_publish_varset(chan, name, "");
+ }
+
+ if (chan)
+ ast_channel_unlock(chan);
+ else
+ ast_rwlock_unlock(&globalslock);
+ return 0;
+}
+
+int pbx_builtin_setvar(struct ast_channel *chan, const char *data)
+{
+ char *name, *value, *mydata;
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "Set requires one variable name/value pair.\n");
+ return 0;
+ }
+
+ mydata = ast_strdupa(data);
+ name = strsep(&mydata, "=");
+ value = mydata;
+ if (!value) {
+ ast_log(LOG_WARNING, "Set requires an '=' to be a valid assignment.\n");
+ return 0;
+ }
+
+ if (strchr(name, ' ')) {
+ ast_log(LOG_WARNING, "Please avoid unnecessary spaces on variables as it may lead to unexpected results ('%s' set to '%s').\n", name, mydata);
+ }
+
+ pbx_builtin_setvar_helper(chan, name, value);
+
+ return 0;
+}
+
+int pbx_builtin_setvar_multiple(struct ast_channel *chan, const char *vdata)
+{
+ char *data;
+ int x;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(pair)[24];
+ );
+ AST_DECLARE_APP_ARGS(pair,
+ AST_APP_ARG(name);
+ AST_APP_ARG(value);
+ );
+
+ if (ast_strlen_zero(vdata)) {
+ ast_log(LOG_WARNING, "MSet requires at least one variable name/value pair.\n");
+ return 0;
+ }
+
+ data = ast_strdupa(vdata);
+ AST_STANDARD_APP_ARGS(args, data);
+
+ for (x = 0; x < args.argc; x++) {
+ AST_NONSTANDARD_APP_ARGS(pair, args.pair[x], '=');
+ if (pair.argc == 2) {
+ pbx_builtin_setvar_helper(chan, pair.name, pair.value);
+ if (strchr(pair.name, ' '))
+ ast_log(LOG_WARNING, "Please avoid unnecessary spaces on variables as it may lead to unexpected results ('%s' set to '%s').\n", pair.name, pair.value);
+ } else if (!chan) {
+ ast_log(LOG_WARNING, "MSet: ignoring entry '%s' with no '='\n", pair.name);
+ } else {
+ ast_log(LOG_WARNING, "MSet: ignoring entry '%s' with no '=' (in %s@%s:%d\n", pair.name, ast_channel_exten(chan), ast_channel_context(chan), ast_channel_priority(chan));
+ }
+ }
+
+ return 0;
+}
+
+void pbx_builtin_clear_globals(void)
+{
+ struct ast_var_t *vardata;
+
+ ast_rwlock_wrlock(&globalslock);
+ while ((vardata = AST_LIST_REMOVE_HEAD(&globals, entries)))
+ ast_var_delete(vardata);
+ ast_rwlock_unlock(&globalslock);
+}
+
+static struct ast_cli_entry vars_cli[] = {
+ AST_CLI_DEFINE(handle_show_globals, "Show global dialplan variables"),
+ AST_CLI_DEFINE(handle_show_chanvar, "Show channel variables"),
+ AST_CLI_DEFINE(handle_set_global, "Set global dialplan variable"),
+ AST_CLI_DEFINE(handle_set_chanvar, "Set a channel variable"),
+};
+
+static void unload_pbx_variables(void)
+{
+ ast_cli_unregister_multiple(vars_cli, ARRAY_LEN(vars_cli));
+ ast_unregister_application("Set");
+ ast_unregister_application("MSet");
+ pbx_builtin_clear_globals();
+}
+
+int load_pbx_variables(void)
+{
+ int res = 0;
+
+ res |= ast_cli_register_multiple(vars_cli, ARRAY_LEN(vars_cli));
+ res |= ast_register_application2("Set", pbx_builtin_setvar, NULL, NULL, NULL);
+ res |= ast_register_application2("MSet", pbx_builtin_setvar_multiple, NULL, NULL, NULL);
+ ast_register_cleanup(unload_pbx_variables);
+
+ return res;
+}
diff --git a/main/stasis_cache_pattern.c b/main/stasis_cache_pattern.c
index ccc9ebf08..7ccf1c181 100644
--- a/main/stasis_cache_pattern.c
+++ b/main/stasis_cache_pattern.c
@@ -138,7 +138,7 @@ struct stasis_cp_single *stasis_cp_single_create(struct stasis_cp_all *all,
{
RAII_VAR(struct stasis_cp_single *, one, NULL, ao2_cleanup);
- one = stasis_cp_single_create_only(all, name);
+ one = stasis_cp_sink_create(all, name);
if (!one) {
return NULL;
}
@@ -157,7 +157,7 @@ struct stasis_cp_single *stasis_cp_single_create(struct stasis_cp_all *all,
return one;
}
-struct stasis_cp_single *stasis_cp_single_create_only(struct stasis_cp_all *all,
+struct stasis_cp_single *stasis_cp_sink_create(struct stasis_cp_all *all,
const char *name)
{
RAII_VAR(struct stasis_cp_single *, one, NULL, ao2_cleanup);
@@ -180,23 +180,6 @@ struct stasis_cp_single *stasis_cp_single_create_only(struct stasis_cp_all *all,
return one;
}
-int stasis_cp_single_forward(struct stasis_cp_single *from, struct stasis_cp_single *to)
-{
- from->forward_topic_to_all = stasis_forward_all(from->topic, to->topic);
- if (!from->forward_topic_to_all) {
- return -1;;
- }
-
- from->forward_cached_to_all = stasis_forward_all(
- stasis_caching_get_topic(from->topic_cached),
- stasis_caching_get_topic(to->topic_cached));
- if (!from->forward_cached_to_all) {
- return -1;
- }
-
- return 0;
-}
-
void stasis_cp_single_unsubscribe(struct stasis_cp_single *one)
{
if (!one) {
diff --git a/res/res_mwi_external.c b/res/res_mwi_external.c
index e5d8a3d32..9ded0d959 100644
--- a/res/res_mwi_external.c
+++ b/res/res_mwi_external.c
@@ -33,7 +33,6 @@
/*** MODULEINFO
<defaultenabled>no</defaultenabled>
- <conflict>app_voicemail</conflict>
<support_level>core</support_level>
***/
@@ -935,12 +934,22 @@ static int unload_module(void)
static int load_module(void)
{
+ int res;
+
if (mwi_sorcery_init()
|| ast_sorcery_observer_add(mwi_sorcery, MWI_MAILBOX_TYPE, &mwi_observers)
#if defined(MWI_DEBUG_CLI)
|| ast_cli_register_multiple(mwi_cli, ARRAY_LEN(mwi_cli))
#endif /* defined(MWI_DEBUG_CLI) */
- || ast_vm_register(&vm_table)) {
+ ) {
+ unload_module();
+ return AST_MODULE_LOAD_DECLINE;
+ }
+
+ /* ast_vm_register may return DECLINE if another module registered for vm */
+ res = ast_vm_register(&vm_table);
+ if (res) {
+ ast_log(LOG_ERROR, "Failure registering as a voicemail provider\n");
unload_module();
return AST_MODULE_LOAD_DECLINE;
}