/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 2007, Ritesh Bansal
*
* Ritesh Bansal , rbansal@talkplus.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 List application
*
* \author Ritesh Bansal rbansal@talkplus.com
*
* This is an application which gives name based list access in the asterisk
* dial plan code
* \ingroup applications
*/
/*** MODULEINFO
yes
***/
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision: 40722 $")
#include
#include
#include
#include
#include "asterisk/file.h"
#include "asterisk/logger.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/module.h"
#include "asterisk/lock.h"
#include "asterisk/app.h"
static char *app = "List";
static char *synopsis = "Name-based list array application.";
static char *descrip = "This application gives the user a name based list capabilities in the dialplan.\n"
"This is useful in creating array-like list in the dialplan.\n"
"The following commands are available\n"
" List(INIT|name|len|reinitialize|[var1|var2|...])\n"
" name is the name of the list in this channel. You can have many \n"
" lists with different names for the same channel.\n"
" len is the length of the list.\n"
" reinitialize has values of 0 and 1. If 1 then if the list exists\n"
" then it will be reinitailized. If 0 and the list exists then it\n"
" will return an error.\n"
" You can additionally pass up to 99 variables to be put in the list.\n"
" List(SET|name|index|[value])\n"
" index is the element you want to set and value is the value.\n"
" If value is not passed then the element value is set to NULL.\n"
" List(GET|name|index|variable)\n"
" index is the element you want to get and variable is the name of the\n"
" dialplan variable in which the value will be stored\n"
" List(LEN|name|variable)\n"
" Returns the length of the list in the variable.\n"
" List(SIZE|name|variable)\n"
" Returns size integer in the variable. Size is the number of elements\n"
" that are not null and have some value. These may not be contiguous\n"
" elements.\n"
" List(INSERT|name|index|value)\n"
" Insert a element with the value at index position. This increases the\n"
" length of the list by 1.\n"
" List(REMOVE|name|index|variable)\n"
" Remove a element at index position. This decreases the\n"
" length of the list by 1. The value removed is returned in variable.\n"
" List(DESTROY|name)\n"
" This frees up the memory and the list. This must be called before\n"
" exit of the dialplan for a call or the system will have a memory leak.\n"
" List(DUMP|name)\n"
" Dumps the list info using a LOG_NOTICE mechanism.\n"
"\n";
struct ast_list_id {
char *identifier;
struct ast_channel *chan;
int len;
char **data;
AST_LIST_ENTRY(ast_list_id) entries;
} *ast_list_id;
static AST_LIST_HEAD_STATIC(listhead, ast_list_id);
/* helpful procs */
static struct ast_list_id *app_list_find_identifier(char *identifier, struct ast_channel *chan) {
struct ast_list_id *i;
struct ast_list_id *res=NULL;
int found=0;
if (AST_LIST_LOCK(&listhead)) {
ast_log(LOG_NOTICE,"Unable to lock identifiers list\n");
} else {
AST_LIST_TRAVERSE(&listhead,i,entries) {
if (strcmp(i->identifier,identifier)==0 && i->chan == chan) {
found=1;
res=i;
break;
}
}
if (!found) {
ast_log(LOG_NOTICE,"%s:Name %s not found in identifier list\n",app, identifier);
}
AST_LIST_UNLOCK(&listhead);
}
return res;
}
static int app_list_add_identifier(char *identifier, char **data,
int len, struct ast_channel *chan) {
struct ast_list_id *i, *j, *res;
i=NULL;
j=NULL;
res = app_list_find_identifier(identifier, chan);
if(res != NULL){
ast_log(LOG_NOTICE,"%s identifier %s already in list\n", app, identifier);
return -1;
}
if (!AST_LIST_LOCK(&listhead)) {
i=malloc(sizeof(struct ast_list_id));
i->identifier=ast_strdup(identifier);
i->data=data;
i->chan=chan;
i->len=len;
AST_LIST_INSERT_HEAD(&listhead,i,entries);
AST_LIST_UNLOCK(&listhead);
}
return 0;
}
static int app_list_remove_identifier(char *identifier, struct ast_channel *chan) {
struct ast_list_id *res = NULL;
res = app_list_find_identifier(identifier, chan);
if(res == NULL){
return -1;
}
if (!AST_LIST_LOCK(&listhead)) {
AST_LIST_REMOVE(&listhead,res,entries);
AST_LIST_UNLOCK(&listhead);
}
free(res);
return 0;
}
static int app_list_init(struct ast_channel *chan, char *name, char *param)
{
/* lets parse the param */
int len = 0;
int i = 0, res = 0;
int reinit = 0;
char *parse;
AST_DECLARE_APP_ARGS(args,
AST_APP_ARG(len);
AST_APP_ARG(reinit);
AST_APP_ARG(values)[100];
);
if(ast_strlen_zero(param)) {
ast_log(LOG_NOTICE, "%s:Invalid argument need size\n", app);
return -1;
}
parse = ast_strdupa(param);
AST_STANDARD_APP_ARGS(args, parse);
if(sscanf(args.len, "%d", &len)!= 1) {
ast_log(LOG_NOTICE, "%s:Invalid argument for size %s\n", app, args.len);
return -1;
}
if((sscanf(args.reinit, "%d", &reinit)!= 1) ||
(reinit !=0 && reinit != 1)) {
ast_log(LOG_NOTICE, "%s:Invalid argument for reinitialize. Valie 0 or 1 %s\n", app, args.reinit);
return -1;
}
/* ok lets create an array */
char **data = (char **)ast_calloc(len , sizeof(char *));
for(i =0; (i< len) && (i len);
for(i=0; ilen; i++) {
if(res->data[i]) {
ast_log(LOG_NOTICE, "%s: list %s elem %d value %s\n", app, name, i, res->data[i]);
size++;
}
}
ast_log(LOG_NOTICE, "%s: list %s has %d elements populated\n", app, name, size);
}
return 0;
}
static int app_list_set(struct ast_channel *chan, char *name, char *param)
{
/* lets parse the param */
struct ast_list_id *res = NULL;
int index = 0;
char *parse;
AST_DECLARE_APP_ARGS(args,
AST_APP_ARG(index);
AST_APP_ARG(value);
);
if(ast_strlen_zero(param)) {
ast_log(LOG_NOTICE, "%s:Invalid argument need index and value\n", app);
return -1;
}
parse = ast_strdupa(param);
AST_STANDARD_APP_ARGS(args, parse);
if(args.argc > 2) {
ast_log(LOG_NOTICE, "%s:Usage List(SET|name|index|[value])\n", app);
return -1;
}
if(sscanf(args.index, "%d", &index)!= 1) {
ast_log(LOG_NOTICE, "%s:Invalid argument for index %s\n", app, args.index);
return -1;
}
res = app_list_find_identifier(name, chan);
if(!res)
return -1;
if(res->len <= index || index < 0) {
ast_log(LOG_NOTICE, "%s:index %s smaller than 0 or greater than len %d\n", app, args.index, res->len);
return -1;
}
/* lets check if there is an existing value in the index position
* If yes then free the memory and make the copy of the new value
* and store */
if(res->data[index])
free(res->data[index]);
if(args.value)
res->data[index] = strdup(args.value);
else
res->data[index] = NULL;
return 0;
}
static int app_list_get(struct ast_channel *chan, char *name, char *param)
{
/* lets parse the param */
struct ast_list_id *res = NULL;
int index = 0;
char *parse;
AST_DECLARE_APP_ARGS(args,
AST_APP_ARG(index);
AST_APP_ARG(var);
);
if(ast_strlen_zero(param)) {
ast_log(LOG_NOTICE, "%s:Invalid argument need index and variable\n", app);
return -1;
}
parse = ast_strdupa(param);
AST_STANDARD_APP_ARGS(args, parse);
if(args.argc != 2) {
ast_log(LOG_NOTICE, "%s:Usage List(GET|name|index|variable)\n", app);
return -1;
}
if(sscanf(args.index, "%d", &index)!= 1) {
ast_log(LOG_NOTICE, "%s:Invalid argument for index %s\n", app, args.index);
return -1;
}
res = app_list_find_identifier(name, chan);
if(!res)
return -1;
if(res->len <= index || index < 0) {
ast_log(LOG_NOTICE, "%s:index %s smaller than 0 or greater than len %d\n", app, args.index, res->len);
return -1;
}
pbx_builtin_setvar_helper(chan, args.var, res->data[index]);
return 0;
}
static int app_list_len(struct ast_channel *chan, char *name, char *param)
{
/* lets parse the param */
struct ast_list_id *res = NULL;
char sresult[10];
if(ast_strlen_zero(param)) {
ast_log(LOG_NOTICE, "%s:Usage List(LEN|name|variable)\n", app);
return -1;
}
res = app_list_find_identifier(name, chan);
if(!res)
return -1;
snprintf(sresult, sizeof(sresult), "%d", res->len);
pbx_builtin_setvar_helper(chan, param, sresult);
return 0;
}
static int app_list_size(struct ast_channel *chan, char *name, char *param)
{
/* lets parse the param */
struct ast_list_id *res = NULL;
int size = 0;
int i = 0;
char sresult[10];
if(ast_strlen_zero(param)) {
ast_log(LOG_NOTICE, "%s:Usage List(SIZE|name|variable)\n", app);
return -1;
}
res = app_list_find_identifier(name, chan);
if(!res)
return -1;
for(i =0 ; ilen; i++)
{
if(res->data[i]) size++;
}
snprintf(sresult, sizeof(sresult), "%d", size);
pbx_builtin_setvar_helper(chan, param, sresult);
return 0;
}
static int app_list_insert(struct ast_channel *chan, char *name, char *param)
{
struct ast_list_id *res = NULL;
int index = 0;
int i = 0;
char *parse;
char **data;
AST_DECLARE_APP_ARGS(args,
AST_APP_ARG(index);
AST_APP_ARG(value);
);
if(ast_strlen_zero(param)) {
ast_log(LOG_NOTICE, "%s:Usage LIST(INSERT|name|index|value)\n", app);
return -1;
}
parse = ast_strdupa(param);
AST_STANDARD_APP_ARGS(args, parse);
if(args.argc != 2) {
ast_log(LOG_NOTICE, "%s:Usage List(INSERT|name|index|variable)\n", app);
return -1;
}
if(sscanf(args.index, "%d", &index)!= 1) {
ast_log(LOG_NOTICE, "%s:Invalid argument for index %s\n", app, args.index);
return -1;
}
res = app_list_find_identifier(name, chan);
if(!res)
return -1;
if(res->len <= index || index < 0) {
ast_log(LOG_NOTICE, "%s:index %s smaller than 0 or greater than len %d\n", app, args.index, res->len);
return -1;
}
/* Need to increase the len of the array */
data = (char **)realloc(res->data, (res->len + 1) * sizeof(char *));
if(!data) {
ast_log(LOG_NOTICE, "%s:Failed to insert element as realloc failed\n", app);
return -1;
}
res->data = data;
res->len = res->len+1;
for(i = res->len -1; i > index; i--)
{
res->data[i] = res->data[i-1];
}
res->data[index] = strdup(args.value);
return 0;
}
static int app_list_remove(struct ast_channel *chan, char *name, char *param)
{
struct ast_list_id *res = NULL;
int index = 0;
int i = 0;
char *parse;
char **data;
AST_DECLARE_APP_ARGS(args,
AST_APP_ARG(index);
AST_APP_ARG(variable);
);
if(ast_strlen_zero(param)) {
ast_log(LOG_NOTICE, "%s:Usage LIST(REMOVE|name|index|variable)\n", app);
return -1;
}
parse = ast_strdupa(param);
AST_STANDARD_APP_ARGS(args, parse);
if(args.argc != 2) {
ast_log(LOG_NOTICE, "%s:Usage List(REMOVE|name|index|variable)\n", app);
return -1;
}
if(sscanf(args.index, "%d", &index)!= 1) {
ast_log(LOG_NOTICE, "%s:Invalid argument for index %s\n", app, args.index);
return -1;
}
res = app_list_find_identifier(name, chan);
if(!res)
return -1;
if(res->len <= index || index < 0) {
ast_log(LOG_NOTICE, "%s:index %s smaller than 0 or greater than len %d\n", app, args.index, res->len);
return -1;
}
/* Need to increase the len of the array */
pbx_builtin_setvar_helper(chan, args.variable, res->data[index]);
free(res->data[index]);
for(i = index; i len - 1; i++) {
res->data[i] = res->data[i + 1];
}
data = (char **)realloc(res->data, (res->len - 1) * sizeof(char *));
if(!data) {
ast_log(LOG_NOTICE, "%s:Failed to remove element as realloc failed\n", app);
return -1;
}
res->data = data;
res->len = res->len-1;
return 0;
}
static int app_list_destroy(struct ast_channel *chan, char *name, char *param)
{
struct ast_list_id *res;
int i = 0;
res = app_list_find_identifier(name, chan);
if(res) {
for(i=0; ilen; i++) {
if(res->data[i])
free(res->data[i]);
}
free(res->data);
app_list_remove_identifier(name, chan);
}
return 0;
}
static int app_list_exec(struct ast_channel *chan, void *data)
{
int res = 0;
char *parse;
struct ast_module_user *u;
AST_DECLARE_APP_ARGS(args,
AST_APP_ARG(command);
AST_APP_ARG(name);
AST_APP_ARG(param);
);
if (ast_strlen_zero(data)) {
ast_log(LOG_NOTICE, "%s requires argument (command|name|[other params])\n", app);
return -1;
}
u = ast_module_user_add(chan);
/* We need to make a copy of the input string if we are going to modify it! */
parse = ast_strdupa(data);
AST_STANDARD_APP_ARGS(args, parse);
if (ast_strlen_zero(args.command) || ast_strlen_zero(args.name)) {
ast_log(LOG_NOTICE, "%s requires argument (command |name|[other params])\n", app);
return -1;
}
if(strcasecmp(args.command, "INIT")==0)
res = app_list_init(chan, args.name, args.param);
else if(strcasecmp(args.command, "DUMP")==0)
res = app_list_dump(chan, args.name, args.param);
else if(strcasecmp(args.command, "SET")==0)
res = app_list_set(chan, args.name, args.param);
else if(strcasecmp(args.command, "GET")==0)
res = app_list_get(chan, args.name, args.param);
else if(strcasecmp(args.command, "LEN")==0)
res = app_list_len(chan, args.name, args.param);
else if(strcasecmp(args.command, "SIZE")==0)
res = app_list_size(chan, args.name, args.param);
else if(strcasecmp(args.command, "INSERT")==0)
res = app_list_insert(chan, args.name, args.param);
else if(strcasecmp(args.command, "REMOVE")==0)
res = app_list_remove(chan, args.name, args.param);
else if(strcasecmp(args.command, "DESTROY")==0)
res = app_list_destroy(chan, args.name, args.param);
ast_module_user_remove(u);
return res;
}
static int unload_module(void)
{
int res;
res = ast_unregister_application(app);
return res;
}
static int load_module(void)
{
return ast_register_application(app, app_list_exec, synopsis, descrip);
}
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "List Application");