diff options
Diffstat (limited to 'trunk/funcs')
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(¤t->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(¤t->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(¤t->mutex) : ast_mutex_lock(¤t->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(¤t->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(®exbuf, args.reg, REG_EXTENDED | REG_NOSUB))) { + regerror(errcode, ®exbuf, buf, len); + ast_log(LOG_WARNING, "Malformed input %s(%s): %s\n", cmd, parse, buf); + return -1; + } + + strcpy(buf, regexec(®exbuf, args.str, 0, NULL, 0) ? "0" : "1"); + + regfree(®exbuf); + + 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(®ex_function); + res |= ast_custom_function_unregister(&array_function); + res |= ast_custom_function_unregister("e_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(®ex_function); + res |= ast_custom_function_register(&array_function); + res |= ast_custom_function_register("e_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"); |