aboutsummaryrefslogtreecommitdiffstats
path: root/trunk/funcs
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/funcs')
-rw-r--r--trunk/funcs/Makefile20
-rw-r--r--trunk/funcs/func_base64.c87
-rw-r--r--trunk/funcs/func_blacklist.c74
-rw-r--r--trunk/funcs/func_callerid.c233
-rw-r--r--trunk/funcs/func_cdr.c162
-rw-r--r--trunk/funcs/func_channel.c206
-rw-r--r--trunk/funcs/func_curl.c204
-rw-r--r--trunk/funcs/func_cut.c300
-rw-r--r--trunk/funcs/func_db.c225
-rw-r--r--trunk/funcs/func_devstate.c255
-rw-r--r--trunk/funcs/func_dialgroup.c220
-rw-r--r--trunk/funcs/func_dialplan.c106
-rw-r--r--trunk/funcs/func_enum.c391
-rw-r--r--trunk/funcs/func_env.c215
-rw-r--r--trunk/funcs/func_extstate.c133
-rw-r--r--trunk/funcs/func_global.c82
-rw-r--r--trunk/funcs/func_groupcount.c231
-rw-r--r--trunk/funcs/func_iconv.c125
-rw-r--r--trunk/funcs/func_lock.c350
-rw-r--r--trunk/funcs/func_logic.c242
-rw-r--r--trunk/funcs/func_math.c333
-rw-r--r--trunk/funcs/func_md5.c67
-rw-r--r--trunk/funcs/func_module.c68
-rw-r--r--trunk/funcs/func_odbc.c900
-rw-r--r--trunk/funcs/func_rand.c93
-rw-r--r--trunk/funcs/func_realtime.c154
-rw-r--r--trunk/funcs/func_sha1.c76
-rw-r--r--trunk/funcs/func_shell.c92
-rw-r--r--trunk/funcs/func_strings.c886
-rw-r--r--trunk/funcs/func_sysinfo.c116
-rw-r--r--trunk/funcs/func_timeout.c184
-rw-r--r--trunk/funcs/func_uri.c96
-rw-r--r--trunk/funcs/func_version.c101
-rw-r--r--trunk/funcs/func_vmcount.c93
-rw-r--r--trunk/funcs/func_volume.c160
35 files changed, 7280 insertions, 0 deletions
diff --git a/trunk/funcs/Makefile b/trunk/funcs/Makefile
new file mode 100644
index 000000000..9b13b17a0
--- /dev/null
+++ b/trunk/funcs/Makefile
@@ -0,0 +1,20 @@
+#
+# Asterisk -- A telephony toolkit for Linux.
+#
+# Makefile for dialplan functions
+#
+# Copyright (C) 2005-2006, Digium, Inc.
+#
+# This program is free software, distributed under the terms of
+# the GNU General Public License
+#
+
+-include $(ASTTOPDIR)/menuselect.makeopts $(ASTTOPDIR)/menuselect.makedeps
+
+MODULE_PREFIX=func
+MENUSELECT_CATEGORY=FUNCS
+MENUSELECT_DESCRIPTION=Dialplan Functions
+
+all: _all
+
+include $(ASTTOPDIR)/Makefile.moddir_rules
diff --git a/trunk/funcs/func_base64.c b/trunk/funcs/func_base64.c
new file mode 100644
index 000000000..0849e9539
--- /dev/null
+++ b/trunk/funcs/func_base64.c
@@ -0,0 +1,87 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2005 - 2006, Digium, Inc.
+ * Copyright (C) 2005, Claude Patry
+ *
+ * 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 Use the base64 as functions
+ *
+ * \ingroup functions
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/module.h"
+#include "asterisk/pbx.h" /* function register/unregister */
+#include "asterisk/utils.h"
+
+static int base64_encode(struct ast_channel *chan, const char *cmd, char *data,
+ char *buf, size_t len)
+{
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "Syntax: BASE64_ENCODE(<data>) - missing argument!\n");
+ return -1;
+ }
+
+ ast_base64encode(buf, (unsigned char *) data, strlen(data), len);
+
+ return 0;
+}
+
+static int base64_decode(struct ast_channel *chan, const char *cmd, char *data,
+ char *buf, size_t len)
+{
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "Syntax: BASE64_DECODE(<base_64 string>) - missing argument!\n");
+ return -1;
+ }
+
+ ast_base64decode((unsigned char *) buf, data, len);
+
+ return 0;
+}
+
+static struct ast_custom_function base64_encode_function = {
+ .name = "BASE64_ENCODE",
+ .synopsis = "Encode a string in base64",
+ .desc = "Returns the base64 string\n",
+ .syntax = "BASE64_ENCODE(<string>)",
+ .read = base64_encode,
+};
+
+static struct ast_custom_function base64_decode_function = {
+ .name = "BASE64_DECODE",
+ .synopsis = "Decode a base64 string",
+ .desc = "Returns the plain text string\n",
+ .syntax = "BASE64_DECODE(<base64_string>)",
+ .read = base64_decode,
+};
+
+static int unload_module(void)
+{
+ return ast_custom_function_unregister(&base64_encode_function) |
+ ast_custom_function_unregister(&base64_decode_function);
+}
+
+static int load_module(void)
+{
+ return ast_custom_function_register(&base64_encode_function) |
+ ast_custom_function_register(&base64_decode_function);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "base64 encode/decode dialplan functions");
diff --git a/trunk/funcs/func_blacklist.c b/trunk/funcs/func_blacklist.c
new file mode 100644
index 000000000..2a2fa78c7
--- /dev/null
+++ b/trunk/funcs/func_blacklist.c
@@ -0,0 +1,74 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.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 Function to lookup the callerid number, and see if it is blacklisted
+ *
+ * \author Mark Spencer <markster@digium.com>
+ *
+ * \ingroup functions
+ *
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/channel.h"
+#include "asterisk/astdb.h"
+
+static int blacklist_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
+{
+ char blacklist[1];
+ int bl = 0;
+
+ if (chan->cid.cid_num) {
+ if (!ast_db_get("blacklist", chan->cid.cid_num, blacklist, sizeof (blacklist)))
+ bl = 1;
+ }
+ if (chan->cid.cid_name) {
+ if (!ast_db_get("blacklist", chan->cid.cid_name, blacklist, sizeof (blacklist)))
+ bl = 1;
+ }
+
+ snprintf(buf, len, "%d", bl);
+ return 0;
+}
+
+static struct ast_custom_function blacklist_function = {
+ .name = "BLACKLIST",
+ .synopsis = "Check if the callerid is on the blacklist",
+ .desc = "Uses astdb to check if the Caller*ID is in family 'blacklist'. Returns 1 or 0.\n",
+ .syntax = "BLACKLIST()",
+ .read = blacklist_read,
+};
+
+static int unload_module(void)
+{
+ return ast_custom_function_unregister(&blacklist_function);
+}
+
+static int load_module(void)
+{
+ return ast_custom_function_register(&blacklist_function);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Look up Caller*ID name/number from blacklist database");
diff --git a/trunk/funcs/func_callerid.c b/trunk/funcs/func_callerid.c
new file mode 100644
index 000000000..9dfe0d8c7
--- /dev/null
+++ b/trunk/funcs/func_callerid.c
@@ -0,0 +1,233 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999-2006, Digium, Inc.
+ *
+ * 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 Caller ID related dialplan functions
+ *
+ * \ingroup functions
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/module.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/utils.h"
+#include "asterisk/app.h"
+#include "asterisk/callerid.h"
+
+static int callerpres_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
+{
+ ast_copy_string(buf, ast_named_caller_presentation(chan->cid.cid_pres), len);
+ return 0;
+}
+
+static int callerpres_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
+{
+ int pres = ast_parse_caller_presentation(value);
+ if (pres < 0)
+ ast_log(LOG_WARNING, "'%s' is not a valid presentation (see 'show function CALLERPRES')\n", value);
+ else
+ chan->cid.cid_pres = pres;
+ return 0;
+}
+
+static int callerid_read(struct ast_channel *chan, const char *cmd, char *data,
+ char *buf, size_t len)
+{
+ char *opt = data;
+
+ if (!chan)
+ return -1;
+
+ if (strchr(opt, ',')) {
+ char name[80], num[80];
+
+ data = strsep(&opt, ",");
+ ast_callerid_split(opt, name, sizeof(name), num, sizeof(num));
+
+ if (!strncasecmp("all", data, 3)) {
+ snprintf(buf, len, "\"%s\" <%s>", name, num);
+ } else if (!strncasecmp("name", data, 4)) {
+ ast_copy_string(buf, name, len);
+ } else if (!strncasecmp("num", data, 3)) {
+ ast_copy_string(buf, num, len);
+ } else {
+ ast_log(LOG_ERROR, "Unknown callerid data type '%s'.\n", data);
+ }
+ } else {
+ ast_channel_lock(chan);
+
+ if (!strncasecmp("all", data, 3)) {
+ snprintf(buf, len, "\"%s\" <%s>",
+ S_OR(chan->cid.cid_name, ""),
+ S_OR(chan->cid.cid_num, ""));
+ } else if (!strncasecmp("name", data, 4)) {
+ if (chan->cid.cid_name) {
+ ast_copy_string(buf, chan->cid.cid_name, len);
+ }
+ } else if (!strncasecmp("num", data, 3)) {
+ if (chan->cid.cid_num) {
+ ast_copy_string(buf, chan->cid.cid_num, len);
+ }
+ } else if (!strncasecmp("ani", data, 3)) {
+ if (!strncasecmp(data + 3, "2", 1)) {
+ snprintf(buf, len, "%d", chan->cid.cid_ani2);
+ } else if (chan->cid.cid_ani) {
+ ast_copy_string(buf, chan->cid.cid_ani, len);
+ }
+ } else if (!strncasecmp("dnid", data, 4)) {
+ if (chan->cid.cid_dnid) {
+ ast_copy_string(buf, chan->cid.cid_dnid, len);
+ }
+ } else if (!strncasecmp("rdnis", data, 5)) {
+ if (chan->cid.cid_rdnis) {
+ ast_copy_string(buf, chan->cid.cid_rdnis, len);
+ }
+ } else if (!strncasecmp("pres", data, 4)) {
+ ast_copy_string(buf, ast_named_caller_presentation(chan->cid.cid_pres), len);
+ } else if (!strncasecmp("ton", data, 3)) {
+ snprintf(buf, len, "%d", chan->cid.cid_ton);
+ } else {
+ ast_log(LOG_ERROR, "Unknown callerid data type '%s'.\n", data);
+ }
+
+ ast_channel_unlock(chan);
+ }
+
+ return 0;
+}
+
+static int callerid_write(struct ast_channel *chan, const char *cmd, char *data,
+ const char *value)
+{
+ if (!value || !chan)
+ return -1;
+
+ if (!strncasecmp("all", data, 3)) {
+ char name[256];
+ char num[256];
+
+ if (!ast_callerid_split(value, name, sizeof(name), num, sizeof(num)))
+ ast_set_callerid(chan, num, name, num);
+ } else if (!strncasecmp("name", data, 4)) {
+ ast_set_callerid(chan, NULL, value, NULL);
+ } else if (!strncasecmp("num", data, 3)) {
+ ast_set_callerid(chan, value, NULL, NULL);
+ } else if (!strncasecmp("ani", data, 3)) {
+ if (!strncasecmp(data + 3, "2", 1)) {
+ int i = atoi(value);
+ chan->cid.cid_ani2 = i;
+ } else
+ ast_set_callerid(chan, NULL, NULL, value);
+ } else if (!strncasecmp("dnid", data, 4)) {
+ ast_channel_lock(chan);
+ if (chan->cid.cid_dnid)
+ ast_free(chan->cid.cid_dnid);
+ chan->cid.cid_dnid = ast_strdup(value);
+ ast_channel_unlock(chan);
+ } else if (!strncasecmp("rdnis", data, 5)) {
+ ast_channel_lock(chan);
+ if (chan->cid.cid_rdnis)
+ ast_free(chan->cid.cid_rdnis);
+ chan->cid.cid_rdnis = ast_strdup(value);
+ ast_channel_unlock(chan);
+ } else if (!strncasecmp("pres", data, 4)) {
+ int i;
+ char *s, *val;
+
+ /* Strip leading spaces */
+ while ((value[0] == '\t') || (value[0] == ' '))
+ ++value;
+
+ val = ast_strdupa(value);
+
+ /* Strip trailing spaces */
+ s = val + strlen(val);
+ while ((s != val) && ((s[-1] == '\t') || (s[-1] == ' ')))
+ --s;
+ *s = '\0';
+
+ if ((val[0] >= '0') && (val[0] <= '9'))
+ i = atoi(val);
+ else
+ i = ast_parse_caller_presentation(val);
+
+ if (i < 0)
+ ast_log(LOG_ERROR, "Unknown calling number presentation '%s', value unchanged\n", val);
+ else
+ chan->cid.cid_pres = i;
+ } else if (!strncasecmp("ton", data, 3)) {
+ int i = atoi(value);
+ chan->cid.cid_ton = i;
+ } else {
+ ast_log(LOG_ERROR, "Unknown callerid data type '%s'.\n", data);
+ }
+
+ return 0;
+}
+
+static struct ast_custom_function callerid_function = {
+ .name = "CALLERID",
+ .synopsis = "Gets or sets Caller*ID data on the channel.",
+ .syntax = "CALLERID(datatype[,<optional-CID>])",
+ .desc =
+ "Gets or sets Caller*ID data on the channel. The allowable datatypes\n"
+ "are \"all\", \"name\", \"num\", \"ANI\", \"DNID\", \"RDNIS\", \"pres\",\n"
+ "and \"ton\".\n"
+ "Uses channel callerid by default or optional callerid, if specified.\n",
+ .read = callerid_read,
+ .write = callerid_write,
+};
+
+static struct ast_custom_function callerpres_function = {
+ .name = "CALLERPRES",
+ .synopsis = "Gets or sets Caller*ID presentation on the channel.",
+ .syntax = "CALLERPRES()",
+ .desc =
+"Gets or sets Caller*ID presentation on the channel. The following values\n"
+"are valid:\n"
+" allowed_not_screened : Presentation Allowed, Not Screened\n"
+" allowed_passed_screen : Presentation Allowed, Passed Screen\n"
+" allowed_failed_screen : Presentation Allowed, Failed Screen\n"
+" allowed : Presentation Allowed, Network Number\n"
+" prohib_not_screened : Presentation Prohibited, Not Screened\n"
+" prohib_passed_screen : Presentation Prohibited, Passed Screen\n"
+" prohib_failed_screen : Presentation Prohibited, Failed Screen\n"
+" prohib : Presentation Prohibited, Network Number\n"
+" unavailable : Number Unavailable\n",
+ .read = callerpres_read,
+ .write = callerpres_write,
+};
+
+static int unload_module(void)
+{
+ int res = ast_custom_function_unregister(&callerpres_function);
+ res |= ast_custom_function_unregister(&callerid_function);
+ return res;
+}
+
+static int load_module(void)
+{
+ int res = ast_custom_function_register(&callerpres_function);
+ res |= ast_custom_function_register(&callerid_function);
+ return res;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Caller ID related dialplan functions");
diff --git a/trunk/funcs/func_cdr.c b/trunk/funcs/func_cdr.c
new file mode 100644
index 000000000..63d49fd42
--- /dev/null
+++ b/trunk/funcs/func_cdr.c
@@ -0,0 +1,162 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999-2006, Digium, Inc.
+ *
+ * Portions Copyright (C) 2005, Anthony Minessale II
+ *
+ * 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 Call Detail Record related dialplan functions
+ *
+ * \author Anthony Minessale II
+ *
+ * \ingroup functions
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/module.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/utils.h"
+#include "asterisk/app.h"
+#include "asterisk/cdr.h"
+
+enum {
+ OPT_RECURSIVE = (1 << 0),
+ OPT_UNPARSED = (1 << 1),
+ OPT_LAST = (1 << 2),
+} cdr_option_flags;
+
+AST_APP_OPTIONS(cdr_func_options, {
+ AST_APP_OPTION('l', OPT_LAST),
+ AST_APP_OPTION('r', OPT_RECURSIVE),
+ AST_APP_OPTION('u', OPT_UNPARSED),
+});
+
+static int cdr_read(struct ast_channel *chan, const char *cmd, char *parse,
+ char *buf, size_t len)
+{
+ char *ret;
+ struct ast_flags flags = { 0 };
+ struct ast_cdr *cdr = chan ? chan->cdr : NULL;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(variable);
+ AST_APP_ARG(options);
+ );
+
+ if (ast_strlen_zero(parse))
+ return -1;
+
+ if (!cdr)
+ return -1;
+
+ AST_STANDARD_APP_ARGS(args, parse);
+
+ if (!ast_strlen_zero(args.options))
+ ast_app_parse_options(cdr_func_options, &flags, NULL, args.options);
+
+ if (ast_test_flag(&flags, OPT_LAST))
+ while (cdr->next)
+ cdr = cdr->next;
+
+ ast_cdr_getvar(cdr, args.variable, &ret, buf, len,
+ ast_test_flag(&flags, OPT_RECURSIVE),
+ ast_test_flag(&flags, OPT_UNPARSED));
+
+ return 0;
+}
+
+static int cdr_write(struct ast_channel *chan, const char *cmd, char *parse,
+ const char *value)
+{
+ struct ast_flags flags = { 0 };
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(variable);
+ AST_APP_ARG(options);
+ );
+
+ if (ast_strlen_zero(parse) || !value || !chan)
+ return -1;
+
+ AST_STANDARD_APP_ARGS(args, parse);
+
+ if (!ast_strlen_zero(args.options))
+ ast_app_parse_options(cdr_func_options, &flags, NULL, args.options);
+
+ if (!strcasecmp(args.variable, "accountcode"))
+ ast_cdr_setaccount(chan, value);
+ else if (!strcasecmp(args.variable, "userfield"))
+ ast_cdr_setuserfield(chan, value);
+ else if (!strcasecmp(args.variable, "amaflags"))
+ ast_cdr_setamaflags(chan, value);
+ else if (chan->cdr)
+ ast_cdr_setvar(chan->cdr, args.variable, value, ast_test_flag(&flags, OPT_RECURSIVE));
+ /* No need to worry about the u flag, as all fields for which setting
+ * 'u' would do anything are marked as readonly. */
+
+ return 0;
+}
+
+static struct ast_custom_function cdr_function = {
+ .name = "CDR",
+ .synopsis = "Gets or sets a CDR variable",
+ .syntax = "CDR(<name>[,options])",
+ .read = cdr_read,
+ .write = cdr_write,
+ .desc =
+"Options:\n"
+" 'r' searches the entire stack of CDRs on the channel\n"
+" 'u' retrieves the raw, unprocessed value\n"
+" For example, 'start', 'answer', and 'end' will be retrieved as epoch\n"
+" values, when the 'u' option is passed, but formatted as YYYY-MM-DD HH:MM:SS\n"
+" otherwise. Similarly, disposition and amaflags will return their raw\n"
+" integral values.\n"
+" Here is a list of all the available cdr field names:\n"
+" clid lastdata disposition\n"
+" src start amaflags\n"
+" dst answer accountcode\n"
+" dcontext end uniqueid\n"
+" dstchannel duration userfield\n"
+" lastapp billsec channel\n"
+" All of the above variables are read-only, except for accountcode,\n"
+" userfield, and amaflags. You may, however, supply\n"
+" a name not on the above list, and create your own\n"
+" variable, whose value can be changed with this function,\n"
+" and this variable will be stored on the cdr.\n"
+" raw values for disposition:\n"
+" 1 = NO ANSWER\n"
+" 2 = BUSY\n"
+" 3 = FAILED\n"
+" 4 = ANSWERED\n"
+" raw values for amaflags:\n"
+" 1 = OMIT\n"
+" 2 = BILLING\n"
+" 3 = DOCUMENTATION\n",
+};
+
+static int unload_module(void)
+{
+ return ast_custom_function_unregister(&cdr_function);
+}
+
+static int load_module(void)
+{
+ return ast_custom_function_register(&cdr_function);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Call Detail Record (CDR) dialplan function");
diff --git a/trunk/funcs/func_channel.c b/trunk/funcs/func_channel.c
new file mode 100644
index 000000000..d920ac21f
--- /dev/null
+++ b/trunk/funcs/func_channel.c
@@ -0,0 +1,206 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2006, Digium, Inc.
+ *
+ * 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 Channel info dialplan function
+ *
+ * \author Kevin P. Fleming <kpfleming@digium.com>
+ *
+ * \ingroup functions
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/module.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/utils.h"
+#include "asterisk/app.h"
+#include "asterisk/indications.h"
+#include "asterisk/stringfields.h"
+
+#define locked_copy_string(chan, dest, source, len) \
+ do { \
+ ast_channel_lock(chan); \
+ ast_copy_string(dest, source, len); \
+ ast_channel_unlock(chan); \
+ } while (0)
+#define locked_string_field_set(chan, field, source) \
+ do { \
+ ast_channel_lock(chan); \
+ ast_string_field_set(chan, field, source); \
+ ast_channel_unlock(chan); \
+ } while (0)
+
+char *transfercapability_table[0x20] = {
+ "SPEECH", "UNK", "UNK", "UNK", "UNK", "UNK", "UNK", "UNK",
+ "DIGITAL", "RESTRICTED_DIGITAL", "UNK", "UNK", "UNK", "UNK", "UNK", "UNK",
+ "3K1AUDIO", "DIGITAL_W_TONES", "UNK", "UNK", "UNK", "UNK", "UNK", "UNK",
+ "VIDEO", "UNK", "UNK", "UNK", "UNK", "UNK", "UNK", "UNK", };
+
+static int func_channel_read(struct ast_channel *chan, const char *function,
+ char *data, char *buf, size_t len)
+{
+ int ret = 0;
+
+ if (!strcasecmp(data, "audionativeformat"))
+ /* use the _multiple version when chan->nativeformats holds multiple formats */
+ /* ast_getformatname_multiple(buf, len, chan->nativeformats & AST_FORMAT_AUDIO_MASK); */
+ ast_copy_string(buf, ast_getformatname(chan->nativeformats & AST_FORMAT_AUDIO_MASK), len);
+ else if (!strcasecmp(data, "videonativeformat"))
+ /* use the _multiple version when chan->nativeformats holds multiple formats */
+ /* ast_getformatname_multiple(buf, len, chan->nativeformats & AST_FORMAT_VIDEO_MASK); */
+ ast_copy_string(buf, ast_getformatname(chan->nativeformats & AST_FORMAT_VIDEO_MASK), len);
+ else if (!strcasecmp(data, "audioreadformat"))
+ ast_copy_string(buf, ast_getformatname(chan->readformat), len);
+ else if (!strcasecmp(data, "audiowriteformat"))
+ ast_copy_string(buf, ast_getformatname(chan->writeformat), len);
+ else if (!strcasecmp(data, "tonezone") && chan->zone)
+ locked_copy_string(chan, buf, chan->zone->country, len);
+ else if (!strcasecmp(data, "language"))
+ locked_copy_string(chan, buf, chan->language, len);
+ else if (!strcasecmp(data, "musicclass"))
+ locked_copy_string(chan, buf, chan->musicclass, len);
+ else if (!strcasecmp(data, "state"))
+ locked_copy_string(chan, buf, ast_state2str(chan->_state), len);
+ else if (!strcasecmp(data, "channeltype"))
+ locked_copy_string(chan, buf, chan->tech->type, len);
+ else if (!strcasecmp(data, "transfercapability"))
+ locked_copy_string(chan, buf, transfercapability_table[chan->transfercapability & 0x1f], len);
+ else if (!strcasecmp(data, "callgroup")) {
+ char groupbuf[256];
+ locked_copy_string(chan, buf, ast_print_group(groupbuf, sizeof(groupbuf), chan->callgroup), len);
+ } else if (!chan->tech->func_channel_read
+ || chan->tech->func_channel_read(chan, function, data, buf, len)) {
+ ast_log(LOG_WARNING, "Unknown or unavailable item requested: '%s'\n", data);
+ ret = -1;
+ }
+
+ return ret;
+}
+
+static int func_channel_write(struct ast_channel *chan, const char *function,
+ char *data, const char *value)
+{
+ int ret = 0;
+ signed char gainset;
+
+ if (!strcasecmp(data, "language"))
+ locked_string_field_set(chan, language, value);
+ else if (!strcasecmp(data, "musicclass"))
+ locked_string_field_set(chan, musicclass, value);
+ else if (!strcasecmp(data, "tonezone")) {
+ struct ind_tone_zone *new_zone;
+ if (!(new_zone = ast_get_indication_zone(value))) {
+ ast_log(LOG_ERROR, "Unknown country code '%s' for tonezone. Check indications.conf for available country codes.\n", value);
+ ret = -1;
+ } else
+ chan->zone = new_zone;
+ } else if (!strcasecmp(data, "callgroup"))
+ chan->callgroup = ast_get_group(value);
+ else if (!strcasecmp(data, "txgain")) {
+ sscanf(value, "%hhd", &gainset);
+ ast_channel_setoption(chan, AST_OPTION_TXGAIN, &gainset, sizeof(gainset), 0);
+ } else if (!strcasecmp(data, "rxgain")) {
+ sscanf(value, "%hhd", &gainset);
+ ast_channel_setoption(chan, AST_OPTION_RXGAIN, &gainset, sizeof(gainset), 0);
+ } else if (!strcasecmp(data, "transfercapability")) {
+ unsigned short i;
+ for (i = 0; i < 0x20; i++) {
+ if (!strcasecmp(transfercapability_table[i], value) && strcmp(value, "UNK")) {
+ chan->transfercapability = i;
+ break;
+ }
+ }
+ } else if (!chan->tech->func_channel_write
+ || chan->tech->func_channel_write(chan, function, data, value)) {
+ ast_log(LOG_WARNING, "Unknown or unavailable item requested: '%s'\n",
+ data);
+ ret = -1;
+ }
+
+ return ret;
+}
+
+static struct ast_custom_function channel_function = {
+ .name = "CHANNEL",
+ .synopsis = "Gets/sets various pieces of information about the channel.",
+ .syntax = "CHANNEL(item)",
+ .desc = "Gets/set various pieces of information about the channel.\n"
+ "Standard items (provided by all channel technologies) are:\n"
+ "R/O audioreadformat format currently being read\n"
+ "R/O audionativeformat format used natively for audio\n"
+ "R/O audiowriteformat format currently being written\n"
+ "R/W callgroup call groups for call pickup\n"
+ "R/O channeltype technology used for channel\n"
+ "R/W language language for sounds played\n"
+ "R/W musicclass class (from musiconhold.conf) for hold music\n"
+ "R/W rxgain set rxgain level on channel drivers that support it\n"
+ "R/O state state for channel\n"
+ "R/W tonezone zone for indications played\n"
+ "R/W txgain set txgain level on channel drivers that support it\n"
+ "R/O videonativeformat format used natively for video\n"
+ "\n"
+ "chan_sip provides the following additional options:\n"
+ "R/O rtpqos Get QOS information about the RTP stream\n"
+ " This option takes two additional arguments:\n"
+ " Argument 1:\n"
+ " audio Get data about the audio stream\n"
+ " video Get data about the video stream\n"
+ " text Get data about the text stream\n"
+ " Argument 2:\n"
+ " local_ssrc Local SSRC (stream ID)\n"
+ " local_lostpackets Local lost packets\n"
+ " local_jitter Local calculated jitter\n"
+ " local_count Number of received packets\n"
+ " remote_ssrc Remote SSRC (stream ID)\n"
+ " remote_lostpackets Remote lost packets\n"
+ " remote_jitter Remote reported jitter\n"
+ " remote_count Number of transmitted packets\n"
+ " rtt Round trip time\n"
+ " all All statistics (in a form suited to logging, but not for parsing)\n"
+ "R/O rtpdest Get remote RTP destination information\n"
+ " This option takes one additional argument:\n"
+ " Argument 1:\n"
+ " audio Get audio destination\n"
+ " video Get video destination\n"
+ "\n"
+ "chan_iax2 provides the following additional options:\n"
+ "R/W osptoken Get or set the OSP token information for a call\n"
+ "\n"
+ "Additional items may be available from the channel driver providing\n"
+ "the channel; see its documentation for details.\n"
+ "\n"
+ "Any item requested that is not available on the current channel will\n"
+ "return an empty string.\n",
+ .read = func_channel_read,
+ .write = func_channel_write,
+};
+
+static int unload_module(void)
+{
+ return ast_custom_function_unregister(&channel_function);
+}
+
+static int load_module(void)
+{
+ return ast_custom_function_register(&channel_function);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Channel information dialplan function");
diff --git a/trunk/funcs/func_curl.c b/trunk/funcs/func_curl.c
new file mode 100644
index 000000000..32dbf7686
--- /dev/null
+++ b/trunk/funcs/func_curl.c
@@ -0,0 +1,204 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2004 - 2006, Tilghman Lesher
+ *
+ * Tilghman Lesher <curl-20050919@the-tilghman.com>
+ * and Brian Wilkins <bwilkins@cfl.rr.com> (Added POST option)
+ *
+ * app_curl.c is distributed with no restrictions on usage or
+ * redistribution.
+ *
+ * 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.
+ *
+ */
+
+/*! \file
+ *
+ * \brief Curl - Load a URL
+ *
+ * \author Tilghman Lesher <curl-20050919@the-tilghman.com>
+ *
+ * \note Brian Wilkins <bwilkins@cfl.rr.com> (Added POST option)
+ *
+ * \extref Depends on the CURL library - http://curl.haxx.se/
+ *
+ * \ingroup functions
+ */
+
+/*** MODULEINFO
+ <depend>curl</depend>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <curl/curl.h>
+
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/cli.h"
+#include "asterisk/module.h"
+#include "asterisk/app.h"
+#include "asterisk/utils.h"
+#include "asterisk/threadstorage.h"
+
+struct MemoryStruct {
+ char *memory;
+ size_t size;
+};
+
+/* There might be a realloc() out there that doesn't like reallocing
+ * NULL pointers, so we take care of it here
+ */
+static void *myrealloc(void *ptr, size_t size)
+{
+ return (ptr ? ast_realloc(ptr, size) : ast_malloc(size));
+}
+
+static size_t WriteMemoryCallback(void *ptr, size_t size, size_t nmemb, void *data)
+{
+ register int realsize = size * nmemb;
+ struct MemoryStruct *mem = (struct MemoryStruct *)data;
+
+ if ((mem->memory = (char *)myrealloc(mem->memory, mem->size + realsize + 1))) {
+ memcpy(&(mem->memory[mem->size]), ptr, realsize);
+ mem->size += realsize;
+ mem->memory[mem->size] = 0;
+ }
+
+ return realsize;
+}
+
+static const char *global_useragent = "asterisk-libcurl-agent/1.0";
+
+static int curl_instance_init(void *data)
+{
+ CURL **curl = data;
+
+ if (!(*curl = curl_easy_init()))
+ return -1;
+
+ curl_easy_setopt(*curl, CURLOPT_NOSIGNAL, 1);
+ curl_easy_setopt(*curl, CURLOPT_TIMEOUT, 180);
+ curl_easy_setopt(*curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
+ curl_easy_setopt(*curl, CURLOPT_USERAGENT, global_useragent);
+
+ return 0;
+}
+
+static void curl_instance_cleanup(void *data)
+{
+ CURL **curl = data;
+
+ curl_easy_cleanup(*curl);
+}
+
+AST_THREADSTORAGE_CUSTOM(curl_instance, curl_instance_init, curl_instance_cleanup);
+
+static int curl_internal(struct MemoryStruct *chunk, char *url, char *post)
+{
+ CURL **curl;
+
+ if (!(curl = ast_threadstorage_get(&curl_instance, sizeof(*curl))))
+ return -1;
+
+ curl_easy_setopt(*curl, CURLOPT_URL, url);
+ curl_easy_setopt(*curl, CURLOPT_WRITEDATA, (void *) chunk);
+
+ if (post) {
+ curl_easy_setopt(*curl, CURLOPT_POST, 1);
+ curl_easy_setopt(*curl, CURLOPT_POSTFIELDS, post);
+ }
+
+ curl_easy_perform(*curl);
+
+ if (post)
+ curl_easy_setopt(*curl, CURLOPT_POST, 0);
+
+ return 0;
+}
+
+static int acf_curl_exec(struct ast_channel *chan, const char *cmd, char *info, char *buf, size_t len)
+{
+ struct MemoryStruct chunk = { NULL, 0 };
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(url);
+ AST_APP_ARG(postdata);
+ );
+
+ *buf = '\0';
+
+ if (ast_strlen_zero(info)) {
+ ast_log(LOG_WARNING, "CURL requires an argument (URL)\n");
+ return -1;
+ }
+
+ AST_STANDARD_APP_ARGS(args, info);
+
+ if (chan)
+ ast_autoservice_start(chan);
+
+ if (!curl_internal(&chunk, args.url, args.postdata)) {
+ if (chunk.memory) {
+ chunk.memory[chunk.size] = '\0';
+ if (chunk.memory[chunk.size - 1] == 10)
+ chunk.memory[chunk.size - 1] = '\0';
+
+ ast_copy_string(buf, chunk.memory, len);
+ ast_free(chunk.memory);
+ }
+ } else {
+ ast_log(LOG_ERROR, "Cannot allocate curl structure\n");
+ }
+
+ if (chan)
+ ast_autoservice_stop(chan);
+
+ return 0;
+}
+
+struct ast_custom_function acf_curl = {
+ .name = "CURL",
+ .synopsis = "Retrieves the contents of a URL",
+ .syntax = "CURL(url[,post-data])",
+ .desc =
+ " url - URL to retrieve\n"
+ " post-data - Optional data to send as a POST (GET is default action)\n",
+ .read = acf_curl_exec,
+};
+
+static int unload_module(void)
+{
+ int res;
+
+ res = ast_custom_function_unregister(&acf_curl);
+
+ curl_global_cleanup();
+
+ return res;
+}
+
+static int load_module(void)
+{
+ int res;
+
+ if (curl_global_init(CURL_GLOBAL_ALL)) {
+ ast_log(LOG_ERROR, "Unable to initialize the CURL library. Cannot load func_curl\n");
+ return AST_MODULE_LOAD_DECLINE;
+ }
+
+ res = ast_custom_function_register(&acf_curl);
+
+ return res;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Load external URL");
+
diff --git a/trunk/funcs/func_cut.c b/trunk/funcs/func_cut.c
new file mode 100644
index 000000000..169fed6b5
--- /dev/null
+++ b/trunk/funcs/func_cut.c
@@ -0,0 +1,300 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (c) 2003-2006 Tilghman Lesher. All rights reserved.
+ *
+ * Tilghman Lesher <app_cut__v003@the-tilghman.com>
+ *
+ * This code is released by the author with no restrictions on usage.
+ *
+ * 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.
+ *
+ */
+
+/*! \file
+ *
+ * \brief CUT function
+ *
+ * \author Tilghman Lesher <app_cut__v003@the-tilghman.com>
+ *
+ * \ingroup functions
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/file.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/app.h"
+
+/* Maximum length of any variable */
+#define MAXRESULT 1024
+
+struct sortable_keys {
+ char *key;
+ float value;
+};
+
+static int sort_subroutine(const void *arg1, const void *arg2)
+{
+ const struct sortable_keys *one=arg1, *two=arg2;
+ if (one->value < two->value)
+ return -1;
+ else if (one->value == two->value)
+ return 0;
+ else
+ return 1;
+}
+
+#define ERROR_NOARG (-1)
+#define ERROR_NOMEM (-2)
+#define ERROR_USAGE (-3)
+
+static int sort_internal(struct ast_channel *chan, char *data, char *buffer, size_t buflen)
+{
+ char *strings, *ptrkey, *ptrvalue;
+ int count=1, count2, element_count=0;
+ struct sortable_keys *sortable_keys;
+
+ *buffer = '\0';
+
+ if (!data)
+ return ERROR_NOARG;
+
+ strings = ast_strdupa(data);
+
+ for (ptrkey = strings; *ptrkey; ptrkey++) {
+ if (*ptrkey == ',')
+ count++;
+ }
+
+ sortable_keys = alloca(count * sizeof(struct sortable_keys));
+
+ memset(sortable_keys, 0, count * sizeof(struct sortable_keys));
+
+ /* Parse each into a struct */
+ count2 = 0;
+ while ((ptrkey = strsep(&strings, ","))) {
+ ptrvalue = index(ptrkey, ':');
+ if (!ptrvalue) {
+ count--;
+ continue;
+ }
+ *ptrvalue++ = '\0';
+ sortable_keys[count2].key = ptrkey;
+ sscanf(ptrvalue, "%f", &sortable_keys[count2].value);
+ count2++;
+ }
+
+ /* Sort the structs */
+ qsort(sortable_keys, count, sizeof(struct sortable_keys), sort_subroutine);
+
+ for (count2 = 0; count2 < count; count2++) {
+ int blen = strlen(buffer);
+ if (element_count++) {
+ strncat(buffer + blen, ",", buflen - blen - 1);
+ blen++;
+ }
+ strncat(buffer + blen, sortable_keys[count2].key, buflen - blen - 1);
+ }
+
+ return 0;
+}
+
+static int cut_internal(struct ast_channel *chan, char *data, char *buffer, size_t buflen)
+{
+ char *parse;
+ size_t delim_consumed;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(varname);
+ AST_APP_ARG(delimiter);
+ AST_APP_ARG(field);
+ );
+
+ *buffer = '\0';
+
+ parse = ast_strdupa(data);
+
+ AST_STANDARD_APP_ARGS(args, parse);
+
+ /* Check and parse arguments */
+ if (args.argc < 3) {
+ return ERROR_NOARG;
+ } else {
+ char d, ds[2] = "";
+ char *tmp = alloca(strlen(args.varname) + 4);
+ char varvalue[MAXRESULT], *tmp2=varvalue;
+
+ if (tmp) {
+ snprintf(tmp, strlen(args.varname) + 4, "${%s}", args.varname);
+ } else {
+ return ERROR_NOMEM;
+ }
+
+ if (ast_get_encoded_char(args.delimiter, ds, &delim_consumed))
+ ast_copy_string(ds, "-", sizeof(ds));
+
+ /* String form of the delimiter, for use with strsep(3) */
+ d = *ds;
+
+ pbx_substitute_variables_helper(chan, tmp, tmp2, MAXRESULT - 1);
+
+ if (tmp2) {
+ int curfieldnum = 1;
+ while (tmp2 != NULL && args.field != NULL) {
+ char *nextgroup = strsep(&(args.field), "&");
+ int num1 = 0, num2 = MAXRESULT;
+ char trashchar;
+
+ if (sscanf(nextgroup, "%d-%d", &num1, &num2) == 2) {
+ /* range with both start and end */
+ } else if (sscanf(nextgroup, "-%d", &num2) == 1) {
+ /* range with end */
+ num1 = 0;
+ } else if ((sscanf(nextgroup, "%d%c", &num1, &trashchar) == 2) && (trashchar == '-')) {
+ /* range with start */
+ num2 = MAXRESULT;
+ } else if (sscanf(nextgroup, "%d", &num1) == 1) {
+ /* single number */
+ num2 = num1;
+ } else {
+ return ERROR_USAGE;
+ }
+
+ /* Get to start, if any */
+ if (num1 > 0) {
+ while (tmp2 != (char *)NULL + 1 && curfieldnum < num1) {
+ tmp2 = index(tmp2, d) + 1;
+ curfieldnum++;
+ }
+ }
+
+ /* Most frequent problem is the expectation of reordering fields */
+ if ((num1 > 0) && (curfieldnum > num1))
+ ast_log(LOG_WARNING, "We're already past the field you wanted?\n");
+
+ /* Re-null tmp2 if we added 1 to NULL */
+ if (tmp2 == (char *)NULL + 1)
+ tmp2 = NULL;
+
+ /* Output fields until we either run out of fields or num2 is reached */
+ while (tmp2 != NULL && curfieldnum <= num2) {
+ char *tmp3 = strsep(&tmp2, ds);
+ int curlen = strlen(buffer);
+
+ if (curlen)
+ snprintf(buffer + curlen, buflen - curlen, "%c%s", d, tmp3);
+ else
+ snprintf(buffer, buflen, "%s", tmp3);
+
+ curfieldnum++;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+static int acf_sort_exec(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
+{
+ int ret = -1;
+
+ switch (sort_internal(chan, data, buf, len)) {
+ case ERROR_NOARG:
+ ast_log(LOG_ERROR, "SORT() requires an argument\n");
+ break;
+ case ERROR_NOMEM:
+ ast_log(LOG_ERROR, "Out of memory\n");
+ break;
+ case 0:
+ ret = 0;
+ break;
+ default:
+ ast_log(LOG_ERROR, "Unknown internal error\n");
+ }
+
+ return ret;
+}
+
+static int acf_cut_exec(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
+{
+ int ret = -1;
+
+ if (chan)
+ ast_autoservice_start(chan);
+
+ switch (cut_internal(chan, data, buf, len)) {
+ case ERROR_NOARG:
+ ast_log(LOG_ERROR, "Syntax: CUT(<varname>,<char-delim>,<range-spec>) - missing argument!\n");
+ break;
+ case ERROR_NOMEM:
+ ast_log(LOG_ERROR, "Out of memory\n");
+ break;
+ case ERROR_USAGE:
+ ast_log(LOG_ERROR, "Usage: CUT(<varname>,<char-delim>,<range-spec>)\n");
+ break;
+ case 0:
+ ret = 0;
+ break;
+ default:
+ ast_log(LOG_ERROR, "Unknown internal error\n");
+ }
+
+ if (chan)
+ ast_autoservice_stop(chan);
+
+ return ret;
+}
+
+struct ast_custom_function acf_sort = {
+ .name = "SORT",
+ .synopsis = "Sorts a list of key/vals into a list of keys, based upon the vals",
+ .syntax = "SORT(key1:val1[...][,keyN:valN])",
+ .desc =
+"Takes a comma-separated list of keys and values, each separated by a colon, and returns a\n"
+"comma-separated list of the keys, sorted by their values. Values will be evaluated as\n"
+"floating-point numbers.\n",
+ .read = acf_sort_exec,
+};
+
+struct ast_custom_function acf_cut = {
+ .name = "CUT",
+ .synopsis = "Slices and dices strings, based upon a named delimiter.",
+ .syntax = "CUT(<varname>,<char-delim>,<range-spec>)",
+ .desc =
+" varname - variable you want cut\n"
+" char-delim - defaults to '-'\n"
+" range-spec - number of the field you want (1-based offset)\n"
+" may also be specified as a range (with -)\n"
+" or group of ranges and fields (with &)\n",
+ .read = acf_cut_exec,
+};
+
+static int unload_module(void)
+{
+ int res = 0;
+
+ res |= ast_custom_function_unregister(&acf_cut);
+ res |= ast_custom_function_unregister(&acf_sort);
+
+ return res;
+}
+
+static int load_module(void)
+{
+ int res = 0;
+
+ res |= ast_custom_function_register(&acf_cut);
+ res |= ast_custom_function_register(&acf_sort);
+
+ return res;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Cut out information from a string");
diff --git a/trunk/funcs/func_db.c b/trunk/funcs/func_db.c
new file mode 100644
index 000000000..1c15e5df8
--- /dev/null
+++ b/trunk/funcs/func_db.c
@@ -0,0 +1,225 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2005-2006, Russell Bryant <russelb@clemson.edu>
+ *
+ * func_db.c adapted from the old app_db.c, copyright by the following people
+ * Copyright (C) 2005, Mark Spencer <markster@digium.com>
+ * Copyright (C) 2003, Jefferson Noxon <jeff@debian.org>
+ *
+ * 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 Functions for interaction with the Asterisk database
+ *
+ * \author Russell Bryant <russelb@clemson.edu>
+ *
+ * \ingroup functions
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <regex.h>
+
+#include "asterisk/module.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/utils.h"
+#include "asterisk/app.h"
+#include "asterisk/astdb.h"
+
+static int function_db_read(struct ast_channel *chan, const char *cmd,
+ char *parse, char *buf, size_t len)
+{
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(family);
+ AST_APP_ARG(key);
+ );
+
+ buf[0] = '\0';
+
+ if (ast_strlen_zero(parse)) {
+ ast_log(LOG_WARNING, "DB requires an argument, DB(<family>/<key>)\n");
+ return -1;
+ }
+
+ AST_NONSTANDARD_APP_ARGS(args, parse, '/');
+
+ if (args.argc < 2) {
+ ast_log(LOG_WARNING, "DB requires an argument, DB(<family>/<key>)\n");
+ return -1;
+ }
+
+ if (ast_db_get(args.family, args.key, buf, len - 1)) {
+ ast_debug(1, "DB: %s/%s not found in database.\n", args.family, args.key);
+ } else
+ pbx_builtin_setvar_helper(chan, "DB_RESULT", buf);
+
+ return 0;
+}
+
+static int function_db_write(struct ast_channel *chan, const char *cmd, char *parse,
+ const char *value)
+{
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(family);
+ AST_APP_ARG(key);
+ );
+
+ if (ast_strlen_zero(parse)) {
+ ast_log(LOG_WARNING, "DB requires an argument, DB(<family>/<key>)=<value>\n");
+ return -1;
+ }
+
+ AST_NONSTANDARD_APP_ARGS(args, parse, '/');
+
+ if (args.argc < 2) {
+ ast_log(LOG_WARNING, "DB requires an argument, DB(<family>/<key>)=value\n");
+ return -1;
+ }
+
+ if (ast_db_put(args.family, args.key, (char *) value))
+ ast_log(LOG_WARNING, "DB: Error writing value to database.\n");
+
+ return 0;
+}
+
+static struct ast_custom_function db_function = {
+ .name = "DB",
+ .synopsis = "Read from or write to the Asterisk database",
+ .syntax = "DB(<family>/<key>)",
+ .desc =
+"This function will read from or write a value to the Asterisk database. On a\n"
+"read, this function returns the corresponding value from the database, or blank\n"
+"if it does not exist. Reading a database value will also set the variable\n"
+"DB_RESULT. If you wish to find out if an entry exists, use the DB_EXISTS\n"
+"function.\n",
+ .read = function_db_read,
+ .write = function_db_write,
+};
+
+static int function_db_exists(struct ast_channel *chan, const char *cmd,
+ char *parse, char *buf, size_t len)
+{
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(family);
+ AST_APP_ARG(key);
+ );
+
+ buf[0] = '\0';
+
+ if (ast_strlen_zero(parse)) {
+ ast_log(LOG_WARNING, "DB_EXISTS requires an argument, DB(<family>/<key>)\n");
+ return -1;
+ }
+
+ AST_NONSTANDARD_APP_ARGS(args, parse, '/');
+
+ if (args.argc < 2) {
+ ast_log(LOG_WARNING, "DB_EXISTS requires an argument, DB(<family>/<key>)\n");
+ return -1;
+ }
+
+ if (ast_db_get(args.family, args.key, buf, len - 1))
+ strcpy(buf, "0");
+ else {
+ pbx_builtin_setvar_helper(chan, "DB_RESULT", buf);
+ strcpy(buf, "1");
+ }
+
+ return 0;
+}
+
+static struct ast_custom_function db_exists_function = {
+ .name = "DB_EXISTS",
+ .synopsis = "Check to see if a key exists in the Asterisk database",
+ .syntax = "DB_EXISTS(<family>/<key>)",
+ .desc =
+ "This function will check to see if a key exists in the Asterisk\n"
+ "database. If it exists, the function will return \"1\". If not,\n"
+ "it will return \"0\". Checking for existence of a database key will\n"
+ "also set the variable DB_RESULT to the key's value if it exists.\n",
+ .read = function_db_exists,
+};
+
+static int function_db_delete(struct ast_channel *chan, const char *cmd,
+ char *parse, char *buf, size_t len)
+{
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(family);
+ AST_APP_ARG(key);
+ );
+
+ buf[0] = '\0';
+
+ if (ast_strlen_zero(parse)) {
+ ast_log(LOG_WARNING, "DB_DELETE requires an argument, DB_DELETE(<family>/<key>)\n");
+ return -1;
+ }
+
+ AST_NONSTANDARD_APP_ARGS(args, parse, '/');
+
+ if (args.argc < 2) {
+ ast_log(LOG_WARNING, "DB_DELETE requires an argument, DB_DELETE(<family>/<key>)\n");
+ return -1;
+ }
+
+ if (ast_db_get(args.family, args.key, buf, len - 1)) {
+ ast_debug(1, "DB_DELETE: %s/%s not found in database.\n", args.family, args.key);
+ } else {
+ if (ast_db_del(args.family, args.key)) {
+ ast_debug(1, "DB_DELETE: %s/%s could not be deleted from the database\n", args.family, args.key);
+ }
+ }
+ pbx_builtin_setvar_helper(chan, "DB_RESULT", buf);
+
+ return 0;
+}
+
+
+static struct ast_custom_function db_delete_function = {
+ .name = "DB_DELETE",
+ .synopsis = "Return a value from the database and delete it",
+ .syntax = "DB_DELETE(<family>/<key>)",
+ .desc =
+ "This function will retrieve a value from the Asterisk database\n"
+ " and then remove that key from the database. DB_RESULT\n"
+ "will be set to the key's value if it exists.\n",
+ .read = function_db_delete,
+};
+
+static int unload_module(void)
+{
+ int res = 0;
+
+ res |= ast_custom_function_unregister(&db_function);
+ res |= ast_custom_function_unregister(&db_exists_function);
+ res |= ast_custom_function_unregister(&db_delete_function);
+
+ return res;
+}
+
+static int load_module(void)
+{
+ int res = 0;
+
+ res |= ast_custom_function_register(&db_function);
+ res |= ast_custom_function_register(&db_exists_function);
+ res |= ast_custom_function_register(&db_delete_function);
+
+ return res;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Database (astdb) related dialplan functions");
diff --git a/trunk/funcs/func_devstate.c b/trunk/funcs/func_devstate.c
new file mode 100644
index 000000000..950208be5
--- /dev/null
+++ b/trunk/funcs/func_devstate.c
@@ -0,0 +1,255 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2007, Digium, Inc.
+ *
+ * Russell Bryant <russell@digium.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 Manually controlled blinky lights
+ *
+ * \author Russell Bryant <russell@digium.com>
+ *
+ * \ingroup functions
+ *
+ * \note Props go out to Ahrimanes in \#asterisk for requesting this at 4:30 AM
+ * when I couldn't sleep. :)
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/module.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/utils.h"
+#include "asterisk/linkedlists.h"
+#include "asterisk/devicestate.h"
+#include "asterisk/cli.h"
+#include "asterisk/astdb.h"
+#include "asterisk/app.h"
+
+static const char astdb_family[] = "CustomDevstate";
+
+static int devstate_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
+{
+ ast_copy_string(buf, ast_devstate_str(ast_device_state(data)), len);
+
+ return 0;
+}
+
+static int devstate_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
+{
+ size_t len = strlen("Custom:");
+
+ if (strncasecmp(data, "Custom:", len)) {
+ ast_log(LOG_WARNING, "The DEVICE_STATE function can only be used to set 'Custom:' device state!\n");
+ return -1;
+ }
+ data += len;
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "DEVICE_STATE function called with no custom device name!\n");
+ return -1;
+ }
+
+ ast_db_put(astdb_family, data, value);
+
+ ast_devstate_changed(ast_devstate_val(value), "Custom:%s", data);
+
+ return 0;
+}
+
+enum {
+ HINT_OPT_NAME = (1 << 0),
+};
+
+AST_APP_OPTIONS(hint_options, BEGIN_OPTIONS
+ AST_APP_OPTION('n', HINT_OPT_NAME),
+END_OPTIONS );
+
+static int hint_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
+{
+ char *exten, *context;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(exten);
+ AST_APP_ARG(options);
+ );
+ struct ast_flags opts = { 0, };
+ int res;
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "The HINT function requires an extension\n");
+ return -1;
+ }
+
+ AST_STANDARD_APP_ARGS(args, data);
+
+ if (ast_strlen_zero(args.exten)) {
+ ast_log(LOG_WARNING, "The HINT function requires an extension\n");
+ return -1;
+ }
+
+ context = exten = args.exten;
+ strsep(&context, "@");
+ if (ast_strlen_zero(context))
+ context = "default";
+
+ if (!ast_strlen_zero(args.options))
+ ast_app_parse_options(hint_options, &opts, NULL, args.options);
+
+ if (ast_test_flag(&opts, HINT_OPT_NAME))
+ res = ast_get_hint(NULL, 0, buf, len, chan, context, exten);
+ else
+ res = ast_get_hint(buf, len, NULL, 0, chan, context, exten);
+
+ return !res; /* ast_get_hint returns non-zero on success */
+}
+
+static enum ast_device_state custom_devstate_callback(const char *data)
+{
+ char buf[256] = "";
+
+ ast_db_get(astdb_family, data, buf, sizeof(buf));
+
+ return ast_devstate_val(buf);
+}
+
+static char *cli_funcdevstate_list(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct ast_db_entry *db_entry, *db_tree;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "funcdevstate list";
+ e->usage =
+ "Usage: funcdevstate list\n"
+ " List all custom device states that have been set by using\n"
+ " the DEVICE_STATE dialplan function.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != e->args)
+ return CLI_SHOWUSAGE;
+
+ ast_cli(a->fd, "\n"
+ "---------------------------------------------------------------------\n"
+ "--- Custom Device States --------------------------------------------\n"
+ "---------------------------------------------------------------------\n"
+ "---\n");
+
+ db_entry = db_tree = ast_db_gettree(astdb_family, NULL);
+ for (; db_entry; db_entry = db_entry->next) {
+ const char *dev_name = strrchr(db_entry->key, '/') + 1;
+ if (dev_name <= (const char *) 1)
+ continue;
+ ast_cli(a->fd, "--- Name: 'Custom:%s' State: '%s'\n"
+ "---\n", dev_name, db_entry->data);
+ }
+ ast_db_freetree(db_tree);
+ db_tree = NULL;
+
+ ast_cli(a->fd,
+ "---------------------------------------------------------------------\n"
+ "---------------------------------------------------------------------\n"
+ "\n");
+
+ return CLI_SUCCESS;
+}
+
+static struct ast_cli_entry cli_funcdevstate[] = {
+ AST_CLI_DEFINE(cli_funcdevstate_list, "List currently known custom device states"),
+};
+
+static struct ast_custom_function devstate_function = {
+ .name = "DEVICE_STATE",
+ .synopsis = "Get or Set a device state",
+ .syntax = "DEVICE_STATE(device)",
+ .desc =
+ " The DEVICE_STATE function can be used to retrieve the device state from any\n"
+ "device state provider. For example:\n"
+ " NoOp(SIP/mypeer has state ${DEVICE_STATE(SIP/mypeer)})\n"
+ " NoOp(Conference number 1234 has state ${DEVICE_STATE(MeetMe:1234)})\n"
+ "\n"
+ " The DEVICE_STATE function can also be used to set custom device state from\n"
+ "the dialplan. The \"Custom:\" prefix must be used. For example:\n"
+ " Set(DEVICE_STATE(Custom:lamp1)=BUSY)\n"
+ " Set(DEVICE_STATE(Custom:lamp2)=NOT_INUSE)\n"
+ "You can subscribe to the status of a custom device state using a hint in\n"
+ "the dialplan:\n"
+ " exten => 1234,hint,Custom:lamp1\n"
+ "\n"
+ " The possible values for both uses of this function are:\n"
+ "UNKNOWN | NOT_INUSE | INUSE | BUSY | INVALID | UNAVAILABLE | RINGING\n"
+ "RINGINUSE | ONHOLD\n",
+ .read = devstate_read,
+ .write = devstate_write,
+};
+
+static struct ast_custom_function hint_function = {
+ .name = "HINT",
+ .synopsis = "Get the devices set for a dialplan hint",
+ .syntax = "HINT(extension[@context][|options])",
+ .desc =
+ " The HINT function can be used to retrieve the list of devices that are\n"
+ "mapped to a dialplan hint. For example:\n"
+ " NoOp(Hint for Extension 1234 is ${HINT(1234)})\n"
+ "Options:\n"
+ " 'n' - Retrieve name on the hint instead of list of devices\n"
+ "",
+ .read = hint_read,
+};
+
+static int unload_module(void)
+{
+ int res = 0;
+
+ res |= ast_custom_function_unregister(&devstate_function);
+ res |= ast_custom_function_unregister(&hint_function);
+ res |= ast_devstate_prov_del("Custom");
+ res |= ast_cli_unregister_multiple(cli_funcdevstate, ARRAY_LEN(cli_funcdevstate));
+
+ return res;
+}
+
+static int load_module(void)
+{
+ int res = 0;
+ struct ast_db_entry *db_entry, *db_tree;
+
+ /* Populate the device state cache on the system with all of the currently
+ * known custom device states. */
+ db_entry = db_tree = ast_db_gettree(astdb_family, NULL);
+ for (; db_entry; db_entry = db_entry->next) {
+ const char *dev_name = strrchr(db_entry->key, '/') + 1;
+ if (dev_name <= (const char *) 1)
+ continue;
+ ast_devstate_changed(ast_devstate_val(db_entry->data),
+ "Custom:%s\n", dev_name);
+ }
+ ast_db_freetree(db_tree);
+ db_tree = NULL;
+
+ res |= ast_custom_function_register(&devstate_function);
+ res |= ast_custom_function_register(&hint_function);
+ res |= ast_devstate_prov_add("Custom", custom_devstate_callback);
+ res |= ast_cli_register_multiple(cli_funcdevstate, ARRAY_LEN(cli_funcdevstate));
+
+ return res;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Gets or sets a device state in the dialplan");
diff --git a/trunk/funcs/func_dialgroup.c b/trunk/funcs/func_dialgroup.c
new file mode 100644
index 000000000..746784ba9
--- /dev/null
+++ b/trunk/funcs/func_dialgroup.c
@@ -0,0 +1,220 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2007, Tilghman Lesher
+ *
+ * Tilghman Lesher <func_dialgroup__200709@the-tilghman.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 Dial group dialplan function
+ *
+ * \author Tilghman Lesher <func_dialgroup__200709@the-tilghman.com>
+ *
+ * \ingroup functions
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <sys/stat.h>
+
+#include "asterisk/module.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/utils.h"
+#include "asterisk/app.h"
+#include "asterisk/astobj2.h"
+
+static struct ao2_container *group_container = NULL;
+
+struct group_entry {
+ char name[AST_CHANNEL_NAME];
+};
+
+struct group {
+ char name[AST_MAX_EXTENSION];
+ struct ao2_container *entries;
+};
+
+static void group_destroy(void *vgroup)
+{
+ struct group *group = vgroup;
+ ao2_ref(group->entries, -1);
+}
+
+static int group_hash_fn(const void *obj, const int flags)
+{
+ const struct group *g = obj;
+ return ast_str_hash(g->name);
+}
+
+static int group_cmp_fn(void *obj1, void *name2, int flags)
+{
+ struct group *g1 = obj1, *g2 = name2;
+ char *name = name2;
+ if (flags & OBJ_POINTER)
+ return strcmp(g1->name, g2->name) ? 0 : CMP_MATCH;
+ else
+ return strcmp(g1->name, name) ? 0 : CMP_MATCH;
+}
+
+static int entry_hash_fn(const void *obj, const int flags)
+{
+ const struct group_entry *e = obj;
+ return ast_str_hash(e->name);
+}
+
+static int entry_cmp_fn(void *obj1, void *name2, int flags)
+{
+ struct group_entry *e1 = obj1, *e2 = name2;
+ char *name = name2;
+ if (flags & OBJ_POINTER)
+ return strcmp(e1->name, e2->name) ? 0 : CMP_MATCH;
+ else
+ return strcmp(e1->name, name) ? 0 : CMP_MATCH;
+}
+
+static int dialgroup_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
+{
+ struct ao2_iterator i;
+ struct group *grhead = ao2_find(group_container, data, 0);
+ struct group_entry *entry;
+ size_t bufused = 0;
+ int trunc_warning = 0;
+
+ if (!grhead) {
+ ast_log(LOG_WARNING, "No such dialgroup '%s'\n", data);
+ return -1;
+ }
+
+ i = ao2_iterator_init(grhead->entries, 0);
+ while ((entry = ao2_iterator_next(&i))) {
+ int tmp = strlen(entry->name);
+ /* Ensure that we copy only complete names, not partials */
+ if (len - bufused > tmp + 2) {
+ if (bufused != 0)
+ buf[bufused++] = '&';
+ ast_copy_string(buf + bufused, entry->name, len - bufused);
+ bufused += tmp;
+ } else if (trunc_warning++ == 0)
+ ast_log(LOG_WARNING, "Dialgroup '%s' is too large. Truncating list.\n", data);
+ }
+
+ return 0;
+}
+
+static int dialgroup_write(struct ast_channel *chan, const char *cmd, char *data, const char *cvalue)
+{
+ struct group *grhead;
+ struct group_entry *entry;
+ int j;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(group);
+ AST_APP_ARG(op);
+ );
+ AST_DECLARE_APP_ARGS(inter,
+ AST_APP_ARG(faces)[100];
+ );
+ char *value = ast_strdupa(cvalue);
+
+ AST_STANDARD_APP_ARGS(args, data);
+ AST_NONSTANDARD_APP_ARGS(inter, value, '&');
+
+ if (!(grhead = ao2_find(group_container, data, 0))) {
+ /* Create group */
+ grhead = ao2_alloc(sizeof(*grhead), group_destroy);
+ if (!grhead)
+ return -1;
+ grhead->entries = ao2_container_alloc(37, entry_hash_fn, entry_cmp_fn);
+ if (!grhead->entries) {
+ ao2_ref(grhead, -1);
+ return -1;
+ }
+ ast_copy_string(grhead->name, args.group, sizeof(grhead->name));
+ ao2_link(group_container, grhead);
+ }
+
+ if (ast_strlen_zero(args.op)) {
+ /* Wholesale replacement of the group */
+ args.op = "add";
+
+ /* Remove all existing */
+ ao2_ref(grhead->entries, -1);
+ if (!(grhead->entries = ao2_container_alloc(37, entry_hash_fn, entry_cmp_fn)))
+ return -1;
+ }
+
+ if (strcasecmp(args.op, "add") == 0) {
+ for (j = 0; j < inter.argc; j++) {
+ if ((entry = ao2_alloc(sizeof(*entry), NULL))) {
+ ast_copy_string(entry->name, inter.faces[j], sizeof(entry->name));
+ ao2_link(grhead->entries, entry);
+ } else
+ ast_log(LOG_WARNING, "Unable to add '%s' to dialgroup '%s'\n", inter.faces[j], grhead->name);
+ }
+ } else if (strncasecmp(args.op, "del", 3) == 0) {
+ for (j = 0; j < inter.argc; j++) {
+ if ((entry = ao2_find(grhead->entries, inter.faces[j], OBJ_UNLINK)))
+ ao2_ref(entry, -1);
+ else
+ ast_log(LOG_WARNING, "Interface '%s' not found in dialgroup '%s'\n", inter.faces[j], grhead->name);
+ }
+ } else
+ ast_log(LOG_ERROR, "Unrecognized operation: %s\n", args.op);
+
+ return 0;
+}
+
+static struct ast_custom_function dialgroup_function = {
+ .name = "DIALGROUP",
+ .synopsis = "Manages a group of users for dialing",
+ .syntax = "DIALGROUP(<group>[,op])",
+ .desc =
+" DIALGROUP presents an interface meant to be used in concert with the Dial\n"
+"application, by presenting a list of channels which should be dialled when\n"
+"referenced.\n"
+" When DIALGROUP is read from, the argument is interpreted as the particular\n"
+"group for which a dial should be attempted. When DIALGROUP is written to\n"
+"with no arguments, the entire list is replaced with the argument specified.\n"
+"Other operations are as follows:\n"
+" add - add a channel name or interface (write-only)\n"
+" del - remove a channel name or interface (write-only)\n\n"
+"Functionality is similar to a queue, except that when no interfaces are\n"
+"available, execution may continue in the dialplan. This is useful when\n"
+"you want certain people to be the first to answer any calls, with immediate\n"
+"fallback to a queue when the front line people are busy or unavailable, but\n"
+"you still want front line people to log in and out of that group, just like\n"
+"a queue.\n",
+ .read = dialgroup_read,
+ .write = dialgroup_write,
+};
+
+static int unload_module(void)
+{
+ int res = ast_custom_function_unregister(&dialgroup_function);
+ ao2_ref(group_container, -1);
+ return res;
+}
+
+static int load_module(void)
+{
+ if ((group_container = ao2_container_alloc(37, group_hash_fn, group_cmp_fn)))
+ return ast_custom_function_register(&dialgroup_function);
+ else
+ return AST_MODULE_LOAD_DECLINE;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Dialgroup dialplan function");
diff --git a/trunk/funcs/func_dialplan.c b/trunk/funcs/func_dialplan.c
new file mode 100644
index 000000000..6d4c488f6
--- /dev/null
+++ b/trunk/funcs/func_dialplan.c
@@ -0,0 +1,106 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2007, Digium, Inc.
+ *
+ * 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 Dialplan group functions check if a dialplan entry exists
+ *
+ * \author Gregory Nietsky AKA irroot <gregory@networksentry.co.za>
+ * \author Russell Bryant <russell@digium.com>
+ *
+ * \ingroup functions
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/module.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/app.h"
+
+static int isexten_function_read(struct ast_channel *chan, const char *cmd, char *data,
+ char *buf, size_t len)
+{
+ char *parse;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(context);
+ AST_APP_ARG(exten);
+ AST_APP_ARG(priority);
+ );
+
+ strcpy(buf, "0");
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_ERROR, "DIALPLAN_EXISTS() requires an argument\n");
+ return -1;
+ }
+
+ parse = ast_strdupa(data);
+ AST_STANDARD_APP_ARGS(args, parse);
+
+ if (!ast_strlen_zero(args.priority)) {
+ int priority_num;
+ if (sscanf(args.priority, "%d", &priority_num) == 1 && priority_num > 0) {
+ int res;
+ res = ast_exists_extension(chan, args.context, args.exten, priority_num,
+ chan->cid.cid_num);
+ if (res)
+ strcpy(buf, "1");
+ } else {
+ int res;
+ res = ast_findlabel_extension(chan, args.context, args.exten,
+ args.priority, chan->cid.cid_num);
+ if (res > 0)
+ strcpy(buf, "1");
+ }
+ } else if (!ast_strlen_zero(args.exten)) {
+ int res;
+ res = ast_exists_extension(chan, args.context, args.exten, 1,
+ chan->cid.cid_num);
+ if (res)
+ strcpy(buf, "1");
+ } else if (!ast_strlen_zero(args.context)) {
+ if (ast_context_find(args.context))
+ strcpy(buf, "1");
+ } else {
+ ast_log(LOG_ERROR, "Invalid arguments provided to DIALPLAN_EXISTS\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static struct ast_custom_function isexten_function = {
+ .name = "DIALPLAN_EXISTS",
+ .syntax = "DIALPLAN_EXISTS(context[,extension[,priority]])",
+ .synopsis = "Checks the existence of a dialplan target.",
+ .desc = "This function returns 1 if the target exits. Otherwise, it returns 0.\n",
+ .read = isexten_function_read,
+};
+
+static int unload_module(void)
+{
+ return ast_custom_function_unregister(&isexten_function);
+}
+
+static int load_module(void)
+{
+ return ast_custom_function_register(&isexten_function);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Dialplan Context/Extension/Priority Checking Functions");
diff --git a/trunk/funcs/func_enum.c b/trunk/funcs/func_enum.c
new file mode 100644
index 000000000..d69881955
--- /dev/null
+++ b/trunk/funcs/func_enum.c
@@ -0,0 +1,391 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2006
+ *
+ * Mark Spencer <markster@digium.com>
+ * Oleksiy Krivoshey <oleksiyk@gmail.com>
+ * Russell Bryant <russelb@clemson.edu>
+ * Brett Bryant <bbryant@digium.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 ENUM Functions
+ *
+ * \author Mark Spencer <markster@digium.com>
+ * \author Oleksiy Krivoshey <oleksiyk@gmail.com>
+ * \author Russell Bryant <russelb@clemson.edu>
+ * \author Brett Bryant <bbryant@digium.com>
+ *
+ * \arg See also AstENUM
+ *
+ * \ingroup functions
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/module.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/utils.h"
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/enum.h"
+#include "asterisk/app.h"
+
+static char *synopsis = "Syntax: ENUMLOOKUP(number[,Method-type[,options[,record#[,zone-suffix]]]])\n";
+
+static int function_enum(struct ast_channel *chan, const char *cmd, char *data,
+ char *buf, size_t len)
+{
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(number);
+ AST_APP_ARG(tech);
+ AST_APP_ARG(options);
+ AST_APP_ARG(record);
+ AST_APP_ARG(zone);
+ );
+ int res = 0;
+ char tech[80];
+ char dest[256] = "", tmp[2] = "", num[AST_MAX_EXTENSION] = "";
+ char *s, *p;
+ unsigned int record = 1;
+
+ buf[0] = '\0';
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, synopsis);
+ return -1;
+ }
+
+ AST_STANDARD_APP_ARGS(args, data);
+
+ if (args.argc < 1) {
+ ast_log(LOG_WARNING, synopsis);
+ return -1;
+ }
+
+ ast_copy_string(tech, args.tech ? args.tech : "sip", sizeof(tech));
+
+ if (!args.zone)
+ args.zone = "e164.arpa";
+
+ if (!args.options)
+ args.options = "";
+
+ if (args.record)
+ record = atoi(args.record);
+
+ /* strip any '-' signs from number */
+ for (s = p = args.number; *s; s++) {
+ if (*s != '-') {
+ snprintf(tmp, sizeof(tmp), "%c", *s);
+ strncat(num, tmp, sizeof(num));
+ }
+
+ }
+
+ res = ast_get_enum(chan, num, dest, sizeof(dest), tech, sizeof(tech), args.zone, args.options, 1, NULL);
+
+ p = strchr(dest, ':');
+ if (p && strcasecmp(tech, "ALL"))
+ ast_copy_string(buf, p + 1, len);
+ else
+ ast_copy_string(buf, dest, len);
+
+ return 0;
+}
+
+unsigned int enum_datastore_id;
+
+struct enum_result_datastore {
+ struct enum_context *context;
+ unsigned int id;
+};
+
+static void erds_destroy(struct enum_result_datastore *data)
+{
+ int k;
+
+ for (k = 0; k < data->context->naptr_rrs_count; k++) {
+ ast_free(data->context->naptr_rrs[k].result);
+ ast_free(data->context->naptr_rrs[k].tech);
+ }
+
+ ast_free(data->context->naptr_rrs);
+ ast_free(data->context);
+ ast_free(data);
+}
+
+static void erds_destroy_cb(void *data)
+{
+ struct enum_result_datastore *erds = data;
+ erds_destroy(erds);
+}
+
+const struct ast_datastore_info enum_result_datastore_info = {
+ .type = "ENUMQUERY",
+ .destroy = erds_destroy_cb,
+};
+
+static int enum_query_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
+{
+ struct enum_result_datastore *erds;
+ struct ast_datastore *datastore;
+ char *parse, tech[128], dest[128];
+ int res = -1;
+
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(number);
+ AST_APP_ARG(tech);
+ AST_APP_ARG(zone);
+ );
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "ENUMQUERY requires at least a number as an argument...\n");
+ goto finish;
+ }
+
+ parse = ast_strdupa(data);
+
+ AST_STANDARD_APP_ARGS(args, parse);
+
+ if (!chan) {
+ ast_log(LOG_ERROR, "ENUMQUERY cannot be used without a channel!\n");
+ goto finish;
+ }
+
+ if (!args.zone)
+ args.zone = "e164.zone";
+
+ ast_copy_string(tech, args.tech ? args.tech : "sip", sizeof(tech));
+
+ if (!(erds = ast_calloc(1, sizeof(*erds))))
+ goto finish;
+
+ if (!(erds->context = ast_calloc(1, sizeof(*erds->context)))) {
+ ast_free(erds);
+ goto finish;
+ }
+
+ erds->id = ast_atomic_fetchadd_int((int *) &enum_datastore_id, 1);
+
+ snprintf(buf, len, "%u", erds->id);
+
+ if (!(datastore = ast_channel_datastore_alloc(&enum_result_datastore_info, buf))) {
+ ast_free(erds->context);
+ ast_free(erds);
+ goto finish;
+ }
+
+ ast_get_enum(chan, args.number, dest, sizeof(dest), tech, sizeof(tech), args.zone, "", 1, &erds->context);
+
+ datastore->data = erds;
+
+ ast_channel_lock(chan);
+ ast_channel_datastore_add(chan, datastore);
+ ast_channel_unlock(chan);
+
+ res = 0;
+
+finish:
+
+ return res;
+}
+
+static int enum_result_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
+{
+ struct enum_result_datastore *erds;
+ struct ast_datastore *datastore;
+ char *parse, *p;
+ unsigned int num;
+ int res = -1, k;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(id);
+ AST_APP_ARG(resultnum);
+ );
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "ENUMRESULT requires two arguments (id and resultnum)\n");
+ goto finish;
+ }
+
+ if (!chan) {
+ ast_log(LOG_ERROR, "ENUMRESULT can not be used without a channel!\n");
+ goto finish;
+ }
+
+ parse = ast_strdupa(data);
+
+ AST_STANDARD_APP_ARGS(args, parse);
+
+ if (ast_strlen_zero(args.id)) {
+ ast_log(LOG_ERROR, "A result ID must be provided to ENUMRESULT\n");
+ goto finish;
+ }
+
+ if (ast_strlen_zero(args.resultnum)) {
+ ast_log(LOG_ERROR, "A result number must be given to ENUMRESULT!\n");
+ goto finish;
+ }
+
+ ast_channel_lock(chan);
+ datastore = ast_channel_datastore_find(chan, &enum_result_datastore_info, args.id);
+ ast_channel_unlock(chan);
+ if (!datastore) {
+ ast_log(LOG_WARNING, "No ENUM results found for query id!\n");
+ goto finish;
+ }
+
+ erds = datastore->data;
+
+ if (!strcasecmp(args.resultnum, "getnum")) {
+ snprintf(buf, len, "%u", erds->context->naptr_rrs_count);
+ res = 0;
+ goto finish;
+ }
+
+ if (sscanf(args.resultnum, "%u", &num) != 1) {
+ ast_log(LOG_ERROR, "Invalid value '%s' for resultnum to ENUMRESULT!\n", args.resultnum);
+ goto finish;
+ }
+
+ if (!num || num > erds->context->naptr_rrs_count) {
+ ast_log(LOG_WARNING, "Result number %u is not valid for ENUM query results for ID %s!\n", num, args.id);
+ goto finish;
+ }
+
+ for (k = 0; k < erds->context->naptr_rrs_count; k++) {
+ if (num - 1 != erds->context->naptr_rrs[k].sort_pos)
+ continue;
+
+ p = strchr(erds->context->naptr_rrs[k].result, ':');
+
+ if (p && strcasecmp(erds->context->naptr_rrs[k].tech, "ALL"))
+ ast_copy_string(buf, p + 1, len);
+ else
+ ast_copy_string(buf, erds->context->naptr_rrs[k].result, len);
+
+ break;
+ }
+
+ res = 0;
+
+finish:
+
+ return res;
+}
+
+static struct ast_custom_function enum_query_function = {
+ .name = "ENUMQUERY",
+ .synopsis = "Initiate an ENUM query",
+ .syntax = "ENUMQUERY(number[,Method-type[,zone-suffix]])",
+ .desc = "This will do a ENUM lookup of the given phone number.\n"
+ "If no method-tpye is given, the default will be sip. If no\n"
+ "zone-suffix is given, the default will be \"e164.arpa\".\n"
+ "The result of this function will be a numeric ID that can\n"
+ "be used to retrieve the results using the ENUMRESULT function.\n",
+ .read = enum_query_read,
+};
+
+static struct ast_custom_function enum_result_function = {
+ .name = "ENUMRESULT",
+ .synopsis = "Retrieve results from a ENUMQUERY",
+ .syntax = "ENUMRESULT(id,resultnum)",
+ .desc = "This function will retrieve results from a previous use\n"
+ "of the ENUMQUERY function.\n"
+ " id - This argument is the identifier returned by the ENUMQUERY function.\n"
+ " resultnum - This is the number of the result that you want to retrieve.\n"
+ " Results start at 1. If this argument is specified as \"getnum\",\n"
+ " then it will return the total number of results that are available.\n",
+ .read = enum_result_read,
+};
+
+static struct ast_custom_function enum_function = {
+ .name = "ENUMLOOKUP",
+ .synopsis =
+ "General or specific querying of NAPTR records for ENUM or ENUM-like DNS pointers",
+ .syntax =
+ "ENUMLOOKUP(number[,Method-type[,options[,record#[,zone-suffix]]]])",
+ .desc =
+ "Option 'c' returns an integer count of the number of NAPTRs of a certain RR type.\n"
+ "Combination of 'c' and Method-type of 'ALL' will return a count of all NAPTRs for the record.\n"
+ "Defaults are: Method-type=sip, no options, record=1, zone-suffix=e164.arpa\n\n"
+ "For more information, see doc/asterisk.pdf",
+ .read = function_enum,
+};
+
+static int function_txtcidname(struct ast_channel *chan, const char *cmd,
+ char *data, char *buf, size_t len)
+{
+ int res;
+ char tech[80];
+ char txt[256] = "";
+ char dest[80];
+
+ buf[0] = '\0';
+
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "TXTCIDNAME requires an argument (number)\n");
+ return -1;
+ }
+
+ res = ast_get_txt(chan, data, dest, sizeof(dest), tech, sizeof(tech), txt,
+ sizeof(txt));
+
+ if (!ast_strlen_zero(txt))
+ ast_copy_string(buf, txt, len);
+
+ return 0;
+}
+
+static struct ast_custom_function txtcidname_function = {
+ .name = "TXTCIDNAME",
+ .synopsis = "TXTCIDNAME looks up a caller name via DNS",
+ .syntax = "TXTCIDNAME(<number>)",
+ .desc =
+ "This function looks up the given phone number in DNS to retrieve\n"
+ "the caller id name. The result will either be blank or be the value\n"
+ "found in the TXT record in DNS.\n",
+ .read = function_txtcidname,
+};
+
+static int unload_module(void)
+{
+ int res = 0;
+
+ res |= ast_custom_function_unregister(&enum_result_function);
+ res |= ast_custom_function_unregister(&enum_query_function);
+ res |= ast_custom_function_unregister(&enum_function);
+ res |= ast_custom_function_unregister(&txtcidname_function);
+
+ return res;
+}
+
+static int load_module(void)
+{
+ int res = 0;
+
+ res |= ast_custom_function_register(&enum_result_function);
+ res |= ast_custom_function_register(&enum_query_function);
+ res |= ast_custom_function_register(&enum_function);
+ res |= ast_custom_function_register(&txtcidname_function);
+
+ return res;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "ENUM related dialplan functions");
diff --git a/trunk/funcs/func_env.c b/trunk/funcs/func_env.c
new file mode 100644
index 000000000..4eba211bb
--- /dev/null
+++ b/trunk/funcs/func_env.c
@@ -0,0 +1,215 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2006, Digium, Inc.
+ *
+ * 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 Environment related dialplan functions
+ *
+ * \ingroup functions
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <sys/stat.h>
+
+#include "asterisk/module.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/utils.h"
+#include "asterisk/app.h"
+
+static int env_read(struct ast_channel *chan, const char *cmd, char *data,
+ char *buf, size_t len)
+{
+ char *ret = NULL;
+
+ *buf = '\0';
+
+ if (data)
+ ret = getenv(data);
+
+ if (ret)
+ ast_copy_string(buf, ret, len);
+
+ return 0;
+}
+
+static int env_write(struct ast_channel *chan, const char *cmd, char *data,
+ const char *value)
+{
+ if (!ast_strlen_zero(data)) {
+ if (!ast_strlen_zero(value)) {
+ setenv(data, value, 1);
+ } else {
+ unsetenv(data);
+ }
+ }
+
+ return 0;
+}
+
+static int stat_read(struct ast_channel *chan, const char *cmd, char *data,
+ char *buf, size_t len)
+{
+ char *action;
+ struct stat s;
+
+ ast_copy_string(buf, "0", len);
+
+ action = strsep(&data, ",");
+ if (stat(data, &s)) {
+ return 0;
+ } else {
+ switch (*action) {
+ case 'e':
+ strcpy(buf, "1");
+ break;
+ case 's':
+ snprintf(buf, len, "%d", (unsigned int) s.st_size);
+ break;
+ case 'f':
+ snprintf(buf, len, "%d", S_ISREG(s.st_mode) ? 1 : 0);
+ break;
+ case 'd':
+ snprintf(buf, len, "%d", S_ISDIR(s.st_mode) ? 1 : 0);
+ break;
+ case 'M':
+ snprintf(buf, len, "%d", (int) s.st_mtime);
+ break;
+ case 'A':
+ snprintf(buf, len, "%d", (int) s.st_mtime);
+ break;
+ case 'C':
+ snprintf(buf, len, "%d", (int) s.st_ctime);
+ break;
+ case 'm':
+ snprintf(buf, len, "%o", (int) s.st_mode);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int file_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
+{
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(filename);
+ AST_APP_ARG(offset);
+ AST_APP_ARG(length);
+ );
+ int offset = 0, length;
+ char *contents;
+
+ AST_STANDARD_APP_ARGS(args, data);
+ if (args.argc > 1)
+ offset = atoi(args.offset);
+
+ if (args.argc > 2) {
+ if ((length = atoi(args.length)) < 1) {
+ ast_log(LOG_WARNING, "Invalid length '%s'. Returning the max (%d)\n", args.length, (int)len);
+ length = len;
+ } else if (length > len) {
+ ast_log(LOG_WARNING, "Length %d is greater than the max (%d). Truncating output.\n", length, (int)len);
+ length = len;
+ }
+ } else
+ length = len;
+
+ if (!(contents = ast_read_textfile(args.filename)))
+ return -1;
+
+ if (offset >= 0)
+ ast_copy_string(buf, &contents[offset], length);
+ else {
+ size_t tmp = strlen(contents);
+ if (offset * -1 > tmp) {
+ ast_log(LOG_WARNING, "Offset is larger than the file size.\n");
+ offset = tmp * -1;
+ }
+ ast_copy_string(buf, &contents[tmp + offset], length);
+ }
+ ast_free(contents);
+
+ return 0;
+}
+
+static struct ast_custom_function env_function = {
+ .name = "ENV",
+ .synopsis = "Gets or sets the environment variable specified",
+ .syntax = "ENV(<envname>)",
+ .read = env_read,
+ .write = env_write,
+};
+
+static struct ast_custom_function stat_function = {
+ .name = "STAT",
+ .synopsis = "Does a check on the specified file",
+ .syntax = "STAT(<flag>,<filename>)",
+ .read = stat_read,
+ .desc =
+ "flag may be one of the following:\n"
+ " d - Checks if the file is a directory\n"
+ " e - Checks if the file exists\n"
+ " f - Checks if the file is a regular file\n"
+ " m - Returns the file mode (in octal)\n"
+ " s - Returns the size (in bytes) of the file\n"
+ " A - Returns the epoch at which the file was last accessed\n"
+ " C - Returns the epoch at which the inode was last changed\n"
+ " M - Returns the epoch at which the file was last modified\n",
+};
+
+static struct ast_custom_function file_function = {
+ .name = "FILE",
+ .synopsis = "Obtains the contents of a file",
+ .syntax = "FILE(<filename>,<offset>,<length>)",
+ .read = file_read,
+ /*
+ * Some enterprising programmer could probably add write functionality
+ * to FILE(), although I'm not sure how useful it would be. Hence why
+ * it's called FILE and not READFILE (like the app was).
+ */
+ .desc =
+"<offset> may be specified as any number. If negative, <offset> specifies\n"
+" the number of bytes back from the end of the file.\n"
+"<length>, if specified, will limit the length of the data read to that size.\n",
+};
+
+static int unload_module(void)
+{
+ int res = 0;
+
+ res |= ast_custom_function_unregister(&env_function);
+ res |= ast_custom_function_unregister(&stat_function);
+ res |= ast_custom_function_unregister(&file_function);
+
+ return res;
+}
+
+static int load_module(void)
+{
+ int res = 0;
+
+ res |= ast_custom_function_register(&env_function);
+ res |= ast_custom_function_register(&stat_function);
+ res |= ast_custom_function_register(&file_function);
+
+ return res;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Environment/filesystem dialplan functions");
diff --git a/trunk/funcs/func_extstate.c b/trunk/funcs/func_extstate.c
new file mode 100644
index 000000000..8866d89b9
--- /dev/null
+++ b/trunk/funcs/func_extstate.c
@@ -0,0 +1,133 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2007, Digium, Inc.
+ *
+ * Modified from func_devstate.c by Russell Bryant <russell@digium.com>
+ * Adam Gundy <adam@starsilk.net>
+
+ * 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 Get the state of a hinted extension for dialplan control
+ *
+ * \author Adam Gundy <adam@starsilk.net>
+ *
+ * \ingroup functions
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/module.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/utils.h"
+#include "asterisk/devicestate.h"
+
+static const char *ast_extstate_str(int state)
+{
+ const char *res = "UNKNOWN";
+
+ switch (state) {
+ case AST_EXTENSION_NOT_INUSE:
+ res = "NOT_INUSE";
+ break;
+ case AST_EXTENSION_INUSE:
+ res = "INUSE";
+ break;
+ case AST_EXTENSION_BUSY:
+ res = "BUSY";
+ break;
+ case AST_EXTENSION_UNAVAILABLE:
+ res = "UNAVAILABLE";
+ break;
+ case AST_EXTENSION_RINGING:
+ res = "RINGING";
+ break;
+ case AST_EXTENSION_INUSE | AST_EXTENSION_RINGING:
+ res = "RINGINUSE";
+ break;
+ case AST_EXTENSION_INUSE | AST_EXTENSION_ONHOLD:
+ res = "HOLDINUSE";
+ break;
+ case AST_EXTENSION_ONHOLD:
+ res = "ONHOLD";
+ break;
+ }
+
+ return res;
+}
+
+static int extstate_read(struct ast_channel *chan, const char *cmd, char *data,
+ char *buf, size_t len)
+{
+ char *exten, *context;
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "EXTENSION_STATE requires an extension\n");
+ return -1;
+ }
+
+ context = exten = data;
+ strsep(&context, "@");
+ if (ast_strlen_zero(context))
+ context = "default";
+
+ if (ast_strlen_zero(exten)) {
+ ast_log(LOG_WARNING, "EXTENSION_STATE requires an extension\n");
+ return -1;
+ }
+
+ ast_copy_string(buf,
+ ast_extstate_str(ast_extension_state(chan, context, exten)), len);
+
+ return 0;
+}
+
+static struct ast_custom_function extstate_function = {
+ .name = "EXTENSION_STATE",
+ .synopsis = "Get an extension's state",
+ .syntax = "EXTENSION_STATE(extension[@context])",
+ .desc =
+ " The EXTENSION_STATE function can be used to retrieve the state from any\n"
+ "hinted extension. For example:\n"
+ " NoOp(1234@default has state ${EXTENSION_STATE(1234)})\n"
+ " NoOp(4567@home has state ${EXTENSION_STATE(4567@home)})\n"
+ "\n"
+ " The possible values returned by this function are:\n"
+ "UNKNOWN | NOT_INUSE | INUSE | BUSY | INVALID | UNAVAILABLE | RINGING\n"
+ "RINGINUSE | HOLDINUSE | ONHOLD\n",
+ .read = extstate_read,
+};
+
+static int unload_module(void)
+{
+ int res;
+
+ res = ast_custom_function_unregister(&extstate_function);
+
+ return res;
+}
+
+static int load_module(void)
+{
+ int res;
+
+ res = ast_custom_function_register(&extstate_function);
+
+ return res ? AST_MODULE_LOAD_DECLINE : AST_MODULE_LOAD_SUCCESS;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Gets an extension's state in the dialplan");
diff --git a/trunk/funcs/func_global.c b/trunk/funcs/func_global.c
new file mode 100644
index 000000000..1f0053d94
--- /dev/null
+++ b/trunk/funcs/func_global.c
@@ -0,0 +1,82 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2006, Tilghman Lesher
+ *
+ * Tilghman Lesher <func_global__200605@the-tilghman.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 Global variable dialplan functions
+ *
+ * \author Tilghman Lesher <func_global__200605@the-tilghman.com>
+ *
+ * \ingroup functions
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <sys/stat.h>
+
+#include "asterisk/module.h"
+#include "asterisk/pbx.h"
+
+static int global_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
+{
+ const char *var = pbx_builtin_getvar_helper(NULL, data);
+
+ *buf = '\0';
+
+ if (var)
+ ast_copy_string(buf, var, len);
+
+ return 0;
+}
+
+static int global_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
+{
+ pbx_builtin_setvar_helper(NULL, data, value);
+
+ return 0;
+}
+
+static struct ast_custom_function global_function = {
+ .name = "GLOBAL",
+ .synopsis = "Gets or sets the global variable specified",
+ .syntax = "GLOBAL(<varname>)",
+ .read = global_read,
+ .write = global_write,
+};
+
+static int unload_module(void)
+{
+ int res = 0;
+
+ res |= ast_custom_function_unregister(&global_function);
+
+ return res;
+}
+
+static int load_module(void)
+{
+ int res = 0;
+
+ res |= ast_custom_function_register(&global_function);
+
+ return res;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Global variable dialplan functions");
diff --git a/trunk/funcs/func_groupcount.c b/trunk/funcs/func_groupcount.c
new file mode 100644
index 000000000..f0d7df1b5
--- /dev/null
+++ b/trunk/funcs/func_groupcount.c
@@ -0,0 +1,231 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2006, Digium, Inc.
+ *
+ * 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 Channel group related dialplan functions
+ *
+ * \ingroup functions
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/module.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/utils.h"
+#include "asterisk/app.h"
+
+static int group_count_function_read(struct ast_channel *chan, const char *cmd,
+ char *data, char *buf, size_t len)
+{
+ int count = -1;
+ char group[80] = "", category[80] = "";
+
+ ast_app_group_split_group(data, group, sizeof(group), category,
+ sizeof(category));
+
+ /* If no group has been provided let's find one */
+ if (ast_strlen_zero(group)) {
+ struct ast_group_info *gi = NULL;
+
+ ast_app_group_list_rdlock();
+ for (gi = ast_app_group_list_head(); gi; gi = AST_LIST_NEXT(gi, list)) {
+ if (gi->chan != chan)
+ continue;
+ if (ast_strlen_zero(category) || (!ast_strlen_zero(gi->category) && !strcasecmp(gi->category, category)))
+ break;
+ }
+ if (gi) {
+ ast_copy_string(group, gi->group, sizeof(group));
+ if (!ast_strlen_zero(gi->category))
+ ast_copy_string(category, gi->category, sizeof(category));
+ }
+ ast_app_group_list_unlock();
+ }
+
+ if ((count = ast_app_group_get_count(group, category)) == -1)
+ ast_log(LOG_NOTICE, "No group could be found for channel '%s'\n", chan->name);
+ else
+ snprintf(buf, len, "%d", count);
+
+ return 0;
+}
+
+static struct ast_custom_function group_count_function = {
+ .name = "GROUP_COUNT",
+ .syntax = "GROUP_COUNT([groupname][@category])",
+ .synopsis = "Counts the number of channels in the specified group",
+ .desc =
+ "Calculates the group count for the specified group, or uses the\n"
+ "channel's current group if not specifed (and non-empty).\n",
+ .read = group_count_function_read,
+};
+
+static int group_match_count_function_read(struct ast_channel *chan,
+ const char *cmd, char *data, char *buf,
+ size_t len)
+{
+ int count;
+ char group[80] = "";
+ char category[80] = "";
+
+ ast_app_group_split_group(data, group, sizeof(group), category,
+ sizeof(category));
+
+ if (!ast_strlen_zero(group)) {
+ count = ast_app_group_match_get_count(group, category);
+ snprintf(buf, len, "%d", count);
+ }
+
+ return 0;
+}
+
+static struct ast_custom_function group_match_count_function = {
+ .name = "GROUP_MATCH_COUNT",
+ .syntax = "GROUP_MATCH_COUNT(groupmatch[@category])",
+ .synopsis =
+ "Counts the number of channels in the groups matching the specified pattern",
+ .desc =
+ "Calculates the group count for all groups that match the specified pattern.\n"
+ "Uses standard regular expression matching (see regex(7)).\n",
+ .read = group_match_count_function_read,
+ .write = NULL,
+};
+
+static int group_function_read(struct ast_channel *chan, const char *cmd,
+ char *data, char *buf, size_t len)
+{
+ struct ast_group_info *gi = NULL;
+
+ ast_app_group_list_rdlock();
+
+ for (gi = ast_app_group_list_head(); gi; gi = AST_LIST_NEXT(gi, list)) {
+ if (gi->chan != chan)
+ continue;
+ if (ast_strlen_zero(data))
+ break;
+ if (!ast_strlen_zero(gi->category) && !strcasecmp(gi->category, data))
+ break;
+ }
+
+ if (gi)
+ ast_copy_string(buf, gi->group, len);
+
+ ast_app_group_list_unlock();
+
+ return 0;
+}
+
+static int group_function_write(struct ast_channel *chan, const char *cmd,
+ char *data, const char *value)
+{
+ char grpcat[256];
+
+ if (!ast_strlen_zero(data)) {
+ snprintf(grpcat, sizeof(grpcat), "%s@%s", value, data);
+ } else {
+ ast_copy_string(grpcat, value, sizeof(grpcat));
+ }
+
+ if (ast_app_group_set_channel(chan, grpcat))
+ ast_log(LOG_WARNING,
+ "Setting a group requires an argument (group name)\n");
+
+ return 0;
+}
+
+static struct ast_custom_function group_function = {
+ .name = "GROUP",
+ .syntax = "GROUP([category])",
+ .synopsis = "Gets or sets the channel group.",
+ .desc = "Gets or sets the channel group.\n",
+ .read = group_function_read,
+ .write = group_function_write,
+};
+
+static int group_list_function_read(struct ast_channel *chan, const char *cmd,
+ char *data, char *buf, size_t len)
+{
+ struct ast_group_info *gi = NULL;
+ char tmp1[1024] = "";
+ char tmp2[1024] = "";
+
+ if (!chan)
+ return -1;
+
+ ast_app_group_list_rdlock();
+
+ for (gi = ast_app_group_list_head(); gi; gi = AST_LIST_NEXT(gi, list)) {
+ if (gi->chan != chan)
+ continue;
+ if (!ast_strlen_zero(tmp1)) {
+ ast_copy_string(tmp2, tmp1, sizeof(tmp2));
+ if (!ast_strlen_zero(gi->category))
+ snprintf(tmp1, sizeof(tmp1), "%s %s@%s", tmp2, gi->group, gi->category);
+ else
+ snprintf(tmp1, sizeof(tmp1), "%s %s", tmp2, gi->group);
+ } else {
+ if (!ast_strlen_zero(gi->category))
+ snprintf(tmp1, sizeof(tmp1), "%s@%s", gi->group, gi->category);
+ else
+ snprintf(tmp1, sizeof(tmp1), "%s", gi->group);
+ }
+ }
+
+ ast_app_group_list_unlock();
+
+ ast_copy_string(buf, tmp1, len);
+
+ return 0;
+}
+
+static struct ast_custom_function group_list_function = {
+ .name = "GROUP_LIST",
+ .syntax = "GROUP_LIST()",
+ .synopsis = "Gets a list of the groups set on a channel.",
+ .desc = "Gets a list of the groups set on a channel.\n",
+ .read = group_list_function_read,
+ .write = NULL,
+};
+
+static int unload_module(void)
+{
+ int res = 0;
+
+ res |= ast_custom_function_unregister(&group_count_function);
+ res |= ast_custom_function_unregister(&group_match_count_function);
+ res |= ast_custom_function_unregister(&group_list_function);
+ res |= ast_custom_function_unregister(&group_function);
+
+ return res;
+}
+
+static int load_module(void)
+{
+ int res = 0;
+
+ res |= ast_custom_function_register(&group_count_function);
+ res |= ast_custom_function_register(&group_match_count_function);
+ res |= ast_custom_function_register(&group_list_function);
+ res |= ast_custom_function_register(&group_function);
+
+ return res;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Channel group dialplan functions");
diff --git a/trunk/funcs/func_iconv.c b/trunk/funcs/func_iconv.c
new file mode 100644
index 000000000..74f32412b
--- /dev/null
+++ b/trunk/funcs/func_iconv.c
@@ -0,0 +1,125 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (c) 2005,2006,2007 Sven Slezak <sunny@mezzo.net>
+ *
+ * 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 Charset conversions
+ *
+ * \author Sven Slezak <sunny@mezzo.net>
+ *
+ * \ingroup functions
+ */
+
+/*** MODULEINFO
+ <depend>iconv</depend>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <ctype.h>
+#include <iconv.h>
+
+#include "asterisk/module.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/utils.h"
+#include "asterisk/app.h"
+
+/*!
+ * Some systems define the second arg to iconv() as (const char *),
+ * while others define it as (char *). Cast it to a (void *) to
+ * suppress compiler warnings about it.
+ */
+#define AST_ICONV_CAST void *
+
+static int iconv_read(struct ast_channel *chan, const char *cmd, char *arguments, char *buf, size_t len)
+{
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(in_charset);
+ AST_APP_ARG(out_charset);
+ AST_APP_ARG(text);
+ );
+ iconv_t cd;
+ size_t incount, outcount = len;
+ char *parse;
+
+ if (ast_strlen_zero(arguments)) {
+ ast_log(LOG_WARNING, "Syntax: ICONV(<in-charset>,<out-charset>,<text>) - missing arguments!\n");
+ return -1;
+ }
+
+ parse = ast_strdupa(arguments);
+ AST_STANDARD_APP_ARGS(args, parse);
+
+ if (args.argc < 3) {
+ ast_log(LOG_WARNING, "Syntax: ICONV(<in-charset>,<out-charset>,<text>) %d\n", args.argc);
+ return -1;
+ }
+
+ incount = strlen(args.text);
+
+ ast_debug(1, "Iconv: \"%s\" %s -> %s\n", args.text, args.in_charset, args.out_charset);
+
+ cd = iconv_open(args.out_charset, args.in_charset);
+
+ if (cd == (iconv_t) -1) {
+ ast_log(LOG_ERROR, "conversion from '%s' to '%s' not available. type 'iconv -l' in a shell to list the supported charsets.\n", args.in_charset, args.out_charset);
+ return -1;
+ }
+
+ if (iconv(cd, (AST_ICONV_CAST) &args.text, &incount, &buf, &outcount) == (size_t) -1) {
+ if (errno == E2BIG)
+ ast_log(LOG_WARNING, "Iconv: output buffer too small.\n");
+ else if (errno == EILSEQ)
+ ast_log(LOG_WARNING, "Iconv: illegal character.\n");
+ else if (errno == EINVAL)
+ ast_log(LOG_WARNING, "Iconv: incomplete character sequence.\n");
+ else
+ ast_log(LOG_WARNING, "Iconv: error %d: %s.\n", errno, strerror(errno));
+ }
+ iconv_close(cd);
+
+ return 0;
+}
+
+
+static struct ast_custom_function iconv_function = {
+ .name = "ICONV",
+ .synopsis = "Converts charsets of strings.",
+ .desc =
+"Converts string from in-charset into out-charset. For available charsets,\n"
+"use 'iconv -l' on your shell command line.\n"
+"Note: due to limitations within the API, ICONV will not currently work with\n"
+"charsets with embedded NULLs. If found, the string will terminate.\n",
+ .syntax = "ICONV(in-charset,out-charset,string)",
+ .read = iconv_read,
+};
+
+static int unload_module(void)
+{
+ return ast_custom_function_unregister(&iconv_function);
+}
+
+static int load_module(void)
+{
+ return ast_custom_function_register(&iconv_function);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Charset conversions");
+
diff --git a/trunk/funcs/func_lock.c b/trunk/funcs/func_lock.c
new file mode 100644
index 000000000..53b05a3e8
--- /dev/null
+++ b/trunk/funcs/func_lock.c
@@ -0,0 +1,350 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2007, Tilghman Lesher
+ *
+ * Tilghman Lesher <func_lock_2007@the-tilghman.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 Dialplan mutexes
+ *
+ * \author Tilghman Lesher <func_lock_2007@the-tilghman.com>
+ *
+ * \ingroup functions
+ *
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/linkedlists.h"
+
+AST_LIST_HEAD_STATIC(locklist, lock_frame);
+
+static void lock_free(void *data);
+static int unloading = 0;
+
+static struct ast_datastore_info lock_info = {
+ .type = "MUTEX",
+ .destroy = lock_free,
+};
+
+struct lock_frame {
+ AST_LIST_ENTRY(lock_frame) entries;
+ ast_mutex_t mutex;
+ /*! count is needed so if a recursive mutex exits early, we know how many times to unlock it. */
+ unsigned int count;
+ /*! who owns us */
+ struct ast_channel *channel;
+ /*! name of the lock */
+ char name[0];
+};
+
+struct channel_lock_frame {
+ AST_LIST_ENTRY(channel_lock_frame) list;
+ /*! Need to save channel pointer here, because during destruction, we won't have it. */
+ struct ast_channel *channel;
+ struct lock_frame *lock_frame;
+};
+
+static void lock_free(void *data)
+{
+ AST_LIST_HEAD(, channel_lock_frame) *oldlist = data;
+ struct channel_lock_frame *clframe;
+ AST_LIST_LOCK(oldlist);
+ while ((clframe = AST_LIST_REMOVE_HEAD(oldlist, list))) {
+ /* Only unlock if we own the lock */
+ if (clframe->channel == clframe->lock_frame->channel) {
+ clframe->lock_frame->channel = NULL;
+ while (clframe->lock_frame->count > 0) {
+ clframe->lock_frame->count--;
+ ast_mutex_unlock(&clframe->lock_frame->mutex);
+ }
+ }
+ ast_free(clframe);
+ }
+ AST_LIST_UNLOCK(oldlist);
+ AST_LIST_HEAD_DESTROY(oldlist);
+ ast_free(oldlist);
+}
+
+static int get_lock(struct ast_channel *chan, char *lockname, int try)
+{
+ struct ast_datastore *lock_store = ast_channel_datastore_find(chan, &lock_info, NULL);
+ struct lock_frame *current;
+ struct channel_lock_frame *clframe = NULL, *save_clframe = NULL;
+ AST_LIST_HEAD(, channel_lock_frame) *list;
+ int res, count_channel_locks = 0;
+
+ if (!lock_store) {
+ ast_debug(1, "Channel %s has no lock datastore, so we're allocating one.\n", chan->name);
+ lock_store = ast_channel_datastore_alloc(&lock_info, NULL);
+ if (!lock_store) {
+ ast_log(LOG_ERROR, "Unable to allocate new datastore. No locks will be obtained.\n");
+ return -1;
+ }
+
+ list = ast_calloc(1, sizeof(*list));
+ if (!list) {
+ ast_log(LOG_ERROR, "Unable to allocate datastore list head. %sLOCK will fail.\n", try ? "TRY" : "");
+ ast_channel_datastore_free(lock_store);
+ return -1;
+ }
+
+ lock_store->data = list;
+ AST_LIST_HEAD_INIT(list);
+ ast_channel_datastore_add(chan, lock_store);
+ } else
+ list = lock_store->data;
+
+ /* Lock already exists? */
+ AST_LIST_LOCK(&locklist);
+ AST_LIST_TRAVERSE(&locklist, current, entries) {
+ if (strcmp(current->name, lockname) == 0) {
+ break;
+ }
+ }
+
+ if (!current) {
+ if (unloading) {
+ /* Don't bother */
+ AST_LIST_UNLOCK(&locklist);
+ return -1;
+ }
+
+ /* Create new lock entry */
+ current = ast_calloc(1, sizeof(*current) + strlen(lockname) + 1);
+ if (!current) {
+ AST_LIST_UNLOCK(&locklist);
+ return -1;
+ }
+
+ strcpy((char *)current + sizeof(*current), lockname);
+ ast_mutex_init(&current->mutex);
+ AST_LIST_INSERT_TAIL(&locklist, current, entries);
+ }
+ AST_LIST_UNLOCK(&locklist);
+
+ /* Found lock or created one - now find or create the corresponding link in the channel */
+ AST_LIST_LOCK(list);
+ AST_LIST_TRAVERSE(list, clframe, list) {
+ if (clframe->lock_frame == current)
+ save_clframe = clframe;
+
+ /* Only count mutexes that we currently hold */
+ if (clframe->lock_frame->channel == chan)
+ count_channel_locks++;
+ }
+
+ if (save_clframe) {
+ clframe = save_clframe;
+ } else {
+ if (unloading) {
+ /* Don't bother */
+ AST_LIST_UNLOCK(list);
+ return -1;
+ }
+
+ clframe = ast_calloc(1, sizeof(*clframe));
+ if (!clframe) {
+ ast_log(LOG_ERROR, "Unable to allocate channel lock frame. %sLOCK will fail.\n", try ? "TRY" : "");
+ AST_LIST_UNLOCK(list);
+ return -1;
+ }
+
+ clframe->lock_frame = current;
+ clframe->channel = chan;
+ /* Count the lock just created */
+ count_channel_locks++;
+ AST_LIST_INSERT_TAIL(list, clframe, list);
+ }
+ AST_LIST_UNLOCK(list);
+
+ /* Okay, we have both frames, so now we need to try to lock the mutex. */
+ if (count_channel_locks > 1) {
+ struct timeval start = ast_tvnow();
+ for (;;) {
+ if ((res = ast_mutex_trylock(&current->mutex)) == 0)
+ break;
+ if (ast_tvdiff_ms(ast_tvnow(), start) > 3000)
+ break; /* bail after 3 seconds of waiting */
+ usleep(1);
+ }
+ } else {
+ /* If the channel doesn't have any locks so far, then there's no possible deadlock. */
+ res = try ? ast_mutex_trylock(&current->mutex) : ast_mutex_lock(&current->mutex);
+ }
+
+ if (res == 0) {
+ current->count++;
+ current->channel = chan;
+ }
+
+ return res;
+}
+
+static int unlock_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
+{
+ struct ast_datastore *lock_store = ast_channel_datastore_find(chan, &lock_info, NULL);
+ struct channel_lock_frame *clframe;
+ AST_LIST_HEAD(, channel_lock_frame) *list;
+
+ if (!lock_store) {
+ ast_log(LOG_WARNING, "No datastore for dialplan locks. Nothing was ever locked!\n");
+ ast_copy_string(buf, "0", len);
+ return 0;
+ }
+
+ if (!(list = lock_store->data)) {
+ ast_debug(1, "This should NEVER happen\n");
+ ast_copy_string(buf, "0", len);
+ return 0;
+ }
+
+ /* Find item in the channel list */
+ AST_LIST_LOCK(list);
+ AST_LIST_TRAVERSE(list, clframe, list) {
+ if (clframe->lock_frame && clframe->lock_frame->channel == chan && strcmp(clframe->lock_frame->name, data) == 0) {
+ break;
+ }
+ }
+ /* We never destroy anything until channel destruction, which will never
+ * happen while this routine is executing, so we don't need to hold the
+ * lock beyond this point. */
+ AST_LIST_UNLOCK(list);
+
+ if (!clframe) {
+ /* We didn't have this lock in the first place */
+ ast_copy_string(buf, "0", len);
+ return 0;
+ }
+
+ /* Decrement before we release, because if a channel is waiting on the
+ * mutex, there's otherwise a race to alter count. */
+ clframe->lock_frame->count--;
+ /* If we get another lock, this one shouldn't count against us for deadlock avoidance. */
+ clframe->lock_frame->channel = NULL;
+ ast_mutex_unlock(&clframe->lock_frame->mutex);
+
+ ast_copy_string(buf, "1", len);
+ return 0;
+}
+
+static int lock_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
+{
+ if (chan)
+ ast_autoservice_start(chan);
+
+ ast_copy_string(buf, get_lock(chan, data, 0) ? "0" : "1", len);
+
+ if (chan)
+ ast_autoservice_stop(chan);
+
+ return 0;
+}
+
+static int trylock_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
+{
+ if (chan)
+ ast_autoservice_start(chan);
+
+ ast_copy_string(buf, get_lock(chan, data, 1) ? "0" : "1", len);
+
+ if (chan)
+ ast_autoservice_stop(chan);
+
+ return 0;
+}
+
+static struct ast_custom_function lock_function = {
+ .name = "LOCK",
+ .synopsis = "Attempt to obtain a named mutex",
+ .desc =
+"Attempts to grab a named lock exclusively, and prevents other channels from\n"
+"obtaining the same lock. LOCK will wait for the lock to become available.\n"
+"Returns 1 if the lock was obtained or 0 on error.\n\n"
+"Note: to avoid the possibility of a deadlock, LOCK will only attempt to\n"
+"obtain the lock for 3 seconds if the channel already has another lock.\n",
+ .syntax = "LOCK(<lockname>)",
+ .read = lock_read,
+};
+
+static struct ast_custom_function trylock_function = {
+ .name = "TRYLOCK",
+ .synopsis = "Attempt to obtain a named mutex",
+ .desc =
+"Attempts to grab a named lock exclusively, and prevents other channels\n"
+"from obtaining the same lock. Returns 1 if the lock was available or 0\n"
+"otherwise.\n",
+ .syntax = "TRYLOCK(<lockname>)",
+ .read = trylock_read,
+};
+
+static struct ast_custom_function unlock_function = {
+ .name = "UNLOCK",
+ .synopsis = "Unlocks a named mutex",
+ .desc =
+"Unlocks a previously locked mutex. Note that it is generally unnecessary to\n"
+"unlock in a hangup routine, as any locks held are automatically freed when the\n"
+"channel is destroyed. Returns 1 if the channel had a lock or 0 otherwise.\n",
+ .syntax = "UNLOCK(<lockname>)",
+ .read = unlock_read,
+};
+
+static int unload_module(void)
+{
+ struct lock_frame *current;
+
+ /* Module flag */
+ unloading = 1;
+
+ AST_LIST_LOCK(&locklist);
+ while ((current = AST_LIST_REMOVE_HEAD(&locklist, entries))) {
+ /* If any locks are currently in use, then we cannot unload this module */
+ if (current->channel) {
+ /* Put it back */
+ AST_LIST_INSERT_HEAD(&locklist, current, entries);
+ AST_LIST_UNLOCK(&locklist);
+ unloading = 0;
+ return -1;
+ }
+ ast_mutex_destroy(&current->mutex);
+ ast_free(current);
+ }
+
+ /* No locks left, unregister functions */
+ ast_custom_function_unregister(&lock_function);
+ ast_custom_function_unregister(&trylock_function);
+ ast_custom_function_unregister(&unlock_function);
+
+ AST_LIST_UNLOCK(&locklist);
+ return 0;
+}
+
+static int load_module(void)
+{
+ int res = ast_custom_function_register(&lock_function);
+ res |= ast_custom_function_register(&trylock_function);
+ res |= ast_custom_function_register(&unlock_function);
+ return res;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Dialplan mutexes");
diff --git a/trunk/funcs/func_logic.c b/trunk/funcs/func_logic.c
new file mode 100644
index 000000000..7fca070aa
--- /dev/null
+++ b/trunk/funcs/func_logic.c
@@ -0,0 +1,242 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2006, Digium, Inc.
+ * Portions Copyright (C) 2005, Anthony Minessale II
+ *
+ * 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 Conditional logic dialplan functions
+ *
+ * \author Anthony Minessale II
+ *
+ * \ingroup functions
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/module.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/utils.h"
+#include "asterisk/app.h"
+
+static int isnull(struct ast_channel *chan, const char *cmd, char *data,
+ char *buf, size_t len)
+{
+ strcpy(buf, data && *data ? "0" : "1");
+
+ return 0;
+}
+
+static int exists(struct ast_channel *chan, const char *cmd, char *data, char *buf,
+ size_t len)
+{
+ strcpy(buf, data && *data ? "1" : "0");
+
+ return 0;
+}
+
+static int iftime(struct ast_channel *chan, const char *cmd, char *data, char *buf,
+ size_t len)
+{
+ struct ast_timing timing;
+ char *expr;
+ char *iftrue;
+ char *iffalse;
+
+ data = ast_strip_quoted(data, "\"", "\"");
+ expr = strsep(&data, "?");
+ iftrue = strsep(&data, ":");
+ iffalse = data;
+
+ if (ast_strlen_zero(expr) || !(iftrue || iffalse)) {
+ ast_log(LOG_WARNING,
+ "Syntax IFTIME(<timespec>?[<true>][:<false>])\n");
+ return -1;
+ }
+
+ if (!ast_build_timing(&timing, expr)) {
+ ast_log(LOG_WARNING, "Invalid Time Spec.\n");
+ return -1;
+ }
+
+ if (iftrue)
+ iftrue = ast_strip_quoted(iftrue, "\"", "\"");
+ if (iffalse)
+ iffalse = ast_strip_quoted(iffalse, "\"", "\"");
+
+ ast_copy_string(buf, ast_check_timing(&timing) ? iftrue : iffalse, len);
+
+ return 0;
+}
+
+static int acf_if(struct ast_channel *chan, const char *cmd, char *data, char *buf,
+ size_t len)
+{
+ AST_DECLARE_APP_ARGS(args1,
+ AST_APP_ARG(expr);
+ AST_APP_ARG(remainder);
+ );
+ AST_DECLARE_APP_ARGS(args2,
+ AST_APP_ARG(iftrue);
+ AST_APP_ARG(iffalse);
+ );
+ args2.iftrue = args2.iffalse = NULL; /* you have to set these, because if there is nothing after the '?',
+ then args1.remainder will be NULL, not a pointer to a null string, and
+ then any garbage in args2.iffalse will not be cleared, and you'll crash.
+ -- and if you mod the ast_app_separate_args func instead, you'll really
+ mess things up badly, because the rest of everything depends on null args
+ for non-specified stuff. */
+
+ AST_NONSTANDARD_APP_ARGS(args1, data, '?');
+ AST_NONSTANDARD_APP_ARGS(args2, args1.remainder, ':');
+
+ if (ast_strlen_zero(args1.expr) || !(args2.iftrue || args2.iffalse)) {
+ ast_log(LOG_WARNING, "Syntax IF(<expr>?[<true>][:<false>]) (expr must be non-null, and either <true> or <false> must be non-null)\n");
+ ast_log(LOG_WARNING, " In this case, <expr>='%s', <true>='%s', and <false>='%s'\n", args1.expr, args2.iftrue, args2.iffalse);
+ return -1;
+ }
+
+ args1.expr = ast_strip(args1.expr);
+ if (args2.iftrue)
+ args2.iftrue = ast_strip(args2.iftrue);
+ if (args2.iffalse)
+ args2.iffalse = ast_strip(args2.iffalse);
+
+ ast_copy_string(buf, pbx_checkcondition(args1.expr) ? (S_OR(args2.iftrue, "")) : (S_OR(args2.iffalse, "")), len);
+
+ return 0;
+}
+
+static int set(struct ast_channel *chan, const char *cmd, char *data, char *buf,
+ size_t len)
+{
+ char *varname;
+ char *val;
+
+ varname = strsep(&data, "=");
+ val = data;
+
+ if (ast_strlen_zero(varname) || !val) {
+ ast_log(LOG_WARNING, "Syntax SET(<varname>=[<value>])\n");
+ return -1;
+ }
+
+ varname = ast_strip(varname);
+ val = ast_strip(val);
+ pbx_builtin_setvar_helper(chan, varname, val);
+ ast_copy_string(buf, val, len);
+
+ return 0;
+}
+
+static int acf_import(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
+{
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(channel);
+ AST_APP_ARG(varname);
+ );
+ AST_STANDARD_APP_ARGS(args, data);
+ buf[0] = 0;
+ if (!ast_strlen_zero(args.varname)) {
+ struct ast_channel *chan2 = ast_get_channel_by_name_locked(args.channel);
+ if (chan2) {
+ char *s = alloca(strlen(args.varname) + 4);
+ if (s) {
+ sprintf(s, "${%s}", args.varname);
+ pbx_substitute_variables_helper(chan2, s, buf, len);
+ }
+ ast_channel_unlock(chan2);
+ }
+ }
+ return 0;
+}
+
+static struct ast_custom_function isnull_function = {
+ .name = "ISNULL",
+ .synopsis = "NULL Test: Returns 1 if NULL or 0 otherwise",
+ .syntax = "ISNULL(<data>)",
+ .read = isnull,
+};
+
+static struct ast_custom_function set_function = {
+ .name = "SET",
+ .synopsis = "SET assigns a value to a channel variable",
+ .syntax = "SET(<varname>=[<value>])",
+ .read = set,
+};
+
+static struct ast_custom_function exists_function = {
+ .name = "EXISTS",
+ .synopsis = "Existence Test: Returns 1 if exists, 0 otherwise",
+ .syntax = "EXISTS(<data>)",
+ .read = exists,
+};
+
+static struct ast_custom_function if_function = {
+ .name = "IF",
+ .synopsis =
+ "Conditional: Returns the data following '?' if true, else the data following ':'",
+ .syntax = "IF(<expr>?[<true>][:<false>])",
+ .read = acf_if,
+};
+
+static struct ast_custom_function if_time_function = {
+ .name = "IFTIME",
+ .synopsis =
+ "Temporal Conditional: Returns the data following '?' if true, else the data following ':'",
+ .syntax = "IFTIME(<timespec>?[<true>][:<false>])",
+ .read = iftime,
+};
+
+static struct ast_custom_function import_function = {
+ .name = "IMPORT",
+ .synopsis =
+ "Retrieve the value of a variable from another channel\n",
+ .syntax = "IMPORT(channel,variable)",
+ .read = acf_import,
+};
+
+static int unload_module(void)
+{
+ int res = 0;
+
+ res |= ast_custom_function_unregister(&isnull_function);
+ res |= ast_custom_function_unregister(&set_function);
+ res |= ast_custom_function_unregister(&exists_function);
+ res |= ast_custom_function_unregister(&if_function);
+ res |= ast_custom_function_unregister(&if_time_function);
+ res |= ast_custom_function_unregister(&import_function);
+
+ return res;
+}
+
+static int load_module(void)
+{
+ int res = 0;
+
+ res |= ast_custom_function_register(&isnull_function);
+ res |= ast_custom_function_register(&set_function);
+ res |= ast_custom_function_register(&exists_function);
+ res |= ast_custom_function_register(&if_function);
+ res |= ast_custom_function_register(&if_time_function);
+ res |= ast_custom_function_register(&import_function);
+
+ return res;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Logical dialplan functions");
diff --git a/trunk/funcs/func_math.c b/trunk/funcs/func_math.c
new file mode 100644
index 000000000..45036d5ad
--- /dev/null
+++ b/trunk/funcs/func_math.c
@@ -0,0 +1,333 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2004 - 2006, Andy Powell
+ *
+ * Updated by Mark Spencer <markster@digium.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 Math related dialplan function
+ *
+ * \author Andy Powell
+ * \author Mark Spencer <markster@digium.com>
+ *
+ * \ingroup functions
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <math.h>
+
+#include "asterisk/module.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/utils.h"
+#include "asterisk/app.h"
+#include "asterisk/config.h"
+
+enum TypeOfFunctions {
+ ADDFUNCTION,
+ DIVIDEFUNCTION,
+ MULTIPLYFUNCTION,
+ SUBTRACTFUNCTION,
+ MODULUSFUNCTION,
+ POWFUNCTION,
+ SHLEFTFUNCTION,
+ SHRIGHTFUNCTION,
+ BITWISEANDFUNCTION,
+ BITWISEXORFUNCTION,
+ BITWISEORFUNCTION,
+ GTFUNCTION,
+ LTFUNCTION,
+ GTEFUNCTION,
+ LTEFUNCTION,
+ EQFUNCTION
+};
+
+enum TypeOfResult {
+ FLOAT_RESULT,
+ INT_RESULT,
+ HEX_RESULT,
+ CHAR_RESULT
+};
+
+static int math(struct ast_channel *chan, const char *cmd, char *parse,
+ char *buf, size_t len)
+{
+ double fnum1;
+ double fnum2;
+ double ftmp = 0;
+ char *op;
+ int iaction = -1;
+ int type_of_result = FLOAT_RESULT;
+ char *mvalue1, *mvalue2 = NULL, *mtype_of_result;
+ int negvalue1 = 0;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(argv0);
+ AST_APP_ARG(argv1);
+ );
+
+ if (ast_strlen_zero(parse)) {
+ ast_log(LOG_WARNING, "Syntax: Math(<number1><op><number 2>[,<type_of_result>]) - missing argument!\n");
+ return -1;
+ }
+
+ AST_STANDARD_APP_ARGS(args, parse);
+
+ if (args.argc < 1) {
+ ast_log(LOG_WARNING, "Syntax: Math(<number1><op><number 2>[,<type_of_result>]) - missing argument!\n");
+ return -1;
+ }
+
+ mvalue1 = args.argv0;
+
+ if (mvalue1[0] == '-') {
+ negvalue1 = 1;
+ mvalue1++;
+ }
+
+ if ((op = strchr(mvalue1, '*'))) {
+ iaction = MULTIPLYFUNCTION;
+ *op = '\0';
+ } else if ((op = strchr(mvalue1, '/'))) {
+ iaction = DIVIDEFUNCTION;
+ *op = '\0';
+ } else if ((op = strchr(mvalue1, '%'))) {
+ iaction = MODULUSFUNCTION;
+ *op = '\0';
+ } else if ((op = strchr(mvalue1, '^'))) {
+ iaction = POWFUNCTION;
+ *op = '\0';
+ } else if ((op = strstr(mvalue1, "AND"))) {
+ iaction = BITWISEANDFUNCTION;
+ op += 3;
+ *op = '\0';
+ } else if ((op = strstr(mvalue1, "XOR"))) {
+ iaction = BITWISEXORFUNCTION;
+ op += 3;
+ *op = '\0';
+ } else if ((op = strstr(mvalue1, "OR"))) {
+ iaction = BITWISEORFUNCTION;
+ op += 2;
+ *op = '\0';
+ } else if ((op = strchr(mvalue1, '>'))) {
+ iaction = GTFUNCTION;
+ *op = '\0';
+ if (*(op + 1) == '=') {
+ *++op = '\0';
+ iaction = GTEFUNCTION;
+ } else if (*(op + 1) == '>') {
+ *++op = '\0';
+ iaction = SHRIGHTFUNCTION;
+ }
+ } else if ((op = strchr(mvalue1, '<'))) {
+ iaction = LTFUNCTION;
+ *op = '\0';
+ if (*(op + 1) == '=') {
+ *++op = '\0';
+ iaction = LTEFUNCTION;
+ } else if (*(op + 1) == '<') {
+ *++op = '\0';
+ iaction = SHLEFTFUNCTION;
+ }
+ } else if ((op = strchr(mvalue1, '='))) {
+ *op = '\0';
+ if (*(op + 1) == '=') {
+ *++op = '\0';
+ iaction = EQFUNCTION;
+ } else
+ op = NULL;
+ } else if ((op = strchr(mvalue1, '+'))) {
+ iaction = ADDFUNCTION;
+ *op = '\0';
+ } else if ((op = strchr(mvalue1, '-'))) { /* subtraction MUST always be last, in case we have a negative first number */
+ iaction = SUBTRACTFUNCTION;
+ *op = '\0';
+ }
+
+ if (op)
+ mvalue2 = op + 1;
+
+ /* detect wanted type of result */
+ mtype_of_result = args.argv1;
+ if (mtype_of_result) {
+ if (!strcasecmp(mtype_of_result, "float")
+ || !strcasecmp(mtype_of_result, "f"))
+ type_of_result = FLOAT_RESULT;
+ else if (!strcasecmp(mtype_of_result, "int")
+ || !strcasecmp(mtype_of_result, "i"))
+ type_of_result = INT_RESULT;
+ else if (!strcasecmp(mtype_of_result, "hex")
+ || !strcasecmp(mtype_of_result, "h"))
+ type_of_result = HEX_RESULT;
+ else if (!strcasecmp(mtype_of_result, "char")
+ || !strcasecmp(mtype_of_result, "c"))
+ type_of_result = CHAR_RESULT;
+ else {
+ ast_log(LOG_WARNING, "Unknown type of result requested '%s'.\n",
+ mtype_of_result);
+ return -1;
+ }
+ }
+
+ if (!mvalue1 || !mvalue2) {
+ ast_log(LOG_WARNING,
+ "Supply all the parameters - just this once, please\n");
+ return -1;
+ }
+
+ if (sscanf(mvalue1, "%lf", &fnum1) != 1) {
+ ast_log(LOG_WARNING, "'%s' is not a valid number\n", mvalue1);
+ return -1;
+ }
+
+ if (sscanf(mvalue2, "%lf", &fnum2) != 1) {
+ ast_log(LOG_WARNING, "'%s' is not a valid number\n", mvalue2);
+ return -1;
+ }
+
+ if (negvalue1)
+ fnum1 = 0 - fnum1;
+
+ switch (iaction) {
+ case ADDFUNCTION:
+ ftmp = fnum1 + fnum2;
+ break;
+ case DIVIDEFUNCTION:
+ if (fnum2 <= 0)
+ ftmp = 0; /* can't do a divide by 0 */
+ else
+ ftmp = (fnum1 / fnum2);
+ break;
+ case MULTIPLYFUNCTION:
+ ftmp = (fnum1 * fnum2);
+ break;
+ case SUBTRACTFUNCTION:
+ ftmp = (fnum1 - fnum2);
+ break;
+ case MODULUSFUNCTION:
+ {
+ int inum1 = fnum1;
+ int inum2 = fnum2;
+
+ ftmp = (inum1 % inum2);
+
+ break;
+ }
+ case POWFUNCTION:
+ ftmp = pow(fnum1, fnum2);
+ break;
+ case SHLEFTFUNCTION:
+ {
+ int inum1 = fnum1;
+ int inum2 = fnum2;
+
+ ftmp = (inum1 << inum2);
+ break;
+ }
+ case SHRIGHTFUNCTION:
+ {
+ int inum1 = fnum1;
+ int inum2 = fnum2;
+
+ ftmp = (inum1 >> inum2);
+ break;
+ }
+ case BITWISEANDFUNCTION:
+ {
+ int inum1 = fnum1;
+ int inum2 = fnum2;
+ ftmp = (inum1 & inum2);
+ break;
+ }
+ case BITWISEXORFUNCTION:
+ {
+ int inum1 = fnum1;
+ int inum2 = fnum2;
+ ftmp = (inum1 ^ inum2);
+ break;
+ }
+ case BITWISEORFUNCTION:
+ {
+ int inum1 = fnum1;
+ int inum2 = fnum2;
+ ftmp = (inum1 | inum2);
+ break;
+ }
+ case GTFUNCTION:
+ ast_copy_string(buf, (fnum1 > fnum2) ? "TRUE" : "FALSE", len);
+ break;
+ case LTFUNCTION:
+ ast_copy_string(buf, (fnum1 < fnum2) ? "TRUE" : "FALSE", len);
+ break;
+ case GTEFUNCTION:
+ ast_copy_string(buf, (fnum1 >= fnum2) ? "TRUE" : "FALSE", len);
+ break;
+ case LTEFUNCTION:
+ ast_copy_string(buf, (fnum1 <= fnum2) ? "TRUE" : "FALSE", len);
+ break;
+ case EQFUNCTION:
+ ast_copy_string(buf, (fnum1 == fnum2) ? "TRUE" : "FALSE", len);
+ break;
+ default:
+ ast_log(LOG_WARNING,
+ "Something happened that neither of us should be proud of %d\n",
+ iaction);
+ return -1;
+ }
+
+ if (iaction < GTFUNCTION || iaction > EQFUNCTION) {
+ if (type_of_result == FLOAT_RESULT)
+ snprintf(buf, len, "%f", ftmp);
+ else if (type_of_result == INT_RESULT)
+ snprintf(buf, len, "%i", (int) ftmp);
+ else if (type_of_result == HEX_RESULT)
+ snprintf(buf, len, "%x", (unsigned int) ftmp);
+ else if (type_of_result == CHAR_RESULT)
+ snprintf(buf, len, "%c", (unsigned char) ftmp);
+ }
+
+ return 0;
+}
+
+static struct ast_custom_function math_function = {
+ .name = "MATH",
+ .synopsis = "Performs Mathematical Functions",
+ .syntax = "MATH(<number1><op><number2>[,<type_of_result>])",
+ .desc = "Perform calculation on number1 to number2. Valid ops are: \n"
+ " +,-,/,*,%,<<,>>,^,AND,OR,XOR,<,>,>=,<=,==\n"
+ "and behave as their C equivalents.\n"
+ "<type_of_result> - wanted type of result:\n"
+ " f, float - float(default)\n"
+ " i, int - integer,\n"
+ " h, hex - hex,\n"
+ " c, char - char\n"
+ "Example: Set(i=${MATH(123%16,int)}) - sets var i=11",
+ .read = math
+};
+
+static int unload_module(void)
+{
+ return ast_custom_function_unregister(&math_function);
+}
+
+static int load_module(void)
+{
+ return ast_custom_function_register(&math_function);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Mathematical dialplan function");
diff --git a/trunk/funcs/func_md5.c b/trunk/funcs/func_md5.c
new file mode 100644
index 000000000..b376e8ae3
--- /dev/null
+++ b/trunk/funcs/func_md5.c
@@ -0,0 +1,67 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2005-2006, Digium, Inc.
+ * Copyright (C) 2005, Olle E. Johansson, Edvina.net
+ * Copyright (C) 2005, Russell Bryant <russelb@clemson.edu>
+ *
+ * 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 MD5 digest related dialplan functions
+ *
+ * \author Olle E. Johansson <oej@edvina.net>
+ * \author Russell Bryant <russelb@clemson.edu>
+ *
+ * \ingroup functions
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/module.h"
+#include "asterisk/pbx.h"
+
+static int md5(struct ast_channel *chan, const char *cmd, char *data,
+ char *buf, size_t len)
+{
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "Syntax: MD5(<data>) - missing argument!\n");
+ return -1;
+ }
+
+ ast_md5_hash(buf, data);
+ buf[32] = '\0';
+
+ return 0;
+}
+
+static struct ast_custom_function md5_function = {
+ .name = "MD5",
+ .synopsis = "Computes an MD5 digest",
+ .syntax = "MD5(<data>)",
+ .read = md5,
+};
+
+static int unload_module(void)
+{
+ return ast_custom_function_unregister(&md5_function);
+}
+
+static int load_module(void)
+{
+ return ast_custom_function_register(&md5_function);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "MD5 digest dialplan functions");
diff --git a/trunk/funcs/func_module.c b/trunk/funcs/func_module.c
new file mode 100644
index 000000000..8b2326386
--- /dev/null
+++ b/trunk/funcs/func_module.c
@@ -0,0 +1,68 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2006, Digium, Inc.
+ *
+ * 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 Simple module check function
+ * \author Olle E. Johansson, Edvina.net
+ *
+ * \ingroup functions
+ */
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/module.h"
+#include "asterisk/pbx.h"
+
+static int ifmodule_read(struct ast_channel *chan, const char *cmd, char *data,
+ char *buf, size_t len)
+{
+ char *ret = "0";
+
+ *buf = '\0';
+
+ if (data)
+ if (ast_module_check(data))
+ ret = "1";
+
+ ast_copy_string(buf, ret, len);
+
+ return 0;
+}
+
+static struct ast_custom_function ifmodule_function = {
+ .name = "IFMODULE",
+ .synopsis = "Checks if an Asterisk module is loaded in memory",
+ .syntax = "IFMODULE(<modulename.so>)",
+ .read = ifmodule_read,
+ .desc = "Checks if a module is loaded. Use the full module name\n"
+ "as shown by the list in \"module list\". \n"
+ "Returns \"1\" if module exists in memory, otherwise \"0\".\n",
+};
+
+
+static int unload_module(void)
+{
+ return ast_custom_function_unregister(&ifmodule_function);
+}
+
+static int load_module(void)
+{
+ return ast_custom_function_register(&ifmodule_function);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Checks if Asterisk module is loaded in memory");
diff --git a/trunk/funcs/func_odbc.c b/trunk/funcs/func_odbc.c
new file mode 100644
index 000000000..af4bab069
--- /dev/null
+++ b/trunk/funcs/func_odbc.c
@@ -0,0 +1,900 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (c) 2005, 2006 Tilghman Lesher
+ *
+ * Tilghman Lesher <func_odbc__200508@the-tilghman.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 ODBC lookups
+ *
+ * \author Tilghman Lesher <func_odbc__200508@the-tilghman.com>
+ *
+ * \ingroup functions
+ */
+
+/*** MODULEINFO
+ <depend>unixodbc</depend>
+ <depend>ltdl</depend>
+ <depend>res_odbc</depend>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/module.h"
+#include "asterisk/file.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/config.h"
+#include "asterisk/res_odbc.h"
+#include "asterisk/app.h"
+
+static char *config = "func_odbc.conf";
+
+enum {
+ OPT_ESCAPECOMMAS = (1 << 0),
+ OPT_MULTIROW = (1 << 1),
+} odbc_option_flags;
+
+struct acf_odbc_query {
+ AST_LIST_ENTRY(acf_odbc_query) list;
+ char readhandle[5][30];
+ char writehandle[5][30];
+ char sql_read[2048];
+ char sql_write[2048];
+ unsigned int flags;
+ int rowlimit;
+ struct ast_custom_function *acf;
+};
+
+static void odbc_datastore_free(void *data);
+
+struct ast_datastore_info odbc_info = {
+ .type = "FUNC_ODBC",
+ .destroy = odbc_datastore_free,
+};
+
+/* For storing each result row */
+struct odbc_datastore_row {
+ AST_LIST_ENTRY(odbc_datastore_row) list;
+ char data[0];
+};
+
+/* For storing each result set */
+struct odbc_datastore {
+ AST_LIST_HEAD(, odbc_datastore_row);
+ char names[0];
+};
+
+AST_LIST_HEAD_STATIC(queries, acf_odbc_query);
+
+static int resultcount = 0;
+
+static void odbc_datastore_free(void *data)
+{
+ struct odbc_datastore *result = data;
+ struct odbc_datastore_row *row;
+ AST_LIST_LOCK(result);
+ while ((row = AST_LIST_REMOVE_HEAD(result, list))) {
+ ast_free(row);
+ }
+ AST_LIST_UNLOCK(result);
+ AST_LIST_HEAD_DESTROY(result);
+ ast_free(result);
+}
+
+static SQLHSTMT generic_execute(struct odbc_obj *obj, void *data)
+{
+ int res;
+ char *sql = data;
+ SQLHSTMT stmt;
+
+ res = SQLAllocHandle (SQL_HANDLE_STMT, obj->con, &stmt);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
+ return NULL;
+ }
+
+ res = SQLExecDirect(stmt, (unsigned char *)sql, SQL_NTS);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Exec Direct failed![%s]\n", sql);
+ SQLCloseCursor(stmt);
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ return NULL;
+ }
+
+ return stmt;
+}
+
+/*
+ * Master control routine
+ */
+static int acf_odbc_write(struct ast_channel *chan, const char *cmd, char *s, const char *value)
+{
+ struct odbc_obj *obj = NULL;
+ struct acf_odbc_query *query;
+ char *t, buf[2048], varname[15];
+ int i, dsn, bogus_chan = 0;
+ AST_DECLARE_APP_ARGS(values,
+ AST_APP_ARG(field)[100];
+ );
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(field)[100];
+ );
+ SQLHSTMT stmt = NULL;
+ SQLLEN rows=0;
+
+ AST_LIST_LOCK(&queries);
+ AST_LIST_TRAVERSE(&queries, query, list) {
+ if (!strcmp(query->acf->name, cmd)) {
+ break;
+ }
+ }
+
+ if (!query) {
+ ast_log(LOG_ERROR, "No such function '%s'\n", cmd);
+ AST_LIST_UNLOCK(&queries);
+ return -1;
+ }
+
+ if (!chan) {
+ if ((chan = ast_channel_alloc(0, 0, "", "", "", "", "", 0, "Bogus/func_odbc")))
+ bogus_chan = 1;
+ }
+
+ if (chan)
+ ast_autoservice_start(chan);
+
+ /* Parse our arguments */
+ t = value ? ast_strdupa(value) : "";
+
+ if (!s || !t) {
+ ast_log(LOG_ERROR, "Out of memory\n");
+ AST_LIST_UNLOCK(&queries);
+ if (chan)
+ ast_autoservice_stop(chan);
+ if (bogus_chan)
+ ast_channel_free(chan);
+ return -1;
+ }
+
+ AST_STANDARD_APP_ARGS(args, s);
+ for (i = 0; i < args.argc; i++) {
+ snprintf(varname, sizeof(varname), "ARG%d", i + 1);
+ pbx_builtin_pushvar_helper(chan, varname, args.field[i]);
+ }
+
+ /* Parse values, just like arguments */
+ AST_STANDARD_APP_ARGS(values, t);
+ for (i = 0; i < values.argc; i++) {
+ snprintf(varname, sizeof(varname), "VAL%d", i + 1);
+ pbx_builtin_pushvar_helper(chan, varname, values.field[i]);
+ }
+
+ /* Additionally set the value as a whole (but push an empty string if value is NULL) */
+ pbx_builtin_pushvar_helper(chan, "VALUE", value ? value : "");
+
+ pbx_substitute_variables_helper(chan, query->sql_write, buf, sizeof(buf) - 1);
+
+ /* Restore prior values */
+ for (i = 0; i < args.argc; i++) {
+ snprintf(varname, sizeof(varname), "ARG%d", i + 1);
+ pbx_builtin_setvar_helper(chan, varname, NULL);
+ }
+
+ for (i = 0; i < values.argc; i++) {
+ snprintf(varname, sizeof(varname), "VAL%d", i + 1);
+ pbx_builtin_setvar_helper(chan, varname, NULL);
+ }
+ pbx_builtin_setvar_helper(chan, "VALUE", NULL);
+
+ AST_LIST_UNLOCK(&queries);
+
+ for (dsn = 0; dsn < 5; dsn++) {
+ if (!ast_strlen_zero(query->writehandle[dsn])) {
+ obj = ast_odbc_request_obj(query->writehandle[dsn], 0);
+ if (obj)
+ stmt = ast_odbc_direct_execute(obj, generic_execute, buf);
+ }
+ if (stmt)
+ break;
+ }
+
+ if (stmt) {
+ /* Rows affected */
+ SQLRowCount(stmt, &rows);
+ }
+
+ /* Output the affected rows, for all cases. In the event of failure, we
+ * flag this as -1 rows. Note that this is different from 0 affected rows
+ * which would be the case if we succeeded in our query, but the values did
+ * not change. */
+ snprintf(varname, sizeof(varname), "%d", (int)rows);
+ pbx_builtin_setvar_helper(chan, "ODBCROWS", varname);
+
+ if (stmt) {
+ SQLCloseCursor(stmt);
+ SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+ }
+ if (obj)
+ ast_odbc_release_obj(obj);
+
+ if (chan)
+ ast_autoservice_stop(chan);
+ if (bogus_chan)
+ ast_channel_free(chan);
+
+ return 0;
+}
+
+static int acf_odbc_read(struct ast_channel *chan, const char *cmd, char *s, char *buf, size_t len)
+{
+ struct odbc_obj *obj = NULL;
+ struct acf_odbc_query *query;
+ char sql[2048], varname[15], colnames[2048] = "", rowcount[12] = "-1";
+ int res, x, y, buflen = 0, escapecommas, rowlimit = 1, dsn, bogus_chan = 0;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(field)[100];
+ );
+ SQLHSTMT stmt = NULL;
+ SQLSMALLINT colcount=0;
+ SQLLEN indicator;
+ SQLSMALLINT collength;
+ struct odbc_datastore *resultset = NULL;
+ struct odbc_datastore_row *row = NULL;
+
+ AST_LIST_LOCK(&queries);
+ AST_LIST_TRAVERSE(&queries, query, list) {
+ if (!strcmp(query->acf->name, cmd)) {
+ break;
+ }
+ }
+
+ if (!query) {
+ ast_log(LOG_ERROR, "No such function '%s'\n", cmd);
+ AST_LIST_UNLOCK(&queries);
+ pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
+ return -1;
+ }
+
+ if (!chan) {
+ if ((chan = ast_channel_alloc(0, 0, "", "", "", "", "", 0, "Bogus/func_odbc")))
+ bogus_chan = 1;
+ }
+
+ if (chan)
+ ast_autoservice_start(chan);
+
+ AST_STANDARD_APP_ARGS(args, s);
+ for (x = 0; x < args.argc; x++) {
+ snprintf(varname, sizeof(varname), "ARG%d", x + 1);
+ pbx_builtin_pushvar_helper(chan, varname, args.field[x]);
+ }
+
+ pbx_substitute_variables_helper(chan, query->sql_read, sql, sizeof(sql) - 1);
+
+ /* Restore prior values */
+ for (x = 0; x < args.argc; x++) {
+ snprintf(varname, sizeof(varname), "ARG%d", x + 1);
+ pbx_builtin_setvar_helper(chan, varname, NULL);
+ }
+
+ /* Save these flags, so we can release the lock */
+ escapecommas = ast_test_flag(query, OPT_ESCAPECOMMAS);
+ if (ast_test_flag(query, OPT_MULTIROW)) {
+ resultset = ast_calloc(1, sizeof(*resultset));
+ AST_LIST_HEAD_INIT(resultset);
+ if (query->rowlimit)
+ rowlimit = query->rowlimit;
+ else
+ rowlimit = INT_MAX;
+ }
+ AST_LIST_UNLOCK(&queries);
+
+ for (dsn = 0; dsn < 5; dsn++) {
+ if (!ast_strlen_zero(query->writehandle[dsn])) {
+ obj = ast_odbc_request_obj(query->writehandle[dsn], 0);
+ if (obj)
+ stmt = ast_odbc_direct_execute(obj, generic_execute, sql);
+ }
+ if (stmt)
+ break;
+ }
+
+ if (!stmt) {
+ ast_log(LOG_ERROR, "Unable to execute query [%s]\n", sql);
+ if (obj)
+ ast_odbc_release_obj(obj);
+ pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
+ if (chan)
+ ast_autoservice_stop(chan);
+ if (bogus_chan)
+ ast_channel_free(chan);
+ return -1;
+ }
+
+ res = SQLNumResultCols(stmt, &colcount);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
+ SQLCloseCursor(stmt);
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ ast_odbc_release_obj(obj);
+ pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
+ if (chan)
+ ast_autoservice_stop(chan);
+ if (bogus_chan)
+ ast_channel_free(chan);
+ return -1;
+ }
+
+ res = SQLFetch(stmt);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ int res1 = -1;
+ if (res == SQL_NO_DATA) {
+ ast_verb(4, "Found no rows [%s]\n", sql);
+ res1 = 0;
+ ast_copy_string(rowcount, "0", sizeof(rowcount));
+ } else {
+ ast_log(LOG_WARNING, "Error %d in FETCH [%s]\n", res, sql);
+ }
+ SQLCloseCursor(stmt);
+ SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+ ast_odbc_release_obj(obj);
+ pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
+ if (chan)
+ ast_autoservice_stop(chan);
+ if (bogus_chan)
+ ast_channel_free(chan);
+ return res1;
+ }
+
+ for (y = 0; y < rowlimit; y++) {
+ *buf = '\0';
+ for (x = 0; x < colcount; x++) {
+ int i;
+ char coldata[256];
+
+ if (y == 0) {
+ char colname[256];
+ int namelen;
+
+ res = SQLDescribeCol(stmt, x + 1, (unsigned char *)colname, sizeof(colname), &collength, NULL, NULL, NULL, NULL);
+ if (((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) || collength == 0) {
+ snprintf(colname, sizeof(colname), "field%d", x);
+ }
+
+ if (!ast_strlen_zero(colnames))
+ strncat(colnames, ",", sizeof(colnames) - 1);
+ namelen = strlen(colnames);
+
+ /* Copy data, encoding '\' and ',' for the argument parser */
+ for (i = 0; i < sizeof(colname); i++) {
+ if (escapecommas && (colname[i] == '\\' || colname[i] == ',')) {
+ colnames[namelen++] = '\\';
+ }
+ colnames[namelen++] = colname[i];
+
+ if (namelen >= sizeof(colnames) - 2) {
+ colnames[namelen >= sizeof(colnames) ? sizeof(colnames) - 1 : namelen] = '\0';
+ break;
+ }
+
+ if (colname[i] == '\0')
+ break;
+ }
+
+ if (resultset) {
+ void *tmp = ast_realloc(resultset, sizeof(*resultset) + strlen(colnames) + 1);
+ if (!tmp) {
+ ast_log(LOG_ERROR, "No space for a new resultset?\n");
+ ast_free(resultset);
+ SQLCloseCursor(stmt);
+ SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+ ast_odbc_release_obj(obj);
+ pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
+ if (chan)
+ ast_autoservice_stop(chan);
+ if (bogus_chan)
+ ast_channel_free(chan);
+ return -1;
+ }
+ resultset = tmp;
+ strcpy((char *)resultset + sizeof(*resultset), colnames);
+ }
+ }
+
+ buflen = strlen(buf);
+ res = SQLGetData(stmt, x + 1, SQL_CHAR, coldata, sizeof(coldata), &indicator);
+ if (indicator == SQL_NULL_DATA) {
+ coldata[0] = '\0';
+ res = SQL_SUCCESS;
+ }
+
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
+ y = -1;
+ goto end_acf_read;
+ }
+
+ /* Copy data, encoding '\' and ',' for the argument parser */
+ for (i = 0; i < sizeof(coldata); i++) {
+ if (escapecommas && (coldata[i] == '\\' || coldata[i] == ',')) {
+ buf[buflen++] = '\\';
+ }
+ buf[buflen++] = coldata[i];
+
+ if (buflen >= len - 2)
+ break;
+
+ if (coldata[i] == '\0')
+ break;
+ }
+
+ buf[buflen - 1] = ',';
+ buf[buflen] = '\0';
+ }
+ /* Trim trailing comma */
+ buf[buflen - 1] = '\0';
+
+ if (resultset) {
+ row = ast_calloc(1, sizeof(*row) + buflen);
+ if (!row) {
+ ast_log(LOG_ERROR, "Unable to allocate space for more rows in this resultset.\n");
+ goto end_acf_read;
+ }
+ strcpy((char *)row + sizeof(*row), buf);
+ AST_LIST_INSERT_TAIL(resultset, row, list);
+
+ /* Get next row */
+ res = SQLFetch(stmt);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ if (res != SQL_NO_DATA)
+ ast_log(LOG_WARNING, "Error %d in FETCH [%s]\n", res, sql);
+ y++;
+ break;
+ }
+ }
+ }
+
+end_acf_read:
+ snprintf(rowcount, sizeof(rowcount), "%d", y);
+ pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
+ pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", colnames);
+ if (resultset) {
+ int uid;
+ struct ast_datastore *odbc_store;
+ uid = ast_atomic_fetchadd_int(&resultcount, +1) + 1;
+ snprintf(buf, len, "%d", uid);
+ odbc_store = ast_channel_datastore_alloc(&odbc_info, buf);
+ if (!odbc_store) {
+ ast_log(LOG_ERROR, "Rows retrieved, but unable to store it in the channel. Results fail.\n");
+ odbc_datastore_free(resultset);
+ SQLCloseCursor(stmt);
+ SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+ ast_odbc_release_obj(obj);
+ if (chan)
+ ast_autoservice_stop(chan);
+ if (bogus_chan)
+ ast_channel_free(chan);
+ return -1;
+ }
+ odbc_store->data = resultset;
+ ast_channel_datastore_add(chan, odbc_store);
+ }
+ SQLCloseCursor(stmt);
+ SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+ ast_odbc_release_obj(obj);
+ if (chan)
+ ast_autoservice_stop(chan);
+ if (bogus_chan)
+ ast_channel_free(chan);
+ return 0;
+}
+
+static int acf_escape(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
+{
+ char *out = buf;
+
+ for (; *data && out - buf < len; data++) {
+ if (*data == '\'') {
+ *out = '\'';
+ out++;
+ }
+ *out++ = *data;
+ }
+ *out = '\0';
+
+ return 0;
+}
+
+static struct ast_custom_function escape_function = {
+ .name = "SQL_ESC",
+ .synopsis = "Escapes single ticks for use in SQL statements",
+ .syntax = "SQL_ESC(<string>)",
+ .desc =
+"Used in SQL templates to escape data which may contain single ticks (') which\n"
+"are otherwise used to delimit data. For example:\n"
+"SELECT foo FROM bar WHERE baz='${SQL_ESC(${ARG1})}'\n",
+ .read = acf_escape,
+ .write = NULL,
+};
+
+static int acf_fetch(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
+{
+ struct ast_datastore *store;
+ struct odbc_datastore *resultset;
+ struct odbc_datastore_row *row;
+ store = ast_channel_datastore_find(chan, &odbc_info, data);
+ if (!store) {
+ return -1;
+ }
+ resultset = store->data;
+ AST_LIST_LOCK(resultset);
+ row = AST_LIST_REMOVE_HEAD(resultset, list);
+ AST_LIST_UNLOCK(resultset);
+ if (!row) {
+ /* Cleanup datastore */
+ ast_channel_datastore_remove(chan, store);
+ ast_channel_datastore_free(store);
+ return -1;
+ }
+ pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", resultset->names);
+ ast_copy_string(buf, row->data, len);
+ ast_free(row);
+ return 0;
+}
+
+static struct ast_custom_function fetch_function = {
+ .name = "ODBC_FETCH",
+ .synopsis = "Fetch a row from a multirow query",
+ .syntax = "ODBC_FETCH(<result-id>)",
+ .desc =
+"For queries which are marked as mode=multirow, the original query returns a\n"
+"result-id from which results may be fetched. This function implements the\n"
+"actual fetch of the results.\n",
+ .read = acf_fetch,
+ .write = NULL,
+};
+
+static char *app_odbcfinish = "ODBCFinish";
+static char *syn_odbcfinish = "Clear the resultset of a successful multirow query";
+static char *desc_odbcfinish =
+"ODBCFinish(<result-id>)\n"
+" Clears any remaining rows of the specified resultset\n";
+
+
+static int exec_odbcfinish(struct ast_channel *chan, void *data)
+{
+ struct ast_datastore *store = ast_channel_datastore_find(chan, &odbc_info, data);
+ if (!store) /* Already freed; no big deal. */
+ return 0;
+ ast_channel_datastore_remove(chan, store);
+ ast_channel_datastore_free(store);
+ return 0;
+}
+
+static int init_acf_query(struct ast_config *cfg, char *catg, struct acf_odbc_query **query)
+{
+ const char *tmp;
+ int i;
+
+ if (!cfg || !catg) {
+ return EINVAL;
+ }
+
+ *query = ast_calloc(1, sizeof(struct acf_odbc_query));
+ if (! (*query))
+ return ENOMEM;
+
+ if (((tmp = ast_variable_retrieve(cfg, catg, "writehandle"))) || ((tmp = ast_variable_retrieve(cfg, catg, "dsn")))) {
+ char *tmp2 = ast_strdupa(tmp);
+ AST_DECLARE_APP_ARGS(write,
+ AST_APP_ARG(dsn)[5];
+ );
+ AST_STANDARD_APP_ARGS(write, tmp2);
+ for (i = 0; i < 5; i++) {
+ if (!ast_strlen_zero(write.dsn[i]))
+ ast_copy_string((*query)->writehandle[i], write.dsn[i], sizeof((*query)->writehandle[i]));
+ }
+ }
+
+ if ((tmp = ast_variable_retrieve(cfg, catg, "readhandle"))) {
+ char *tmp2 = ast_strdupa(tmp);
+ AST_DECLARE_APP_ARGS(read,
+ AST_APP_ARG(dsn)[5];
+ );
+ AST_STANDARD_APP_ARGS(read, tmp2);
+ for (i = 0; i < 5; i++) {
+ if (!ast_strlen_zero(read.dsn[i]))
+ ast_copy_string((*query)->readhandle[i], read.dsn[i], sizeof((*query)->readhandle[i]));
+ }
+ } else {
+ /* If no separate readhandle, then use the writehandle for reading */
+ for (i = 0; i < 5; i++) {
+ if (!ast_strlen_zero((*query)->writehandle[i]))
+ ast_copy_string((*query)->readhandle[i], (*query)->writehandle[i], sizeof((*query)->readhandle[i]));
+ }
+ }
+
+ if ((tmp = ast_variable_retrieve(cfg, catg, "readsql")))
+ ast_copy_string((*query)->sql_read, tmp, sizeof((*query)->sql_read));
+ else if ((tmp = ast_variable_retrieve(cfg, catg, "read"))) {
+ ast_log(LOG_WARNING, "Parameter 'read' is deprecated for category %s. Please use 'readsql' instead.\n", catg);
+ ast_copy_string((*query)->sql_read, tmp, sizeof((*query)->sql_read));
+ }
+
+ if (!ast_strlen_zero((*query)->sql_read) && ast_strlen_zero((*query)->readhandle[0])) {
+ ast_free(*query);
+ *query = NULL;
+ ast_log(LOG_ERROR, "There is SQL, but no ODBC class to be used for reading: %s\n", catg);
+ return EINVAL;
+ }
+
+ if ((tmp = ast_variable_retrieve(cfg, catg, "writesql")))
+ ast_copy_string((*query)->sql_write, tmp, sizeof((*query)->sql_write));
+ else if ((tmp = ast_variable_retrieve(cfg, catg, "write"))) {
+ ast_log(LOG_WARNING, "Parameter 'write' is deprecated for category %s. Please use 'writesql' instead.\n", catg);
+ ast_copy_string((*query)->sql_write, tmp, sizeof((*query)->sql_write));
+ }
+
+ if (!ast_strlen_zero((*query)->sql_write) && ast_strlen_zero((*query)->writehandle[0])) {
+ ast_free(*query);
+ *query = NULL;
+ ast_log(LOG_ERROR, "There is SQL, but no ODBC class to be used for writing: %s\n", catg);
+ return EINVAL;
+ }
+
+ /* Allow escaping of embedded commas in fields to be turned off */
+ ast_set_flag((*query), OPT_ESCAPECOMMAS);
+ if ((tmp = ast_variable_retrieve(cfg, catg, "escapecommas"))) {
+ if (ast_false(tmp))
+ ast_clear_flag((*query), OPT_ESCAPECOMMAS);
+ }
+
+ if ((tmp = ast_variable_retrieve(cfg, catg, "mode"))) {
+ if (strcasecmp(tmp, "multirow") == 0)
+ ast_set_flag((*query), OPT_MULTIROW);
+ if ((tmp = ast_variable_retrieve(cfg, catg, "rowlimit")))
+ sscanf(tmp, "%d", &((*query)->rowlimit));
+ }
+
+ (*query)->acf = ast_calloc(1, sizeof(struct ast_custom_function));
+ if (! (*query)->acf) {
+ ast_free(*query);
+ *query = NULL;
+ return ENOMEM;
+ }
+
+ if ((tmp = ast_variable_retrieve(cfg, catg, "prefix")) && !ast_strlen_zero(tmp)) {
+ asprintf((char **)&((*query)->acf->name), "%s_%s", tmp, catg);
+ } else {
+ asprintf((char **)&((*query)->acf->name), "ODBC_%s", catg);
+ }
+
+ if (!((*query)->acf->name)) {
+ ast_free((*query)->acf);
+ ast_free(*query);
+ *query = NULL;
+ return ENOMEM;
+ }
+
+ asprintf((char **)&((*query)->acf->syntax), "%s(<arg1>[...[,<argN>]])", (*query)->acf->name);
+
+ if (!((*query)->acf->syntax)) {
+ ast_free((char *)(*query)->acf->name);
+ ast_free((*query)->acf);
+ ast_free(*query);
+ *query = NULL;
+ return ENOMEM;
+ }
+
+ (*query)->acf->synopsis = "Runs the referenced query with the specified arguments";
+ if (!ast_strlen_zero((*query)->sql_read) && !ast_strlen_zero((*query)->sql_write)) {
+ asprintf((char **)&((*query)->acf->desc),
+ "Runs the following query, as defined in func_odbc.conf, performing\n"
+ "substitution of the arguments into the query as specified by ${ARG1},\n"
+ "${ARG2}, ... ${ARGn}. When setting the function, the values are provided\n"
+ "either in whole as ${VALUE} or parsed as ${VAL1}, ${VAL2}, ... ${VALn}.\n"
+ "\nRead:\n%s\n\nWrite:\n%s\n",
+ (*query)->sql_read,
+ (*query)->sql_write);
+ } else if (!ast_strlen_zero((*query)->sql_read)) {
+ asprintf((char **)&((*query)->acf->desc),
+ "Runs the following query, as defined in func_odbc.conf, performing\n"
+ "substitution of the arguments into the query as specified by ${ARG1},\n"
+ "${ARG2}, ... ${ARGn}. This function may only be read, not set.\n\nSQL:\n%s\n",
+ (*query)->sql_read);
+ } else if (!ast_strlen_zero((*query)->sql_write)) {
+ asprintf((char **)&((*query)->acf->desc),
+ "Runs the following query, as defined in func_odbc.conf, performing\n"
+ "substitution of the arguments into the query as specified by ${ARG1},\n"
+ "${ARG2}, ... ${ARGn}. The values are provided either in whole as\n"
+ "${VALUE} or parsed as ${VAL1}, ${VAL2}, ... ${VALn}.\n"
+ "This function may only be set.\nSQL:\n%s\n",
+ (*query)->sql_write);
+ } else {
+ ast_free((char *)(*query)->acf->syntax);
+ ast_free((char *)(*query)->acf->name);
+ ast_free((*query)->acf);
+ ast_free(*query);
+ ast_log(LOG_WARNING, "Section %s was found, but there was no SQL to execute. Ignoring.\n", catg);
+ return EINVAL;
+ }
+
+ if (! ((*query)->acf->desc)) {
+ ast_free((char *)(*query)->acf->syntax);
+ ast_free((char *)(*query)->acf->name);
+ ast_free((*query)->acf);
+ ast_free(*query);
+ *query = NULL;
+ return ENOMEM;
+ }
+
+ if (ast_strlen_zero((*query)->sql_read)) {
+ (*query)->acf->read = NULL;
+ } else {
+ (*query)->acf->read = acf_odbc_read;
+ }
+
+ if (ast_strlen_zero((*query)->sql_write)) {
+ (*query)->acf->write = NULL;
+ } else {
+ (*query)->acf->write = acf_odbc_write;
+ }
+
+ return 0;
+}
+
+static int free_acf_query(struct acf_odbc_query *query)
+{
+ if (query) {
+ if (query->acf) {
+ if (query->acf->name)
+ ast_free((char *)query->acf->name);
+ if (query->acf->syntax)
+ ast_free((char *)query->acf->syntax);
+ if (query->acf->desc)
+ ast_free((char *)query->acf->desc);
+ ast_free(query->acf);
+ }
+ ast_free(query);
+ }
+ return 0;
+}
+
+static int load_module(void)
+{
+ int res = 0;
+ struct ast_config *cfg;
+ char *catg;
+ struct ast_flags config_flags = { 0 };
+
+ res |= ast_custom_function_register(&fetch_function);
+ res |= ast_register_application(app_odbcfinish, exec_odbcfinish, syn_odbcfinish, desc_odbcfinish);
+ AST_LIST_LOCK(&queries);
+
+ cfg = ast_config_load(config, config_flags);
+ if (!cfg) {
+ ast_log(LOG_NOTICE, "Unable to load config for func_odbc: %s\n", config);
+ AST_LIST_UNLOCK(&queries);
+ return AST_MODULE_LOAD_DECLINE;
+ }
+
+ for (catg = ast_category_browse(cfg, NULL);
+ catg;
+ catg = ast_category_browse(cfg, catg)) {
+ struct acf_odbc_query *query = NULL;
+ int err;
+
+ if ((err = init_acf_query(cfg, catg, &query))) {
+ if (err == ENOMEM)
+ ast_log(LOG_ERROR, "Out of memory\n");
+ else if (err == EINVAL)
+ ast_log(LOG_ERROR, "Invalid parameters for category %s\n", catg);
+ else
+ ast_log(LOG_ERROR, "%s (%d)\n", strerror(err), err);
+ } else {
+ AST_LIST_INSERT_HEAD(&queries, query, list);
+ ast_custom_function_register(query->acf);
+ }
+ }
+
+ ast_config_destroy(cfg);
+ res |= ast_custom_function_register(&escape_function);
+
+ AST_LIST_UNLOCK(&queries);
+ return res;
+}
+
+static int unload_module(void)
+{
+ struct acf_odbc_query *query;
+ int res = 0;
+
+ AST_LIST_LOCK(&queries);
+ while (!AST_LIST_EMPTY(&queries)) {
+ query = AST_LIST_REMOVE_HEAD(&queries, list);
+ ast_custom_function_unregister(query->acf);
+ free_acf_query(query);
+ }
+
+ res |= ast_custom_function_unregister(&escape_function);
+ res |= ast_custom_function_unregister(&fetch_function);
+ res |= ast_unregister_application(app_odbcfinish);
+
+ /* Allow any threads waiting for this lock to pass (avoids a race) */
+ AST_LIST_UNLOCK(&queries);
+ usleep(1);
+ AST_LIST_LOCK(&queries);
+
+ AST_LIST_UNLOCK(&queries);
+ return 0;
+}
+
+static int reload(void)
+{
+ int res = 0;
+ struct ast_config *cfg;
+ struct acf_odbc_query *oldquery;
+ char *catg;
+ struct ast_flags config_flags = { CONFIG_FLAG_FILEUNCHANGED };
+
+ cfg = ast_config_load(config, config_flags);
+ if (cfg == CONFIG_STATUS_FILEUNCHANGED)
+ return 0;
+
+ AST_LIST_LOCK(&queries);
+
+ while (!AST_LIST_EMPTY(&queries)) {
+ oldquery = AST_LIST_REMOVE_HEAD(&queries, list);
+ ast_custom_function_unregister(oldquery->acf);
+ free_acf_query(oldquery);
+ }
+
+ if (!cfg) {
+ ast_log(LOG_WARNING, "Unable to load config for func_odbc: %s\n", config);
+ goto reload_out;
+ }
+
+ for (catg = ast_category_browse(cfg, NULL);
+ catg;
+ catg = ast_category_browse(cfg, catg)) {
+ struct acf_odbc_query *query = NULL;
+
+ if (init_acf_query(cfg, catg, &query)) {
+ ast_log(LOG_ERROR, "Cannot initialize query %s\n", catg);
+ } else {
+ AST_LIST_INSERT_HEAD(&queries, query, list);
+ ast_custom_function_register(query->acf);
+ }
+ }
+
+ ast_config_destroy(cfg);
+reload_out:
+ AST_LIST_UNLOCK(&queries);
+ return res;
+}
+
+/* XXX need to revise usecount - set if query_lock is set */
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "ODBC lookups",
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+ );
+
diff --git a/trunk/funcs/func_rand.c b/trunk/funcs/func_rand.c
new file mode 100644
index 000000000..a3db21d26
--- /dev/null
+++ b/trunk/funcs/func_rand.c
@@ -0,0 +1,93 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2006, Digium, Inc.
+ * Copyright (C) 2006, Claude Patry
+ *
+ * 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 Generate Random Number
+ *
+ * \author Claude Patry <cpatry@gmail.com>
+ * \author Tilghman Lesher ( http://asterisk.drunkcoder.com/ )
+ * \ingroup functions
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/module.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/utils.h"
+#include "asterisk/app.h"
+
+static int acf_rand_exec(struct ast_channel *chan, const char *cmd,
+ char *parse, char *buffer, size_t buflen)
+{
+ int min_int, response_int, max_int;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(min);
+ AST_APP_ARG(max);
+ );
+
+ AST_STANDARD_APP_ARGS(args, parse);
+
+ if (ast_strlen_zero(args.min) || sscanf(args.min, "%d", &min_int) != 1)
+ min_int = 0;
+
+ if (ast_strlen_zero(args.max) || sscanf(args.max, "%d", &max_int) != 1)
+ max_int = RAND_MAX;
+
+ if (max_int < min_int) {
+ int tmp = max_int;
+
+ max_int = min_int;
+ min_int = tmp;
+ ast_debug(1, "max<min\n");
+ }
+
+ response_int = min_int + (ast_random() % (max_int - min_int + 1));
+ ast_debug(1, "%d was the lucky number in range [%d,%d]\n", response_int, min_int, max_int);
+ snprintf(buffer, buflen, "%d", response_int);
+
+ return 0;
+}
+
+static struct ast_custom_function acf_rand = {
+ .name = "RAND",
+ .synopsis = "Choose a random number in a range",
+ .syntax = "RAND([min][,max])",
+ .desc =
+ "Choose a random number between min and max. Min defaults to 0, if not\n"
+ "specified, while max defaults to RAND_MAX (2147483647 on many systems).\n"
+ " Example: Set(junky=${RAND(1,8)}); \n"
+ " Sets junky to a random number between 1 and 8, inclusive.\n",
+ .read = acf_rand_exec,
+};
+
+static int unload_module(void)
+{
+ ast_custom_function_unregister(&acf_rand);
+
+ return 0;
+}
+
+static int load_module(void)
+{
+ return ast_custom_function_register(&acf_rand);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Random number dialplan function");
diff --git a/trunk/funcs/func_realtime.c b/trunk/funcs/func_realtime.c
new file mode 100644
index 000000000..7b269d91c
--- /dev/null
+++ b/trunk/funcs/func_realtime.c
@@ -0,0 +1,154 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2005-2006, BJ Weschke. All rights reserved.
+ *
+ * BJ Weschke <bweschke@btwtech.com>
+ *
+ * This code is released by the author with no restrictions on usage.
+ *
+ * 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.
+ *
+ */
+
+/*! \file
+ *
+ * \brief REALTIME dialplan function
+ *
+ * \author BJ Weschke <bweschke@btwtech.com>
+ *
+ * \ingroup functions
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/file.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/config.h"
+#include "asterisk/module.h"
+#include "asterisk/lock.h"
+#include "asterisk/utils.h"
+#include "asterisk/app.h"
+
+static int function_realtime_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
+{
+ struct ast_variable *var, *head;
+ struct ast_str *out;
+ size_t resultslen;
+ int n;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(family);
+ AST_APP_ARG(fieldmatch);
+ AST_APP_ARG(value);
+ AST_APP_ARG(delim1);
+ AST_APP_ARG(delim2);
+ );
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "Syntax: REALTIME(family,fieldmatch[,value[,delim1[,delim2]]]) - missing argument!\n");
+ return -1;
+ }
+
+ AST_STANDARD_APP_ARGS(args, data);
+
+ if (!args.delim1)
+ args.delim1 = ",";
+ if (!args.delim2)
+ args.delim2 = "=";
+
+ if (chan)
+ ast_autoservice_start(chan);
+
+ head = ast_load_realtime_all(args.family, args.fieldmatch, args.value, NULL);
+
+ if (!head)
+ if (chan)
+ ast_autoservice_stop(chan);
+ return -1;
+
+ resultslen = 0;
+ n = 0;
+ for (var = head; var; n++, var = var->next)
+ resultslen += strlen(var->name) + strlen(var->value);
+ /* add space for delimiters and final '\0' */
+ resultslen += n * (strlen(args.delim1) + strlen(args.delim2)) + 1;
+
+ out = ast_str_alloca(resultslen);
+ for (var = head; var; var = var->next)
+ ast_str_append(&out, 0, "%s%s%s%s", var->name, args.delim2, var->value, args.delim1);
+ ast_copy_string(buf, out->str, len);
+
+ if (chan)
+ ast_autoservice_stop(chan);
+
+ return 0;
+}
+
+static int function_realtime_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
+{
+ int res = 0;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(family);
+ AST_APP_ARG(fieldmatch);
+ AST_APP_ARG(value);
+ AST_APP_ARG(field);
+ );
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "Syntax: REALTIME(family,fieldmatch,value,newcol) - missing argument!\n");
+ return -1;
+ }
+
+ if (chan)
+ ast_autoservice_start(chan);
+
+ AST_STANDARD_APP_ARGS(args, data);
+
+ res = ast_update_realtime(args.family, args.fieldmatch, args.value, args.field, (char *)value, NULL);
+
+ if (res < 0) {
+ ast_log(LOG_WARNING, "Failed to update. Check the debug log for possible data repository related entries.\n");
+ }
+
+ if (chan)
+ ast_autoservice_stop(chan);
+
+ return 0;
+}
+
+struct ast_custom_function realtime_function = {
+ .name = "REALTIME",
+ .synopsis = "RealTime Read/Write Functions",
+ .syntax = "REALTIME(family,fieldmatch[,value[,delim1[,delim2]]]) on read\n"
+ "REALTIME(family,fieldmatch,value,field) on write",
+ .desc = "This function will read or write values from/to a RealTime repository.\n"
+ "REALTIME(....) will read names/values from the repository, and \n"
+ "REALTIME(....)= will write a new value/field to the repository. On a\n"
+ "read, this function returns a delimited text string. The name/value \n"
+ "pairs are delimited by delim1, and the name and value are delimited \n"
+ "between each other with delim2. The default for delim1 is ',' and \n"
+ "the default for delim2 is '='. If there is no match, NULL will be \n"
+ "returned by the function. On a write, this function will always \n"
+ "return NULL. \n",
+ .read = function_realtime_read,
+ .write = function_realtime_write,
+};
+
+static int unload_module(void)
+{
+ return ast_custom_function_unregister(&realtime_function);
+}
+
+static int load_module(void)
+{
+ return ast_custom_function_register(&realtime_function);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Read/Write values from a RealTime repository");
diff --git a/trunk/funcs/func_sha1.c b/trunk/funcs/func_sha1.c
new file mode 100644
index 000000000..6474140dd
--- /dev/null
+++ b/trunk/funcs/func_sha1.c
@@ -0,0 +1,76 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2006, Digium, Inc.
+ * Copyright (C) 2006, Claude Patry
+ *
+ * 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 SHA1 digest related dialplan functions
+ *
+ * \author Claude Patry <cpatry@gmail.com>
+ *
+ * \ingroup functions
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/module.h"
+#include "asterisk/pbx.h"
+
+static int sha1(struct ast_channel *chan, const char *cmd, char *data,
+ char *buf, size_t len)
+{
+ *buf = '\0';
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "Syntax: SHA1(<data>) - missing argument!\n");
+ return -1;
+ }
+
+ if (len >= 41)
+ ast_sha1_hash(buf, data);
+ else {
+ ast_log(LOG_ERROR,
+ "Insufficient space to produce SHA1 hash result (%d < 41)\n",
+ (int) len);
+ }
+
+ return 0;
+}
+
+static struct ast_custom_function sha1_function = {
+ .name = "SHA1",
+ .synopsis = "Computes a SHA1 digest",
+ .syntax = "SHA1(<data>)",
+ .read = sha1,
+ .desc = "Generate a SHA1 digest via the SHA1 algorythm.\n"
+ " Example: Set(sha1hash=${SHA1(junky)})\n"
+ " Sets the asterisk variable sha1hash to the string '60fa5675b9303eb62f99a9cd47f9f5837d18f9a0'\n"
+ " which is known as his hash\n",
+};
+
+static int unload_module(void)
+{
+ return ast_custom_function_unregister(&sha1_function);
+}
+
+static int load_module(void)
+{
+ return ast_custom_function_register(&sha1_function);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "SHA-1 computation dialplan function");
diff --git a/trunk/funcs/func_shell.c b/trunk/funcs/func_shell.c
new file mode 100644
index 000000000..57a9819cd
--- /dev/null
+++ b/trunk/funcs/func_shell.c
@@ -0,0 +1,92 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2006, Digium, Inc.
+ *
+ * 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
+ *
+ * SHELL function to return the value of a system call.
+ *
+ * \note Inspiration and Guidance from Russell! Thank You!
+ *
+ * \author Brandon Kruse <bkruse@digium.com>
+ *
+ * \ingroup functions
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/module.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/utils.h"
+#include "asterisk/app.h"
+
+static int shell_helper(struct ast_channel *chan, const char *cmd, char *data,
+ char *buf, size_t len)
+{
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "Missing Argument! Example: Set(foo=${SHELL(echo \"bar\")})\n");
+ return -1;
+ }
+
+ if (chan)
+ ast_autoservice_start(chan);
+
+ if (len >= 1) {
+ FILE *ptr;
+ char plbuff[4096];
+
+ ptr = popen(data, "r");
+ while (fgets(plbuff, sizeof(plbuff), ptr)) {
+ strncat(buf, plbuff, len - strlen(buf) - 1);
+ }
+ pclose(ptr);
+ }
+
+ if (chan)
+ ast_autoservice_stop(chan);
+
+ return 0;
+}
+
+static struct ast_custom_function shell_function = {
+ .name = "SHELL",
+ .synopsis = "Executes a command as if you were at a shell.",
+ .syntax = "SHELL(<command>)",
+ .read = shell_helper,
+ .desc =
+"Returns the value from a system command\n"
+" Example: Set(foo=${SHELL(echo \"bar\")})\n"
+" Note: When using the SHELL() dialplan function, your \"SHELL\" is /bin/sh,\n"
+" which may differ as to the underlying shell, depending upon your production\n"
+" platform. Also keep in mind that if you are using a common path, you should\n"
+" be mindful of race conditions that could result from two calls running\n"
+" SHELL() simultaneously.\n",
+};
+
+static int unload_module(void)
+{
+ return ast_custom_function_unregister(&shell_function);
+}
+
+static int load_module(void)
+{
+ return ast_custom_function_register(&shell_function);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Returns the output of a shell command");
+
diff --git a/trunk/funcs/func_strings.c b/trunk/funcs/func_strings.c
new file mode 100644
index 000000000..54730e108
--- /dev/null
+++ b/trunk/funcs/func_strings.c
@@ -0,0 +1,886 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2005-2006, Digium, Inc.
+ * Portions Copyright (C) 2005, Tilghman Lesher. All rights reserved.
+ * Portions Copyright (C) 2005, Anthony Minessale II
+ *
+ * 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 String manipulation dialplan functions
+ *
+ * \author Tilghman Lesher
+ * \author Anothony Minessale II
+ * \ingroup functions
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <regex.h>
+#include <ctype.h>
+
+#include "asterisk/module.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/utils.h"
+#include "asterisk/app.h"
+#include "asterisk/localtime.h"
+
+static int function_fieldqty(struct ast_channel *chan, const char *cmd,
+ char *parse, char *buf, size_t len)
+{
+ char *varsubst, varval[8192], *varval2 = varval;
+ int fieldcount = 0;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(varname);
+ AST_APP_ARG(delim);
+ );
+ char delim[2] = "";
+ size_t delim_used;
+
+ if (chan)
+ ast_autoservice_start(chan);
+
+ AST_STANDARD_APP_ARGS(args, parse);
+ if (args.delim) {
+ ast_get_encoded_char(args.delim, delim, &delim_used);
+
+ varsubst = alloca(strlen(args.varname) + 4);
+
+ sprintf(varsubst, "${%s}", args.varname);
+ pbx_substitute_variables_helper(chan, varsubst, varval, sizeof(varval) - 1);
+ if (ast_strlen_zero(varval2))
+ fieldcount = 0;
+ else {
+ while (strsep(&varval2, delim))
+ fieldcount++;
+ }
+ } else {
+ fieldcount = 1;
+ }
+ snprintf(buf, len, "%d", fieldcount);
+
+ if (chan)
+ ast_autoservice_stop(chan);
+
+ return 0;
+}
+
+static struct ast_custom_function fieldqty_function = {
+ .name = "FIELDQTY",
+ .synopsis = "Count the fields, with an arbitrary delimiter",
+ .syntax = "FIELDQTY(<varname>,<delim>)",
+ .read = function_fieldqty,
+};
+
+static int filter(struct ast_channel *chan, const char *cmd, char *parse, char *buf,
+ size_t len)
+{
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(allowed);
+ AST_APP_ARG(string);
+ );
+ char *outbuf = buf, ac;
+ char allowed[256] = "";
+ size_t allowedlen = 0;
+
+ AST_STANDARD_APP_ARGS(args, parse);
+
+ if (!args.string) {
+ ast_log(LOG_ERROR, "Usage: FILTER(<allowed-chars>,<string>)\n");
+ return -1;
+ }
+
+ /* Expand ranges */
+ for (; *(args.allowed) && allowedlen < sizeof(allowed); (args.allowed)++) {
+ char c1 = 0, c2 = 0;
+ size_t consumed = 0;
+
+ if (ast_get_encoded_char(args.allowed, &c1, &consumed))
+ return -1;
+ args.allowed += consumed;
+
+ if (*(args.allowed) == '-') {
+ if (ast_get_encoded_char(args.allowed + 1, &c2, &consumed))
+ c2 = -1;
+ args.allowed += consumed + 1;
+
+ /*!\note
+ * Looks a little strange, until you realize that we can overflow
+ * the size of a char.
+ */
+ for (ac = c1; ac != c2 && allowedlen < sizeof(allowed) - 1; ac++)
+ allowed[allowedlen++] = ac;
+ allowed[allowedlen++] = ac;
+
+ ast_debug(4, "c1=%d, c2=%d\n", c1, c2);
+
+ /* Decrement before the loop increment */
+ (args.allowed)--;
+ } else
+ allowed[allowedlen++] = c1;
+ }
+
+ ast_debug(1, "Allowed: %s\n", allowed);
+
+ for (; *(args.string) && (buf + len - 1 > outbuf); (args.string)++) {
+ if (strchr(allowed, *(args.string)))
+ *outbuf++ = *(args.string);
+ }
+ *outbuf = '\0';
+
+ return 0;
+}
+
+static struct ast_custom_function filter_function = {
+ .name = "FILTER",
+ .synopsis = "Filter the string to include only the allowed characters",
+ .syntax = "FILTER(<allowed-chars>,<string>)",
+ .read = filter,
+ .desc =
+"Permits all characters listed in <allowed-chars>, filtering all others out.\n"
+"In addition to literally listing the characters, you may also use ranges of\n"
+"characters (delimited by a '-'), as well as hexadecimal characters started\n"
+"with a \\x (i.e. \\x20) and octal characters started with \\0 (i.e. \\040).\n"
+"Also, \\t, \\n, and \\r are recognized. If you want a literal '-' character,\n"
+"simply prefix it with a '\\'\n",
+};
+
+static int regex(struct ast_channel *chan, const char *cmd, char *parse, char *buf,
+ size_t len)
+{
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(null);
+ AST_APP_ARG(reg);
+ AST_APP_ARG(str);
+ );
+ int errcode;
+ regex_t regexbuf;
+
+ buf[0] = '\0';
+
+ AST_NONSTANDARD_APP_ARGS(args, parse, '"');
+
+ if (args.argc != 3) {
+ ast_log(LOG_ERROR, "Unexpected arguments: should have been in the form '\"<regex>\" <string>'\n");
+ return -1;
+ }
+ if ((*args.str == ' ') || (*args.str == '\t'))
+ args.str++;
+
+ ast_debug(1, "FUNCTION REGEX (%s)(%s)\n", args.reg, args.str);
+
+ if ((errcode = regcomp(&regexbuf, args.reg, REG_EXTENDED | REG_NOSUB))) {
+ regerror(errcode, &regexbuf, buf, len);
+ ast_log(LOG_WARNING, "Malformed input %s(%s): %s\n", cmd, parse, buf);
+ return -1;
+ }
+
+ strcpy(buf, regexec(&regexbuf, args.str, 0, NULL, 0) ? "0" : "1");
+
+ regfree(&regexbuf);
+
+ return 0;
+}
+
+static struct ast_custom_function regex_function = {
+ .name = "REGEX",
+ .synopsis = "Regular Expression",
+ .desc =
+ "Returns 1 if data matches regular expression, or 0 otherwise.\n"
+ "Please note that the space following the double quotes separating the regex from the data\n"
+ "is optional and if present, is skipped. If a space is desired at the beginning of the data,\n"
+ "then put two spaces there; the second will not be skipped.\n",
+ .syntax = "REGEX(\"<regular expression>\" <data>)",
+ .read = regex,
+};
+
+#define HASH_PREFIX "~HASH~%s~"
+#define HASH_FORMAT HASH_PREFIX "%s~"
+
+static char *app_clearhash = "ClearHash";
+static char *syn_clearhash = "Clear the keys from a specified hashname";
+static char *desc_clearhash =
+"ClearHash(<hashname>)\n"
+" Clears all keys out of the specified hashname\n";
+
+/* This function probably should migrate to main/pbx.c, as pbx_builtin_clearvar_prefix() */
+static void clearvar_prefix(struct ast_channel *chan, const char *prefix)
+{
+ struct ast_var_t *var;
+ int len = strlen(prefix);
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&chan->varshead, var, entries) {
+ if (strncasecmp(prefix, ast_var_name(var), len) == 0) {
+ AST_LIST_REMOVE_CURRENT(entries);
+ ast_free(var);
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END
+}
+
+static int exec_clearhash(struct ast_channel *chan, void *data)
+{
+ char prefix[80];
+ snprintf(prefix, sizeof(prefix), HASH_PREFIX, data ? (char *)data : "null");
+ clearvar_prefix(chan, prefix);
+ return 0;
+}
+
+static int array(struct ast_channel *chan, const char *cmd, char *var,
+ const char *value)
+{
+ AST_DECLARE_APP_ARGS(arg1,
+ AST_APP_ARG(var)[100];
+ );
+ AST_DECLARE_APP_ARGS(arg2,
+ AST_APP_ARG(val)[100];
+ );
+ char *origvar = "", *value2, varname[256];
+ int i, ishash = 0;
+
+ value2 = ast_strdupa(value);
+ if (!var || !value2)
+ return -1;
+
+ if (chan)
+ ast_autoservice_start(chan);
+
+ if (!strcmp(cmd, "HASH")) {
+ const char *var2 = pbx_builtin_getvar_helper(chan, "~ODBCFIELDS~");
+ origvar = var;
+ if (var2)
+ var = ast_strdupa(var2);
+ else {
+ if (chan)
+ ast_autoservice_stop(chan);
+ return -1;
+ }
+ ishash = 1;
+ }
+
+ /* The functions this will generally be used with are SORT and ODBC_*, which
+ * both return comma-delimited lists. However, if somebody uses literal lists,
+ * their commas will be translated to vertical bars by the load, and I don't
+ * want them to be surprised by the result. Hence, we prefer commas as the
+ * delimiter, but we'll fall back to vertical bars if commas aren't found.
+ */
+ ast_debug(1, "array (%s=%s)\n", var, value2);
+ AST_STANDARD_APP_ARGS(arg1, var);
+
+ AST_STANDARD_APP_ARGS(arg2, value2);
+
+ for (i = 0; i < arg1.argc; i++) {
+ ast_debug(1, "array set value (%s=%s)\n", arg1.var[i],
+ arg2.val[i]);
+ if (i < arg2.argc) {
+ if (ishash) {
+ snprintf(varname, sizeof(varname), HASH_FORMAT, origvar, arg1.var[i]);
+ pbx_builtin_setvar_helper(chan, varname, arg2.val[i]);
+ } else {
+ pbx_builtin_setvar_helper(chan, arg1.var[i], arg2.val[i]);
+ }
+ } else {
+ /* We could unset the variable, by passing a NULL, but due to
+ * pushvar semantics, that could create some undesired behavior. */
+ if (ishash) {
+ snprintf(varname, sizeof(varname), HASH_FORMAT, origvar, arg1.var[i]);
+ pbx_builtin_setvar_helper(chan, varname, "");
+ } else {
+ pbx_builtin_setvar_helper(chan, arg1.var[i], "");
+ }
+ }
+ }
+
+ if (chan)
+ ast_autoservice_stop(chan);
+
+ return 0;
+}
+
+static int hashkeys_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
+{
+ struct ast_var_t *newvar;
+ int plen;
+ char prefix[80];
+ snprintf(prefix, sizeof(prefix), HASH_PREFIX, data);
+ plen = strlen(prefix);
+
+ memset(buf, 0, len);
+ AST_LIST_TRAVERSE(&chan->varshead, newvar, entries) {
+ if (strncasecmp(prefix, ast_var_name(newvar), plen) == 0) {
+ /* Copy everything after the prefix */
+ strncat(buf, ast_var_name(newvar) + plen, len);
+ /* Trim the trailing ~ */
+ buf[strlen(buf) - 1] = ',';
+ }
+ }
+ /* Trim the trailing comma */
+ buf[strlen(buf) - 1] = '\0';
+ return 0;
+}
+
+static int hash_write(struct ast_channel *chan, const char *cmd, char *var, const char *value)
+{
+ char varname[256];
+ AST_DECLARE_APP_ARGS(arg,
+ AST_APP_ARG(hashname);
+ AST_APP_ARG(hashkey);
+ );
+
+ if (!strchr(var, ',')) {
+ /* Single argument version */
+ return array(chan, "HASH", var, value);
+ }
+
+ AST_STANDARD_APP_ARGS(arg, var);
+ snprintf(varname, sizeof(varname), HASH_FORMAT, arg.hashname, arg.hashkey);
+ pbx_builtin_setvar_helper(chan, varname, value);
+
+ return 0;
+}
+
+static int hash_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
+{
+ char varname[256];
+ const char *varvalue;
+ AST_DECLARE_APP_ARGS(arg,
+ AST_APP_ARG(hashname);
+ AST_APP_ARG(hashkey);
+ );
+
+ AST_STANDARD_APP_ARGS(arg, data);
+ if (arg.argc == 2) {
+ snprintf(varname, sizeof(varname), HASH_FORMAT, arg.hashname, arg.hashkey);
+ varvalue = pbx_builtin_getvar_helper(chan, varname);
+ if (varvalue)
+ ast_copy_string(buf, varvalue, len);
+ else
+ *buf = '\0';
+ } else if (arg.argc == 1) {
+ char colnames[4096];
+ int i;
+ AST_DECLARE_APP_ARGS(arg2,
+ AST_APP_ARG(col)[100];
+ );
+
+ /* Get column names, in no particular order */
+ hashkeys_read(chan, "HASHKEYS", arg.hashname, colnames, sizeof(colnames));
+ pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", colnames);
+
+ AST_STANDARD_APP_ARGS(arg2, colnames);
+ *buf = '\0';
+
+ /* Now get the corresponding column values, in exactly the same order */
+ for (i = 0; i < arg2.argc; i++) {
+ snprintf(varname, sizeof(varname), HASH_FORMAT, arg.hashname, arg2.col[i]);
+ varvalue = pbx_builtin_getvar_helper(chan, varname);
+ strncat(buf, varvalue, len);
+ strncat(buf, ",", len);
+ }
+
+ /* Strip trailing comma */
+ buf[strlen(buf) - 1] = '\0';
+ }
+
+ return 0;
+}
+
+static struct ast_custom_function hash_function = {
+ .name = "HASH",
+ .synopsis = "Implementation of a dialplan associative array",
+ .syntax = "HASH(hashname[,hashkey])",
+ .write = hash_write,
+ .read = hash_read,
+ .desc =
+ "In two argument mode, gets and sets values to corresponding keys within a named\n"
+ "associative array. The single-argument mode will only work when assigned to from\n"
+ "a function defined by func_odbc.so.\n",
+};
+
+static struct ast_custom_function hashkeys_function = {
+ .name = "HASHKEYS",
+ .synopsis = "Retrieve the keys of a HASH()",
+ .syntax = "HASHKEYS(<hashname>)",
+ .read = hashkeys_read,
+ .desc =
+ "Returns a comma-delimited list of the current keys of an associative array\n"
+ "defined by the HASH() function. Note that if you iterate over the keys of\n"
+ "the result, adding keys during iteration will cause the result of the HASHKEYS\n"
+ "function to change.\n",
+};
+
+static struct ast_custom_function array_function = {
+ .name = "ARRAY",
+ .synopsis = "Allows setting multiple variables at once",
+ .syntax = "ARRAY(var1[,var2[...][,varN]])",
+ .write = array,
+ .desc =
+ "The comma-separated list passed as a value to which the function is set will\n"
+ "be interpreted as a set of values to which the comma-separated list of\n"
+ "variable names in the argument should be set.\n"
+ "Hence, Set(ARRAY(var1,var2)=1,2) will set var1 to 1 and var2 to 2.\n",
+};
+
+static int acf_sprintf(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
+{
+#define SPRINTF_FLAG 0
+#define SPRINTF_WIDTH 1
+#define SPRINTF_PRECISION 2
+#define SPRINTF_LENGTH 3
+#define SPRINTF_CONVERSION 4
+ int i, state = -1, argcount = 0;
+ char *formatstart = NULL, *bufptr = buf;
+ char formatbuf[256] = "";
+ int tmpi;
+ double tmpd;
+ AST_DECLARE_APP_ARGS(arg,
+ AST_APP_ARG(format);
+ AST_APP_ARG(var)[100];
+ );
+
+ AST_STANDARD_APP_ARGS(arg, data);
+
+ /* Scan the format, converting each argument into the requisite format type. */
+ for (i = 0; arg.format[i]; i++) {
+ switch (state) {
+ case SPRINTF_FLAG:
+ if (strchr("#0- +'I", arg.format[i]))
+ break;
+ state = SPRINTF_WIDTH;
+ case SPRINTF_WIDTH:
+ if (arg.format[i] >= '0' && arg.format[i] <= '9')
+ break;
+
+ /* Next character must be a period to go into a precision */
+ if (arg.format[i] == '.') {
+ state = SPRINTF_PRECISION;
+ } else {
+ state = SPRINTF_LENGTH;
+ i--;
+ }
+ break;
+ case SPRINTF_PRECISION:
+ if (arg.format[i] >= '0' && arg.format[i] <= '9')
+ break;
+ state = SPRINTF_LENGTH;
+ case SPRINTF_LENGTH:
+ if (strchr("hl", arg.format[i])) {
+ if (arg.format[i + 1] == arg.format[i])
+ i++;
+ state = SPRINTF_CONVERSION;
+ break;
+ } else if (strchr("Lqjzt", arg.format[i]))
+ state = SPRINTF_CONVERSION;
+ break;
+ state = SPRINTF_CONVERSION;
+ case SPRINTF_CONVERSION:
+ if (strchr("diouxXc", arg.format[i])) {
+ /* Integer */
+
+ /* Isolate this format alone */
+ ast_copy_string(formatbuf, formatstart, sizeof(formatbuf));
+ formatbuf[&arg.format[i] - formatstart + 1] = '\0';
+
+ /* Convert the argument into the required type */
+ if (sscanf(arg.var[argcount++], "%d", &tmpi) != 1) {
+ ast_log(LOG_ERROR, "Argument '%s' is not an integer number for format '%s'\n", arg.var[argcount - 1], formatbuf);
+ goto sprintf_fail;
+ }
+
+ /* Format the argument */
+ snprintf(bufptr, buf + len - bufptr, formatbuf, tmpi);
+
+ /* Update the position of the next parameter to print */
+ bufptr = strchr(buf, '\0');
+ } else if (strchr("eEfFgGaA", arg.format[i])) {
+ /* Double */
+
+ /* Isolate this format alone */
+ ast_copy_string(formatbuf, formatstart, sizeof(formatbuf));
+ formatbuf[&arg.format[i] - formatstart + 1] = '\0';
+
+ /* Convert the argument into the required type */
+ if (sscanf(arg.var[argcount++], "%lf", &tmpd) != 1) {
+ ast_log(LOG_ERROR, "Argument '%s' is not a floating point number for format '%s'\n", arg.var[argcount - 1], formatbuf);
+ goto sprintf_fail;
+ }
+
+ /* Format the argument */
+ snprintf(bufptr, buf + len - bufptr, formatbuf, tmpd);
+
+ /* Update the position of the next parameter to print */
+ bufptr = strchr(buf, '\0');
+ } else if (arg.format[i] == 's') {
+ /* String */
+
+ /* Isolate this format alone */
+ ast_copy_string(formatbuf, formatstart, sizeof(formatbuf));
+ formatbuf[&arg.format[i] - formatstart + 1] = '\0';
+
+ /* Format the argument */
+ snprintf(bufptr, buf + len - bufptr, formatbuf, arg.var[argcount++]);
+
+ /* Update the position of the next parameter to print */
+ bufptr = strchr(buf, '\0');
+ } else if (arg.format[i] == '%') {
+ /* Literal data to copy */
+ *bufptr++ = arg.format[i];
+ } else {
+ /* Not supported */
+
+ /* Isolate this format alone */
+ ast_copy_string(formatbuf, formatstart, sizeof(formatbuf));
+ formatbuf[&arg.format[i] - formatstart + 1] = '\0';
+
+ ast_log(LOG_ERROR, "Format type not supported: '%s' with argument '%s'\n", formatbuf, arg.var[argcount++]);
+ goto sprintf_fail;
+ }
+ state = -1;
+ break;
+ default:
+ if (arg.format[i] == '%') {
+ state = SPRINTF_FLAG;
+ formatstart = &arg.format[i];
+ break;
+ } else {
+ /* Literal data to copy */
+ *bufptr++ = arg.format[i];
+ }
+ }
+ }
+ return 0;
+sprintf_fail:
+ return -1;
+}
+
+static struct ast_custom_function sprintf_function = {
+ .name = "SPRINTF",
+ .synopsis = "Format a variable according to a format string",
+ .syntax = "SPRINTF(<format>,<arg1>[,...<argN>])",
+ .read = acf_sprintf,
+ .desc =
+"Parses the format string specified and returns a string matching that format.\n"
+"Supports most options supported by sprintf(3). Returns a shortened string if\n"
+"a format specifier is not recognized.\n",
+};
+
+static int quote(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
+{
+ char *bufptr = buf, *dataptr = data;
+ *bufptr++ = '"';
+ for (; bufptr < buf + len - 1; dataptr++) {
+ if (*dataptr == '\\') {
+ *bufptr++ = '\\';
+ *bufptr++ = '\\';
+ } else if (*dataptr == '"') {
+ *bufptr++ = '\\';
+ *bufptr++ = '"';
+ } else if (*dataptr == '\0') {
+ break;
+ } else {
+ *bufptr++ = *dataptr;
+ }
+ }
+ *bufptr++ = '"';
+ *bufptr = '\0';
+ return 0;
+}
+
+static struct ast_custom_function quote_function = {
+ .name = "QUOTE",
+ .synopsis = "Quotes a given string, escaping embedded quotes as necessary",
+ .syntax = "QUOTE(<string>)",
+ .read = quote,
+};
+
+
+static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf,
+ size_t len)
+{
+ int length = 0;
+
+ if (data)
+ length = strlen(data);
+
+ snprintf(buf, len, "%d", length);
+
+ return 0;
+}
+
+static struct ast_custom_function len_function = {
+ .name = "LEN",
+ .synopsis = "Returns the length of the argument given",
+ .syntax = "LEN(<string>)",
+ .read = len,
+};
+
+static int acf_strftime(struct ast_channel *chan, const char *cmd, char *parse,
+ char *buf, size_t len)
+{
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(epoch);
+ AST_APP_ARG(timezone);
+ AST_APP_ARG(format);
+ );
+ struct timeval tv;
+ struct ast_tm tm;
+
+ buf[0] = '\0';
+
+ AST_STANDARD_APP_ARGS(args, parse);
+
+ ast_get_timeval(args.epoch, &tv, ast_tvnow(), NULL);
+ ast_localtime(&tv, &tm, args.timezone);
+
+ if (!args.format)
+ args.format = "%c";
+
+ if (ast_strftime(buf, len, args.format, &tm) <= 0)
+ ast_log(LOG_WARNING, "C function strftime() output nothing?!!\n");
+
+ buf[len - 1] = '\0';
+
+ return 0;
+}
+
+static struct ast_custom_function strftime_function = {
+ .name = "STRFTIME",
+ .synopsis = "Returns the current date/time in a specified format.",
+ .syntax = "STRFTIME([<epoch>][,[timezone][,format]])",
+ .desc =
+"STRFTIME sports all of the same formats as the underlying C function\n"
+"strftime(3) - see the man page for details. It also supports the\n"
+"following format:\n"
+" %[n]q - fractions of a second, with leading zeroes. For example, %3q will\n"
+" give milliseconds and %1q will give tenths of a second. The default\n"
+" is to output milliseconds (n=3). The common case is to use it in\n"
+" combination with %S, as in \"%S.%3q\".\n",
+ .read = acf_strftime,
+};
+
+static int acf_strptime(struct ast_channel *chan, const char *cmd, char *data,
+ char *buf, size_t len)
+{
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(timestring);
+ AST_APP_ARG(timezone);
+ AST_APP_ARG(format);
+ );
+ union {
+ struct ast_tm atm;
+ struct tm time;
+ } t = { { 0, }, };
+
+ buf[0] = '\0';
+
+ if (!data) {
+ ast_log(LOG_ERROR,
+ "Asterisk function STRPTIME() requires an argument.\n");
+ return -1;
+ }
+
+ AST_STANDARD_APP_ARGS(args, data);
+
+ if (ast_strlen_zero(args.format)) {
+ ast_log(LOG_ERROR,
+ "No format supplied to STRPTIME(<timestring>,<timezone>,<format>)");
+ return -1;
+ }
+
+ if (!strptime(args.timestring, args.format, &t.time)) {
+ ast_log(LOG_WARNING, "C function strptime() output nothing?!!\n");
+ } else {
+ struct timeval tv = ast_mktime(&t.atm, args.timezone);
+ snprintf(buf, len, "%d", (int) tv.tv_sec);
+ }
+
+ return 0;
+}
+
+static struct ast_custom_function strptime_function = {
+ .name = "STRPTIME",
+ .synopsis =
+ "Returns the epoch of the arbitrary date/time string structured as described in the format.",
+ .syntax = "STRPTIME(<datetime>,<timezone>,<format>)",
+ .desc =
+ "This is useful for converting a date into an EPOCH time, possibly to pass to\n"
+ "an application like SayUnixTime or to calculate the difference between two\n"
+ "date strings.\n"
+ "\n"
+ "Example:\n"
+ " ${STRPTIME(2006-03-01 07:30:35,America/Chicago,%Y-%m-%d %H:%M:%S)} returns 1141219835\n",
+ .read = acf_strptime,
+};
+
+static int function_eval(struct ast_channel *chan, const char *cmd, char *data,
+ char *buf, size_t len)
+{
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "EVAL requires an argument: EVAL(<string>)\n");
+ return -1;
+ }
+
+ if (chan)
+ ast_autoservice_start(chan);
+ pbx_substitute_variables_helper(chan, data, buf, len - 1);
+ if (chan)
+ ast_autoservice_stop(chan);
+
+ return 0;
+}
+
+static struct ast_custom_function eval_function = {
+ .name = "EVAL",
+ .synopsis = "Evaluate stored variables.",
+ .syntax = "EVAL(<variable>)",
+ .desc = "Using EVAL basically causes a string to be evaluated twice.\n"
+ "When a variable or expression is in the dialplan, it will be\n"
+ "evaluated at runtime. However, if the result of the evaluation\n"
+ "is in fact a variable or expression, using EVAL will have it\n"
+ "evaluated a second time. For example, if the variable ${MYVAR}\n"
+ "contains \"${OTHERVAR}\", then the result of putting ${EVAL(${MYVAR})}\n"
+ "in the dialplan will be the contents of the variable, OTHERVAR.\n"
+ "Normally, by just putting ${MYVAR} in the dialplan, you would be\n"
+ "left with \"${OTHERVAR}\".\n",
+ .read = function_eval,
+};
+
+static int keypadhash(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
+{
+ char *bufptr, *dataptr;
+
+ for (bufptr = buf, dataptr = data; bufptr < buf + len - 1; dataptr++) {
+ if (*dataptr == '1') {
+ *bufptr++ = '1';
+ } else if (strchr("AaBbCc2", *dataptr)) {
+ *bufptr++ = '2';
+ } else if (strchr("DdEeFf3", *dataptr)) {
+ *bufptr++ = '3';
+ } else if (strchr("GgHhIi4", *dataptr)) {
+ *bufptr++ = '4';
+ } else if (strchr("JjKkLl5", *dataptr)) {
+ *bufptr++ = '5';
+ } else if (strchr("MmNnOo6", *dataptr)) {
+ *bufptr++ = '6';
+ } else if (strchr("PpQqRrSs7", *dataptr)) {
+ *bufptr++ = '7';
+ } else if (strchr("TtUuVv8", *dataptr)) {
+ *bufptr++ = '8';
+ } else if (strchr("WwXxYyZz9", *dataptr)) {
+ *bufptr++ = '9';
+ } else if (*dataptr == '0') {
+ *bufptr++ = '0';
+ } else if (*dataptr == '\0') {
+ *bufptr++ = '\0';
+ break;
+ }
+ }
+ buf[len - 1] = '\0';
+
+ return 0;
+}
+
+static struct ast_custom_function keypadhash_function = {
+ .name = "KEYPADHASH",
+ .synopsis = "Hash the letters in the string into the equivalent keypad numbers.",
+ .syntax = "KEYPADHASH(<string>)",
+ .read = keypadhash,
+ .desc = "Example: ${KEYPADHASH(Les)} returns \"537\"\n",
+};
+
+static int string_toupper(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
+{
+ char *bufptr = buf, *dataptr = data;
+
+ while ((bufptr < buf + len - 1) && (*bufptr++ = toupper(*dataptr++)));
+
+ return 0;
+}
+
+static struct ast_custom_function toupper_function = {
+ .name = "TOUPPER",
+ .synopsis = "Convert the string to upper case.",
+ .syntax = "TOUPPER(<string>)",
+ .read = string_toupper,
+ .desc = "Example: ${TOUPPER(Example)} returns \"EXAMPLE\"\n",
+};
+
+static int string_tolower(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
+{
+ char *bufptr = buf, *dataptr = data;
+
+ while ((bufptr < buf + len - 1) && (*bufptr++ = tolower(*dataptr++)));
+
+ return 0;
+}
+
+static struct ast_custom_function tolower_function = {
+ .name = "TOLOWER",
+ .synopsis = "Convert the string to lower case.",
+ .syntax = "TOLOWER(<string>)",
+ .read = string_tolower,
+ .desc = "Example: ${TOLOWER(Example)} returns \"example\"\n",
+};
+
+static int unload_module(void)
+{
+ int res = 0;
+
+ res |= ast_custom_function_unregister(&fieldqty_function);
+ res |= ast_custom_function_unregister(&filter_function);
+ res |= ast_custom_function_unregister(&regex_function);
+ res |= ast_custom_function_unregister(&array_function);
+ res |= ast_custom_function_unregister(&quote_function);
+ res |= ast_custom_function_unregister(&len_function);
+ res |= ast_custom_function_unregister(&strftime_function);
+ res |= ast_custom_function_unregister(&strptime_function);
+ res |= ast_custom_function_unregister(&eval_function);
+ res |= ast_custom_function_unregister(&keypadhash_function);
+ res |= ast_custom_function_unregister(&sprintf_function);
+ res |= ast_custom_function_unregister(&hashkeys_function);
+ res |= ast_custom_function_unregister(&hash_function);
+ res |= ast_unregister_application(app_clearhash);
+ res |= ast_custom_function_unregister(&toupper_function);
+ res |= ast_custom_function_unregister(&tolower_function);
+
+ return res;
+}
+
+static int load_module(void)
+{
+ int res = 0;
+
+ res |= ast_custom_function_register(&fieldqty_function);
+ res |= ast_custom_function_register(&filter_function);
+ res |= ast_custom_function_register(&regex_function);
+ res |= ast_custom_function_register(&array_function);
+ res |= ast_custom_function_register(&quote_function);
+ res |= ast_custom_function_register(&len_function);
+ res |= ast_custom_function_register(&strftime_function);
+ res |= ast_custom_function_register(&strptime_function);
+ res |= ast_custom_function_register(&eval_function);
+ res |= ast_custom_function_register(&keypadhash_function);
+ res |= ast_custom_function_register(&sprintf_function);
+ res |= ast_custom_function_register(&hashkeys_function);
+ res |= ast_custom_function_register(&hash_function);
+ res |= ast_register_application(app_clearhash, exec_clearhash, syn_clearhash, desc_clearhash);
+ res |= ast_custom_function_register(&toupper_function);
+ res |= ast_custom_function_register(&tolower_function);
+
+ return res;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "String handling dialplan functions");
diff --git a/trunk/funcs/func_sysinfo.c b/trunk/funcs/func_sysinfo.c
new file mode 100644
index 000000000..b386b9fb4
--- /dev/null
+++ b/trunk/funcs/func_sysinfo.c
@@ -0,0 +1,116 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2007, Digium, Inc.
+ *
+ * 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
+ *
+ * SYSINFO function to return various system data.
+ *
+ * \note Inspiration and Guidance from Russell
+ *
+ * \author Jeff Peeler
+ *
+ * \ingroup functions
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision: 87233 $")
+
+#if defined(HAVE_SYSINFO)
+#include <sys/sysinfo.h>
+#endif
+
+#include "asterisk/module.h"
+#include "asterisk/pbx.h"
+
+static int sysinfo_helper(struct ast_channel *chan, const char *cmd, char *data,
+ char *buf, size_t len)
+{
+#if defined(HAVE_SYSINFO)
+ struct sysinfo sys_info;
+ if (sysinfo(&sys_info)) {
+ ast_log(LOG_ERROR, "FAILED to retrieve system information\n");
+ return -1;
+ }
+#endif
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "Syntax: ${SYSINFO(<parameter>)} - missing argument!)\n");
+ return -1;
+ } else if (!strcasecmp("loadavg", data)) {
+ double curloadavg;
+ getloadavg(&curloadavg, 1);
+ snprintf(buf, len, "%f", curloadavg);
+ } else if (!strcasecmp("numcalls", data)) {
+ snprintf(buf, len, "%d", ast_active_calls());
+ }
+#if defined(HAVE_SYSINFO)
+ else if (!strcasecmp("uptime", data)) { /* in hours */
+ snprintf(buf, len, "%ld", sys_info.uptime/3600);
+ } else if (!strcasecmp("totalram", data)) { /* in KiB */
+ snprintf(buf, len, "%ld",(sys_info.totalram / sys_info.mem_unit)/1024);
+ } else if (!strcasecmp("freeram", data)) { /* in KiB */
+ snprintf(buf, len, "%ld",(sys_info.freeram / sys_info.mem_unit)/1024);
+ } else if (!strcasecmp("bufferram", data)) { /* in KiB */
+ snprintf(buf, len, "%ld",(sys_info.bufferram / sys_info.mem_unit)/1024);
+ } else if (!strcasecmp("totalswap", data)) { /* in KiB */
+ snprintf(buf, len, "%ld",(sys_info.totalswap / sys_info.mem_unit)/1024);
+ } else if (!strcasecmp("freeswap", data)) { /* in KiB */
+ snprintf(buf, len, "%ld",(sys_info.freeswap / sys_info.mem_unit)/1024);
+ } else if (!strcasecmp("numprocs", data)) {
+ snprintf(buf, len, "%d", sys_info.procs);
+ }
+#endif
+ else {
+ ast_log(LOG_ERROR, "Unknown sysinfo parameter type '%s'.\n", data);
+ return -1;
+ }
+
+ return 0;
+}
+
+static struct ast_custom_function sysinfo_function = {
+ .name = "SYSINFO",
+ .synopsis = "Returns system information specified by parameter.",
+ .syntax = "SYSINFO(<parameter>)",
+ .read = sysinfo_helper,
+ .desc =
+"Returns information from a given parameter\n"
+" Options:\n"
+" loadavg - system load average from past minute\n"
+" numcalls - number of active calls currently in progress\n"
+#if defined(HAVE_SYSINFO)
+" uptime - system uptime in hours\n"
+" totalram - total usable main memory size in KiB\n"
+" freeram - available memory size in KiB\n"
+" bufferram - memory used by buffers in KiB\n"
+" totalswap - total swap space size in KiB\n"
+" freeswap - free swap space still available in KiB\n"
+" numprocs - number of current processes\n",
+#endif /* HAVE_SYSINFO */
+};
+
+static int unload_module(void)
+{
+ return ast_custom_function_unregister(&sysinfo_function);
+}
+
+static int load_module(void)
+{
+ return ast_custom_function_register(&sysinfo_function);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "System information related functions");
+
diff --git a/trunk/funcs/func_timeout.c b/trunk/funcs/func_timeout.c
new file mode 100644
index 000000000..a42718a6d
--- /dev/null
+++ b/trunk/funcs/func_timeout.c
@@ -0,0 +1,184 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2006, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.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 Channel timeout related dialplan functions
+ *
+ * \author Mark Spencer <markster@digium.com>
+ * \ingroup functions
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/module.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/utils.h"
+#include "asterisk/app.h"
+
+static int timeout_read(struct ast_channel *chan, const char *cmd, char *data,
+ char *buf, size_t len)
+{
+ time_t myt;
+
+ if (!chan)
+ return -1;
+
+ if (!data) {
+ ast_log(LOG_ERROR, "Must specify type of timeout to get.\n");
+ return -1;
+ }
+
+ switch (*data) {
+ case 'a':
+ case 'A':
+ if (chan->whentohangup == 0) {
+ ast_copy_string(buf, "0", len);
+ } else {
+ time(&myt);
+ snprintf(buf, len, "%d", (int) (chan->whentohangup - myt));
+ }
+ break;
+
+ case 'r':
+ case 'R':
+ if (chan->pbx) {
+ snprintf(buf, len, "%d", chan->pbx->rtimeout);
+ }
+ break;
+
+ case 'd':
+ case 'D':
+ if (chan->pbx) {
+ snprintf(buf, len, "%d", chan->pbx->dtimeout);
+ }
+ break;
+
+ default:
+ ast_log(LOG_ERROR, "Unknown timeout type specified.\n");
+ break;
+ }
+
+ return 0;
+}
+
+static int timeout_write(struct ast_channel *chan, const char *cmd, char *data,
+ const char *value)
+{
+ int x;
+ char timestr[64];
+ struct ast_tm myt;
+
+ if (!chan)
+ return -1;
+
+ if (!data) {
+ ast_log(LOG_ERROR, "Must specify type of timeout to set.\n");
+ return -1;
+ }
+
+ if (!value)
+ return -1;
+
+ x = atoi(value);
+ if (x < 0)
+ x = 0;
+
+ switch (*data) {
+ case 'a':
+ case 'A':
+ ast_channel_setwhentohangup(chan, x);
+ if (VERBOSITY_ATLEAST(3)) {
+ if (chan->whentohangup) {
+ struct timeval tv = { chan->whentohangup, 0 };
+ ast_strftime(timestr, sizeof(timestr), "%Y-%m-%d %H:%M:%S.%3q %Z",
+ ast_localtime(&tv, &myt, NULL));
+ ast_verbose("Channel will hangup at %s.\n", timestr);
+ } else {
+ ast_verbose("Channel hangup cancelled.\n");
+ }
+ }
+ break;
+
+ case 'r':
+ case 'R':
+ if (chan->pbx) {
+ chan->pbx->rtimeout = x;
+ ast_verb(3, "Response timeout set to %d\n", chan->pbx->rtimeout);
+ }
+ break;
+
+ case 'd':
+ case 'D':
+ if (chan->pbx) {
+ chan->pbx->dtimeout = x;
+ ast_verb(3, "Digit timeout set to %d\n", chan->pbx->dtimeout);
+ }
+ break;
+
+ default:
+ ast_log(LOG_ERROR, "Unknown timeout type specified.\n");
+ break;
+ }
+
+ return 0;
+}
+
+static struct ast_custom_function timeout_function = {
+ .name = "TIMEOUT",
+ .synopsis = "Gets or sets timeouts on the channel. Timeout values are in seconds.",
+ .syntax = "TIMEOUT(timeouttype)",
+ .desc =
+ "Gets or sets various channel timeouts. The timeouts that can be\n"
+ "manipulated are:\n" "\n"
+ "absolute: The absolute maximum amount of time permitted for a call. A\n"
+ " setting of 0 disables the timeout.\n" "\n"
+ "digit: The maximum amount of time permitted between digits when the\n"
+ " user is typing in an extension. When this timeout expires,\n"
+ " after the user has started to type in an extension, the\n"
+ " extension will be considered complete, and will be\n"
+ " interpreted. Note that if an extension typed in is valid,\n"
+ " it will not have to timeout to be tested, so typically at\n"
+ " the expiry of this timeout, the extension will be considered\n"
+ " invalid (and thus control would be passed to the 'i'\n"
+ " extension, or if it doesn't exist the call would be\n"
+ " terminated). The default timeout is 5 seconds.\n" "\n"
+ "response: The maximum amount of time permitted after falling through a\n"
+ " series of priorities for a channel in which the user may\n"
+ " begin typing an extension. If the user does not type an\n"
+ " extension in this amount of time, control will pass to the\n"
+ " 't' extension if it exists, and if not the call would be\n"
+ " terminated. The default timeout is 10 seconds.\n",
+ .read = timeout_read,
+ .write = timeout_write,
+};
+
+static int unload_module(void)
+{
+ return ast_custom_function_unregister(&timeout_function);
+}
+
+static int load_module(void)
+{
+ return ast_custom_function_register(&timeout_function);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Channel timeout dialplan functions");
diff --git a/trunk/funcs/func_uri.c b/trunk/funcs/func_uri.c
new file mode 100644
index 000000000..94ae220b6
--- /dev/null
+++ b/trunk/funcs/func_uri.c
@@ -0,0 +1,96 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2006, Digium, Inc.
+ *
+ * Created by Olle E. Johansson, Edvina.net
+ *
+ * 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 URI encoding / decoding
+ *
+ * \author Olle E. Johansson <oej@edvina.net>
+ *
+ * \note For now this code only supports 8 bit characters, not unicode,
+ which we ultimately will need to support.
+ *
+ * \ingroup functions
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/module.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/utils.h"
+#include "asterisk/app.h"
+
+/*! \brief uriencode: Encode URL according to RFC 2396 */
+static int uriencode(struct ast_channel *chan, const char *cmd, char *data,
+ char *buf, size_t len)
+{
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "Syntax: URIENCODE(<data>) - missing argument!\n");
+ return -1;
+ }
+
+ ast_uri_encode(data, buf, len, 1);
+
+ return 0;
+}
+
+/*!\brief uridecode: Decode URI according to RFC 2396 */
+static int uridecode(struct ast_channel *chan, const char *cmd, char *data,
+ char *buf, size_t len)
+{
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "Syntax: URIDECODE(<data>) - missing argument!\n");
+ return -1;
+ }
+
+ ast_copy_string(buf, data, len);
+ ast_uri_decode(buf);
+
+ return 0;
+}
+
+static struct ast_custom_function urldecode_function = {
+ .name = "URIDECODE",
+ .synopsis = "Decodes a URI-encoded string according to RFC 2396.",
+ .syntax = "URIDECODE(<data>)",
+ .read = uridecode,
+};
+
+static struct ast_custom_function urlencode_function = {
+ .name = "URIENCODE",
+ .synopsis = "Encodes a string to URI-safe encoding according to RFC 2396.",
+ .syntax = "URIENCODE(<data>)",
+ .read = uriencode,
+};
+
+static int unload_module(void)
+{
+ return ast_custom_function_unregister(&urldecode_function)
+ || ast_custom_function_unregister(&urlencode_function);
+}
+
+static int load_module(void)
+{
+ return ast_custom_function_register(&urldecode_function)
+ || ast_custom_function_register(&urlencode_function);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "URI encode/decode dialplan functions");
diff --git a/trunk/funcs/func_version.c b/trunk/funcs/func_version.c
new file mode 100644
index 000000000..8d1250375
--- /dev/null
+++ b/trunk/funcs/func_version.c
@@ -0,0 +1,101 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2006, Digium, Inc.
+ *
+ * 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 Return the current Version strings
+ *
+ * \author Steve Murphy (murf@digium.com)
+ * \ingroup functions
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/module.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/utils.h"
+#include "asterisk/app.h"
+#include "asterisk/version.h"
+#include "asterisk/build.h"
+
+static int acf_version_exec(struct ast_channel *chan, const char *cmd,
+ char *parse, char *buffer, size_t buflen)
+{
+ const char *response_char = ast_get_version();
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(info);
+ );
+
+ AST_STANDARD_APP_ARGS(args, parse);
+
+ if (!ast_strlen_zero(args.info) ) {
+ if (!strcasecmp(args.info,"ASTERISK_VERSION_NUM"))
+ response_char = ast_get_version_num();
+ else if (!strcasecmp(args.info,"BUILD_USER"))
+ response_char = BUILD_USER;
+ else if (!strcasecmp(args.info,"BUILD_HOSTNAME"))
+ response_char = BUILD_HOSTNAME;
+ else if (!strcasecmp(args.info,"BUILD_MACHINE"))
+ response_char = BUILD_MACHINE;
+ else if (!strcasecmp(args.info,"BUILD_KERNEL"))
+ response_char = BUILD_KERNEL;
+ else if (!strcasecmp(args.info,"BUILD_OS"))
+ response_char = BUILD_OS;
+ else if (!strcasecmp(args.info,"BUILD_DATE"))
+ response_char = BUILD_DATE;
+ }
+
+ ast_debug(1, "VERSION returns %s result, given %s argument\n", response_char, args.info);
+
+ snprintf(buffer, buflen, "%s", response_char);
+
+ return 0;
+}
+
+static struct ast_custom_function acf_version = {
+ .name = "VERSION",
+ .synopsis = "Return the Version info for this Asterisk",
+ .syntax = "VERSION([info])",
+ .desc =
+ "If there are no arguments, return the version of Asterisk in this format: SVN-branch-1.4-r44830M\n"
+ "If the argument is 'ASTERISK_VERSION_NUM', a string of digits is returned (right now fixed at 999999).\n"
+ "If the argument is 'BUILD_USER', the string representing the user's name whose account was used to configure Asterisk, is returned.\n"
+ "If the argument is 'BUILD_HOSTNAME', the string representing the name of the host on which Asterisk was configured, is returned.\n"
+ "If the argument is 'BUILD_MACHINE', the string representing the type of machine on which Asterisk was configured, is returned.\n"
+ "If the argument is 'BUILD_OS', the string representing the OS of the machine on which Asterisk was configured, is returned.\n"
+ "If the argument is 'BUILD_DATE', the string representing the date on which Asterisk was configured, is returned.\n"
+ "If the argument is 'BUILD_KERNEL', the string representing the kernel version of the machine on which Asterisk was configured, is returned .\n"
+ " Example: Set(junky=${VERSION()}; \n"
+ " Sets junky to the string 'SVN-branch-1.6-r74830M', or possibly, 'SVN-trunk-r45126M'.\n",
+ .read = acf_version_exec,
+};
+
+static int unload_module(void)
+{
+ ast_custom_function_unregister(&acf_version);
+
+ return 0;
+}
+
+static int load_module(void)
+{
+ return ast_custom_function_register(&acf_version);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Get Asterisk Version/Build Info");
diff --git a/trunk/funcs/func_vmcount.c b/trunk/funcs/func_vmcount.c
new file mode 100644
index 000000000..24d83140d
--- /dev/null
+++ b/trunk/funcs/func_vmcount.c
@@ -0,0 +1,93 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (c) 2006 Tilghman Lesher. All rights reserved.
+ *
+ * Tilghman Lesher <asterisk-vmcount-func@the-tilghman.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 VMCOUNT dialplan function
+ *
+ * \author Tilghman Lesher <asterisk-vmcount-func@the-tilghman.com>
+ *
+ * \ingroup functions
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <dirent.h>
+
+#include "asterisk/file.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/lock.h"
+#include "asterisk/utils.h"
+#include "asterisk/app.h"
+
+static int acf_vmcount_exec(struct ast_channel *chan, const char *cmd, char *argsstr, char *buf, size_t len)
+{
+ char *context;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(vmbox);
+ AST_APP_ARG(folder);
+ );
+
+ buf[0] = '\0';
+
+ if (ast_strlen_zero(argsstr))
+ return -1;
+
+ AST_STANDARD_APP_ARGS(args, argsstr);
+
+ if (strchr(args.vmbox, '@')) {
+ context = args.vmbox;
+ args.vmbox = strsep(&context, "@");
+ } else {
+ context = "default";
+ }
+
+ if (ast_strlen_zero(args.folder)) {
+ args.folder = "INBOX";
+ }
+
+ snprintf(buf, len, "%d", ast_app_messagecount(context, args.vmbox, args.folder));
+
+ return 0;
+}
+
+struct ast_custom_function acf_vmcount = {
+ .name = "VMCOUNT",
+ .synopsis = "Counts the voicemail in a specified mailbox",
+ .syntax = "VMCOUNT(vmbox[@context][,folder])",
+ .desc =
+ " context - defaults to \"default\"\n"
+ " folder - defaults to \"INBOX\"\n",
+ .read = acf_vmcount_exec,
+};
+
+static int unload_module(void)
+{
+ return ast_custom_function_unregister(&acf_vmcount);
+}
+
+static int load_module(void)
+{
+ return ast_custom_function_register(&acf_vmcount);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Indicator for whether a voice mailbox has messages in a given folder.");
diff --git a/trunk/funcs/func_volume.c b/trunk/funcs/func_volume.c
new file mode 100644
index 000000000..9a5247f54
--- /dev/null
+++ b/trunk/funcs/func_volume.c
@@ -0,0 +1,160 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2007, Digium, Inc.
+ *
+ * Joshua Colp <jcolp@digium.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 Technology independent volume control
+ *
+ * \author Joshua Colp <jcolp@digium.com>
+ *
+ * \ingroup functions
+ *
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/module.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/utils.h"
+#include "asterisk/audiohook.h"
+
+struct volume_information {
+ struct ast_audiohook audiohook;
+ int tx_gain;
+ int rx_gain;
+};
+
+static void destroy_callback(void *data)
+{
+ struct volume_information *vi = data;
+
+ /* Destroy the audiohook, and destroy ourselves */
+ ast_audiohook_destroy(&vi->audiohook);
+ free(vi);
+
+ return;
+}
+
+/*! \brief Static structure for datastore information */
+static const struct ast_datastore_info volume_datastore = {
+ .type = "volume",
+ .destroy = destroy_callback
+};
+
+static int volume_callback(struct ast_audiohook *audiohook, struct ast_channel *chan, struct ast_frame *frame, enum ast_audiohook_direction direction)
+{
+ struct ast_datastore *datastore = NULL;
+ struct volume_information *vi = NULL;
+ int *gain = NULL;
+
+ /* If the audiohook is stopping it means the channel is shutting down.... but we let the datastore destroy take care of it */
+ if (audiohook->status == AST_AUDIOHOOK_STATUS_DONE)
+ return 0;
+
+ /* Grab datastore which contains our gain information */
+ if (!(datastore = ast_channel_datastore_find(chan, &volume_datastore, NULL)))
+ return 0;
+
+ vi = datastore->data;
+
+ /* If this is DTMF then allow them to increase/decrease the gains */
+ if (frame->frametype == AST_FRAME_DTMF) {
+ /* Only use DTMF coming from the source... not going to it */
+ if (direction != AST_AUDIOHOOK_DIRECTION_READ)
+ return 0;
+ if (frame->subclass == '*') {
+ vi->tx_gain += 1;
+ vi->rx_gain += 1;
+ } else if (frame->subclass == '#') {
+ vi->tx_gain -= 1;
+ vi->rx_gain -= 1;
+ }
+ } else if (frame->frametype == AST_FRAME_VOICE) {
+ /* Based on direction of frame grab the gain, and confirm it is applicable */
+ if (!(gain = (direction == AST_AUDIOHOOK_DIRECTION_READ) ? &vi->rx_gain : &vi->tx_gain) || !*gain)
+ return 0;
+ /* Apply gain to frame... easy as pi */
+ ast_frame_adjust_volume(frame, *gain);
+ }
+
+ return 0;
+}
+
+static int volume_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
+{
+ struct ast_datastore *datastore = NULL;
+ struct volume_information *vi = NULL;
+ int is_new = 0;
+
+ if (!(datastore = ast_channel_datastore_find(chan, &volume_datastore, NULL))) {
+ /* Allocate a new datastore to hold the reference to this volume and audiohook information */
+ if (!(datastore = ast_channel_datastore_alloc(&volume_datastore, NULL)))
+ return 0;
+ if (!(vi = ast_calloc(1, sizeof(*vi)))) {
+ ast_channel_datastore_free(datastore);
+ return 0;
+ }
+ ast_audiohook_init(&vi->audiohook, AST_AUDIOHOOK_TYPE_MANIPULATE, "Volume");
+ vi->audiohook.manipulate_callback = volume_callback;
+ ast_set_flag(&vi->audiohook, AST_AUDIOHOOK_WANTS_DTMF);
+ is_new = 1;
+ } else {
+ vi = datastore->data;
+ }
+
+ /* Adjust gain on volume information structure */
+ if (!strcasecmp(data, "tx"))
+ vi->tx_gain = atoi(value);
+ else if (!strcasecmp(data, "rx"))
+ vi->rx_gain = atoi(value);
+
+ if (is_new) {
+ datastore->data = vi;
+ ast_channel_datastore_add(chan, datastore);
+ ast_audiohook_attach(chan, &vi->audiohook);
+ }
+
+ return 0;
+}
+
+static struct ast_custom_function volume_function = {
+ .name = "VOLUME",
+ .synopsis = "Set the TX or RX volume of a channel",
+ .syntax = "VOLUME(TX|RX)",
+ .desc =
+ " The VOLUME function can be used to increase or decrease the tx or\n"
+ "rx gain of any channel. For example:\n"
+ " Set(VOLUME(TX)=3)\n"
+ " Set(VOLUME(RX)=2)\n",
+ .write = volume_write,
+};
+
+static int unload_module(void)
+{
+ return ast_custom_function_unregister(&volume_function);
+}
+
+static int load_module(void)
+{
+ return ast_custom_function_register(&volume_function);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Technology independent volume control");