/* * 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");