Skip to content
Snippets Groups Projects
pbx_lua.c 40.3 KiB
Newer Older
  • Learn to ignore specific revisions
  • /*
     * Asterisk -- An open source telephony toolkit.
     *
     * Copyright (C) 1999 - 2007, Digium, Inc.
     *
     * Matthew Nicholson <mnicholson@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
     *
     * \author Matthew Nicholson <mnicholson@digium.com>
     * \brief Lua PBX Switch
     *
     */
    
    /*** MODULEINFO
    	<depend>lua</depend>
     ***/
    
    #include "asterisk.h"
    
    ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
    
    #include "asterisk/logger.h"
    #include "asterisk/channel.h"
    #include "asterisk/pbx.h"
    #include "asterisk/module.h"
    #include "asterisk/cli.h"
    #include "asterisk/utils.h"
    #include "asterisk/term.h"
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    #include "asterisk/paths.h"
    
    #include "asterisk/hashtab.h"
    
    #include <lua.h>
    #include <lauxlib.h>
    #include <lualib.h>
    
    
    static char *config = "extensions.lua";
    
    static char *registrar = "pbx_lua";
    
    
    #define LUA_EXT_DATA_SIZE 256
    #define LUA_BUF_SIZE 4096
    
    static char *lua_read_extensions_file(lua_State *L, long *size);
    static int lua_load_extensions(lua_State *L, struct ast_channel *chan);
    static int lua_reload_extensions(lua_State *L);
    static void lua_free_extensions(void);
    static int lua_sort_extensions(lua_State *L);
    
    static int lua_register_switches(lua_State *L);
    
    static int lua_register_hints(lua_State *L);
    
    static int lua_extension_cmp(lua_State *L);
    static int lua_find_extension(lua_State *L, const char *context, const char *exten, int priority, ast_switch_f *func, int push_func);
    static int lua_pbx_findapp(lua_State *L);
    static int lua_pbx_exec(lua_State *L);
    
    static int lua_get_variable_value(lua_State *L);
    static int lua_set_variable_value(lua_State *L);
    static int lua_get_variable(lua_State *L);
    static int lua_set_variable(lua_State *L);
    static int lua_func_read(lua_State *L);
    
    static int lua_autoservice_start(lua_State *L);
    static int lua_autoservice_stop(lua_State *L);
    static int lua_autoservice_status(lua_State *L);
    
    static int lua_check_hangup(lua_State *L);
    
    Steve Murphy's avatar
    Steve Murphy committed
    static int lua_error_function(lua_State *L);
    
    
    static void lua_update_registry(lua_State *L, const char *context, const char *exten, int priority);
    static void lua_push_variable_table(lua_State *L, const char *name);
    static void lua_create_app_table(lua_State *L);
    static void lua_create_channel_table(lua_State *L);
    static void lua_create_variable_metatable(lua_State *L);
    static void lua_create_application_metatable(lua_State *L);
    static void lua_create_autoservice_functions(lua_State *L);
    
    static void lua_create_hangup_function(lua_State *L);
    
    static void lua_state_destroy(void *data);
    
    static void lua_datastore_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan);
    
    static lua_State *lua_get_state(struct ast_channel *chan);
    
    static int exists(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data);
    static int canmatch(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data);
    static int matchmore(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data);
    static int exec(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data);
    
    AST_MUTEX_DEFINE_STATIC(config_file_lock);
    
    static char *config_file_data = NULL;
    static long config_file_size = 0;
    
    static struct ast_context *local_contexts = NULL;
    static struct ast_hashtab *local_table = NULL;
    
    
    static const struct ast_datastore_info lua_datastore = {
    	.type = "lua",
    	.destroy = lua_state_destroy,
    
    	.chan_fixup = lua_datastore_fixup,
    
    };
    
    
    /*!
     * \brief The destructor for lua_datastore
     */
    
    static void lua_state_destroy(void *data)
    
    /*!
     * \brief The fixup function for the lua_datastore.
     * \param data the datastore data, in this case it will be a lua_State
     * \param old_chan the channel we are moving from
     * \param new_chan the channel we are moving to
     *
     * This function updates our internal channel pointer.
     */
    static void lua_datastore_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan)
    {
    	lua_State *L = data;
    	lua_pushlightuserdata(L, new_chan);
    	lua_setfield(L, LUA_REGISTRYINDEX, "channel");
    }
    
    
    /*!
     * \brief [lua_CFunction] Find an app and return it in a lua table (for access from lua, don't
     * call directly)
     *
     * This function would be called in the following example as it would be found
     * in extensions.lua.
     *
     * \code
     * app.dial
     * \endcode
     */
    static int lua_pbx_findapp(lua_State *L)
    {
    	const char *app_name = luaL_checkstring(L, 2);
    	
    	lua_newtable(L);
    
    	lua_pushstring(L, "name");
    	lua_pushstring(L, app_name);
    	lua_settable(L, -3);
    
    	luaL_getmetatable(L, "application");
    	lua_setmetatable(L, -2);
    
    	return 1;
    }
    
    /*!
     * \brief [lua_CFunction] This function is part of the 'application' metatable
     * and is used to execute applications similar to pbx_exec() (for access from
     * lua, don't call directly)
     *
     * \param L the lua_State to use
     * \return nothing
     *
     * This funciton is executed as the '()' operator for apps accessed through the
     * 'app' table.
     *
     * \code
     * app.playback('demo-congrats')
     * \endcode
     */
    static int lua_pbx_exec(lua_State *L)
    {
    
    	int res, nargs = lua_gettop(L);
    
    	char data[LUA_EXT_DATA_SIZE] = "";
    
    	char *data_next = data, *app_name;
    	char *context, *exten;
    	char tmp[80], tmp2[80], tmp3[LUA_EXT_DATA_SIZE];
    	int priority, autoservice;
    
    	size_t data_left = sizeof(data);
    
    	struct ast_app *app;
    	struct ast_channel *chan;
    
    	
    	lua_getfield(L, 1, "name");
    
    	app_name = ast_strdupa(lua_tostring(L, -1));
    
    	if (!(app = pbx_findapp(app_name))) {
    
    		lua_pushstring(L, "application '");
    		lua_pushstring(L, app_name);
    		lua_pushstring(L, "' not found");
    		lua_concat(L, 3);
    		return lua_error(L);
    	}
    	
    
    	lua_getfield(L, LUA_REGISTRYINDEX, "channel");
    
    	chan = lua_touserdata(L, -1);
    
    	lua_pop(L, 1);
    	
    	
    	lua_getfield(L, LUA_REGISTRYINDEX, "context");
    
    	context = ast_strdupa(lua_tostring(L, -1));
    
    	lua_pop(L, 1);
    	
    	lua_getfield(L, LUA_REGISTRYINDEX, "exten");
    
    	exten = ast_strdupa(lua_tostring(L, -1));
    
    	lua_pop(L, 1);
    	
    	lua_getfield(L, LUA_REGISTRYINDEX, "priority");
    
    	priority = lua_tointeger(L, -1);
    
    	lua_pop(L, 1);
    
    
    	if (nargs > 1) {
    
    		if (!lua_isnil(L, 2))
    			ast_build_string(&data_next, &data_left, "%s", luaL_checkstring(L, 2));
    
    		for (i = 3; i <= nargs; i++) {
    			if (lua_isnil(L, i))
    				ast_build_string(&data_next, &data_left, ",");
    			else
    				ast_build_string(&data_next, &data_left, ",%s", luaL_checkstring(L, i));
    		}
    	}
    	
    	ast_verb(3, "Executing [%s@%s:%d] %s(\"%s\", \"%s\")\n",
    			exten, context, priority,
    			term_color(tmp, app_name, COLOR_BRCYAN, 0, sizeof(tmp)),
    			term_color(tmp2, chan->name, COLOR_BRMAGENTA, 0, sizeof(tmp2)),
    			term_color(tmp3, data, COLOR_BRMAGENTA, 0, sizeof(tmp3)));
    
    	lua_getfield(L, LUA_REGISTRYINDEX, "autoservice");
    
    	autoservice = lua_toboolean(L, -1);
    
    	lua_pop(L, 1);
    
    	if (autoservice)
    		ast_autoservice_stop(chan);
    
    	res = pbx_exec(chan, app, data);
    	
    	if (autoservice)
    		ast_autoservice_start(chan);
    
    	/* error executing an application, report it */
    	if (res) {
    		lua_pushinteger(L, res);
    		return lua_error(L);
    	}
    	return 0;
    }
    
    /*!
     * \brief [lua_CFunction] Used to get the value of a variable or dialplan
     * function (for access from lua, don't call directly)
     * 
     * The value of the variable or function is returned.  This function is the
     * 'get()' function in the following example as would be seen in
     * extensions.lua.
     *
     * \code
     * channel.variable:get()
     * \endcode
     */
    static int lua_get_variable_value(lua_State *L)
    {
    
    	struct ast_channel *chan;
    	char *value = NULL, *name;
    
    	char *workspace = alloca(LUA_BUF_SIZE);
    
    	workspace[0] = '\0';
    
    	if (!lua_istable(L, 1)) {
    		lua_pushstring(L, "User probably used '.' instead of ':' for retrieving a channel variable value");
    		return lua_error(L);
    	}
    	
    	lua_getfield(L, LUA_REGISTRYINDEX, "channel");
    
    	chan = lua_touserdata(L, -1);
    
    	lua_pop(L, 1);
    
    	lua_getfield(L, 1, "name");
    
    	name = ast_strdupa(lua_tostring(L, -1));
    
    	lua_pop(L, 1);
    	
    	lua_getfield(L, LUA_REGISTRYINDEX, "autoservice");
    
    	autoservice = lua_toboolean(L, -1);
    
    	lua_pop(L, 1);
    
    	if (autoservice)
    		ast_autoservice_stop(chan);
    	
    	/* if this is a dialplan function then use ast_func_read(), otherwise
    	 * use pbx_retrieve_variable() */
    	if (!ast_strlen_zero(name) && name[strlen(name) - 1] == ')') {
    		value = ast_func_read(chan, name, workspace, LUA_BUF_SIZE) ? NULL : workspace;
    	} else {
    		pbx_retrieve_variable(chan, name, &value, workspace, LUA_BUF_SIZE, &chan->varshead);
    	}
    	
    	if (autoservice)
    		ast_autoservice_start(chan);
    
    	if (value) {
    		lua_pushstring(L, value);
    	} else {
    		lua_pushnil(L);
    	}
    
    	return 1;
    }
    
    /*!
     * \brief [lua_CFunction] Used to set the value of a variable or dialplan
     * function (for access from lua, don't call directly)
     * 
     * This function is the 'set()' function in the following example as would be
     * seen in extensions.lua.
     *
     * \code
    
     * channel.variable:set("value")
    
     * \endcode
     */
    static int lua_set_variable_value(lua_State *L)
    {
    
    	const char *name, *value;
    	struct ast_channel *chan;
    	int autoservice;
    
    
    	if (!lua_istable(L, 1)) {
    		lua_pushstring(L, "User probably used '.' instead of ':' for setting a channel variable");
    		return lua_error(L);
    	}
    
    	lua_getfield(L, 1, "name");
    
    	name = ast_strdupa(lua_tostring(L, -1));
    
    	value = luaL_checkstring(L, 2);
    
    	
    	lua_getfield(L, LUA_REGISTRYINDEX, "channel");
    
    	chan = lua_touserdata(L, -1);
    
    	lua_pop(L, 1);
    
    	lua_getfield(L, LUA_REGISTRYINDEX, "autoservice");
    
    	autoservice = lua_toboolean(L, -1);
    
    	lua_pop(L, 1);
    
    	if (autoservice)
    		ast_autoservice_stop(chan);
    
    	pbx_builtin_setvar_helper(chan, name, value);
    	
    	if (autoservice)
    		ast_autoservice_start(chan);
    
    	return 0;
    }
    
    /*!
     * \brief Update the lua registry with the given context, exten, and priority.
     *
     * \param L the lua_State to use
     * \param context the new context
     * \param exten the new exten
     * \param priority the new priority
     */
    static void lua_update_registry(lua_State *L, const char *context, const char *exten, int priority)
    {
    	lua_pushstring(L, context);
    	lua_setfield(L, LUA_REGISTRYINDEX, "context");
    
    	lua_pushstring(L, exten);
    	lua_setfield(L, LUA_REGISTRYINDEX, "exten");
    
    	lua_pushinteger(L, priority);
    	lua_setfield(L, LUA_REGISTRYINDEX, "priority");
    }
    
    /*!
     * \brief Push a 'variable' table on the stack for access the channel variable
     * with the given name.
     *
     * \param L the lua_State to use
     * \param name the name of the variable
     */
    static void lua_push_variable_table(lua_State *L, const char *name)
    {
    	lua_newtable(L);
    	luaL_getmetatable(L, "variable");
    	lua_setmetatable(L, -2);
    
    	lua_pushstring(L, name);
    	lua_setfield(L, -2, "name");
    	
    	lua_pushcfunction(L, &lua_get_variable_value);
    	lua_setfield(L, -2, "get");
    	
    	lua_pushcfunction(L, &lua_set_variable_value);
    	lua_setfield(L, -2, "set");
    }
    
    /*!
     * \brief Create the global 'app' table for executing applications
     *
     * \param L the lua_State to use
     */
    static void lua_create_app_table(lua_State *L)
    {
    	lua_newtable(L);
    	luaL_newmetatable(L, "app");
    
    	lua_pushstring(L, "__index");
    	lua_pushcfunction(L, &lua_pbx_findapp);
    	lua_settable(L, -3);
    
    	lua_setmetatable(L, -2);
    	lua_setglobal(L, "app");
    }
    
    /*!
     * \brief Create the global 'channel' table for accesing channel variables
     *
     * \param L the lua_State to use
     */
    static void lua_create_channel_table(lua_State *L)
    {
    	lua_newtable(L);
    	luaL_newmetatable(L, "channel_data");
    
    	lua_pushstring(L, "__index");
    	lua_pushcfunction(L, &lua_get_variable);
    	lua_settable(L, -3);
    
    	lua_pushstring(L, "__newindex");
    	lua_pushcfunction(L, &lua_set_variable);
    	lua_settable(L, -3);
    
    	lua_setmetatable(L, -2);
    	lua_setglobal(L, "channel");
    }
    
    /*!
     * \brief Create the 'variable' metatable, used to retrieve channel variables
     *
     * \param L the lua_State to use
     */
    static void lua_create_variable_metatable(lua_State *L)
    {
    	luaL_newmetatable(L, "variable");
    
    	lua_pushstring(L, "__call");
    	lua_pushcfunction(L, &lua_func_read);
    	lua_settable(L, -3);
    
    	lua_pop(L, 1);
    }
    
    /*!
     * \brief Create the 'application' metatable, used to execute asterisk
     * applications from lua 
     *
     * \param L the lua_State to use
     */
    static void lua_create_application_metatable(lua_State *L)
    {
    	luaL_newmetatable(L, "application");
    
    	lua_pushstring(L, "__call");
    	lua_pushcfunction(L, &lua_pbx_exec);
    	lua_settable(L, -3);
    
    	lua_pop(L, 1);
    }
    
    /*!
     * \brief Create the autoservice functions
     *
     * \param L the lua_State to use
     */
    static void lua_create_autoservice_functions(lua_State *L)
    {
    	lua_pushcfunction(L, &lua_autoservice_start);
    	lua_setglobal(L, "autoservice_start");
    	
    	lua_pushcfunction(L, &lua_autoservice_stop);
    	lua_setglobal(L, "autoservice_stop");
    
    	lua_pushcfunction(L, &lua_autoservice_status);
    	lua_setglobal(L, "autoservice_status");
    
    	lua_pushboolean(L, 0);
    	lua_setfield(L, LUA_REGISTRYINDEX, "autoservice");
    }
    
    
    /*!
     * \brief Create the hangup check function
     *
     * \param L the lua_State to use
     */
    static void lua_create_hangup_function(lua_State *L)
    {
    	lua_pushcfunction(L, &lua_check_hangup);
    	lua_setglobal(L, "check_hangup");
    }
    
    
    /*!
     * \brief [lua_CFunction] Return a lua 'variable' object (for access from lua, don't call
     * directly)
     * 
     * This function is called to lookup a variable construct a 'variable' object.
     * It would be called in the following example as would be seen in
     * extensions.lua.
     *
     * \code
     * channel.variable
     * \endcode
     */
    static int lua_get_variable(lua_State *L)
    {
    
    	struct ast_channel *chan;
    
    	char *name = ast_strdupa(luaL_checkstring(L, 2));
    	char *value = NULL;
    	char *workspace = alloca(LUA_BUF_SIZE);
    	workspace[0] = '\0';
    	
    	lua_getfield(L, LUA_REGISTRYINDEX, "channel");
    
    	chan = lua_touserdata(L, -1);
    
    	lua_pop(L, 1);
    
    	lua_push_variable_table(L, name);
    	
    	/* if this is not a request for a dialplan funciton attempt to retrieve
    	 * the value of the variable */
    	if (!ast_strlen_zero(name) && name[strlen(name) - 1] != ')') {
    		pbx_retrieve_variable(chan, name, &value, workspace, LUA_BUF_SIZE, &chan->varshead);
    	}
    
    	if (value) {
    		lua_pushstring(L, value);
    		lua_setfield(L, -2, "value");
    	}
    
    	return 1;	
    }
    
    /*!
     * \brief [lua_CFunction] Set the value of a channel variable or dialplan
     * function (for access from lua, don't call directly)
     * 
     * This function is called to set a variable or dialplan function.  It would be
     * called in the following example as would be seen in extensions.lua.
     *
     * \code
     * channel.variable = "value"
     * \endcode
     */
    static int lua_set_variable(lua_State *L)
    {
    
    	struct ast_channel *chan;
    	int autoservice;
    
    	const char *name = luaL_checkstring(L, 2);
    	const char *value = luaL_checkstring(L, 3);
    
    	lua_getfield(L, LUA_REGISTRYINDEX, "channel");
    
    	chan = lua_touserdata(L, -1);
    
    	lua_pop(L, 1);
    
    	lua_getfield(L, LUA_REGISTRYINDEX, "autoservice");
    
    	autoservice = lua_toboolean(L, -1);
    
    	lua_pop(L, 1);
    
    	if (autoservice)
    		ast_autoservice_stop(chan);
    
    	pbx_builtin_setvar_helper(chan, name, value);
    	
    	if (autoservice)
    		ast_autoservice_start(chan);
    
    	return 0;
    }
    
    /*!
     * \brief [lua_CFunction] Create a 'variable' object for accessing a dialplan
     * function (for access from lua, don't call directly)
     * 
     * This function is called to create a 'variable' object to access a dialplan
     * function.  It would be called in the following example as would be seen in
     * extensions.lua.
     *
     * \code
     * channel.func("arg1", "arg2", "arg3")
     * \endcode
     *
     * To actually do anything with the resulting value you must use the 'get()'
     * and 'set()' methods (the reason is the resulting value is not a value, but
     * an object in the form of a lua table).
     */
    static int lua_func_read(lua_State *L)
    {
    	int nargs = lua_gettop(L);
    	char fullname[LUA_EXT_DATA_SIZE] = "";
    
    	char *fullname_next = fullname, *name;
    
    	size_t fullname_left = sizeof(fullname);
    	
    	lua_getfield(L, 1, "name");
    
    	name = ast_strdupa(lua_tostring(L, -1));
    
    	lua_pop(L, 1);
    
    	ast_build_string(&fullname_next, &fullname_left, "%s(", name);
    	
    	if (nargs > 1) {
    
    		if (!lua_isnil(L, 2))
    			ast_build_string(&fullname_next, &fullname_left, "%s", luaL_checkstring(L, 2));
    
    		for (i = 3; i <= nargs; i++) {
    			if (lua_isnil(L, i))
    				ast_build_string(&fullname_next, &fullname_left, ",");
    			else
    				ast_build_string(&fullname_next, &fullname_left, ",%s", luaL_checkstring(L, i));
    		}
    	}
    
    	ast_build_string(&fullname_next, &fullname_left, ")");
    	
    	lua_push_variable_table(L, fullname);
    	
    	return 1;
    }
    
    /*!
     * \brief [lua_CFunction] Tell pbx_lua to maintain an autoservice on this
     * channel (for access from lua, don't call directly)
     *
     * \param L the lua_State to use
     *
     * This function will set a flag that will cause pbx_lua to maintain an
     * autoservice on this channel.  The autoservice will automatically be stopped
     * and restarted before calling applications and functions.
     *
     * \return This function returns the result of the ast_autoservice_start()
     * function as a boolean to its lua caller.
     */
    static int lua_autoservice_start(lua_State *L)
    {
    
    	struct ast_channel *chan;
    	int res;
    
    
    	lua_getfield(L, LUA_REGISTRYINDEX, "channel");
    
    	chan = lua_touserdata(L, -1);
    
    	res = ast_autoservice_start(chan);
    
    
    	lua_pushboolean(L, !res);
    	lua_setfield(L, LUA_REGISTRYINDEX, "autoservice");
    
    	lua_pushboolean(L, !res);
    	return 1;
    }
    
    /*!
     * \brief [lua_CFunction] Tell pbx_lua to stop maintaning an autoservice on
     * this channel (for access from lua, don't call directly)
     *
     * \param L the lua_State to use
     *
     * This function will stop any autoservice running and turn off the autoservice
     * flag.  If this function returns false, it's probably because no autoservice
     * was running to begin with.
     *
     * \return This function returns the result of the ast_autoservice_stop()
     * function as a boolean to its lua caller.
     */
    static int lua_autoservice_stop(lua_State *L)
    {
    
    	struct ast_channel *chan;
    	int res;
    
    
    	lua_getfield(L, LUA_REGISTRYINDEX, "channel");
    
    	chan = lua_touserdata(L, -1);
    
    	res = ast_autoservice_stop(chan);
    
    
    	lua_pushboolean(L, 0);
    	lua_setfield(L, LUA_REGISTRYINDEX, "autoservice");
    
    	lua_pushboolean(L, !res);
    	return 1;
    }
    
    /*!
     * \brief [lua_CFunction] Get the status of the autoservice flag (for access
     * from lua, don't call directly)
     *
     * \param L the lua_State to use
     *
     * \return This function returns the status of the autoservice flag as a
     * boolean to its lua caller.
     */
    static int lua_autoservice_status(lua_State *L)
    {
    	lua_getfield(L, LUA_REGISTRYINDEX, "autoservice");
    	return 1;
    }
    
    
    /*!
     * \brief [lua_CFunction] Check if this channel has been hungup or not (for
     * access from lua, don't call directly)
     *
     * \param L the lua_State to use
     *
     * \return This function returns true if the channel was hungup
     */
    static int lua_check_hangup(lua_State *L)
    {
    	struct ast_channel *chan;
    	lua_getfield(L, LUA_REGISTRYINDEX, "channel");
    	chan = lua_touserdata(L, -1);
    	lua_pop(L, 1);
    
    	lua_pushboolean(L, ast_check_hangup(chan));
    	return 1;
    }
    
    
    Steve Murphy's avatar
    Steve Murphy committed
    /*!
     * \brief [lua_CFunction] Handle lua errors (for access from lua, don't call
     * directly)
     *
     * \param L the lua_State to use
     */
    static int lua_error_function(lua_State *L)
    {
    	int message_index;
    
    	/* pass number arguments right through back to asterisk*/
    	if (lua_isnumber(L, -1)) {
    		return 1;
    	}
    
    	/* if we are here then we have a string error message, let's attach a
    	 * backtrace to it */
    	message_index = lua_gettop(L);
    
    
    	/* prepare to prepend a new line to the traceback */
    	lua_pushliteral(L, "\n");
    
    
    Steve Murphy's avatar
    Steve Murphy committed
    	lua_getglobal(L, "debug");
    	lua_getfield(L, -1, "traceback");
    	lua_remove(L, -2); /* remove the 'debug' table */
    
    	lua_pushvalue(L, message_index);
    	lua_remove(L, message_index);
    
    	lua_pushnumber(L, 2);
    
    	lua_call(L, 2, 1);
    
    
    	/* prepend the new line we prepared above */
    	lua_concat(L, 2);
    
    
    Steve Murphy's avatar
    Steve Murphy committed
    	return 1;
    }
    
    
    /*!
     * \brief Store the sort order of each context
     
     * In the event of an error, an error string will be pushed onto the lua stack.
     *
     * \retval 0 success
     * \retval 1 failure
     */
    static int lua_sort_extensions(lua_State *L)
    {
    
    	int extensions, extensions_order;
    
    
    	/* create the extensions_order table */
    	lua_newtable(L);
    	lua_setfield(L, LUA_REGISTRYINDEX, "extensions_order");
    	lua_getfield(L, LUA_REGISTRYINDEX, "extensions_order");
    
    	extensions_order = lua_gettop(L);
    
    
    	/* sort each context in the extensions table */
    	/* load the 'extensions' table */
    	lua_getglobal(L, "extensions");
    
    	extensions = lua_gettop(L);
    
    	if (lua_isnil(L, -1)) {
    		lua_pop(L, 1);
    		lua_pushstring(L, "Unable to find 'extensions' table in extensions.lua\n");
    		return 1;
    	}
    
    	/* iterate through the extensions table and create a
    	 * matching table (holding the sort order) in the
    	 * extensions_order table for each context that is found
    	 */
    	for (lua_pushnil(L); lua_next(L, extensions); lua_pop(L, 1)) {
    		int context = lua_gettop(L);
    		int context_name = context - 1;
    
    		int context_order;
    
    		/* copy the context_name to be used as the key for the
    		 * context_order table in the extensions_order table later */
    		lua_pushvalue(L, context_name);
    
    
    		/* create the context_order table */
    
    		lua_newtable(L);
    
    		context_order = lua_gettop(L);
    
    
    		/* iterate through this context an popluate the corrisponding
    		 * table in the extensions_order table */
    		for (lua_pushnil(L); lua_next(L, context); lua_pop(L, 1)) {
    			int exten = lua_gettop(L) - 1;
    
    			lua_pushinteger(L, lua_objlen(L, context_order) + 1);
    			lua_pushvalue(L, exten);
    			lua_settable(L, context_order);
    		}
    		lua_settable(L, extensions_order); /* put the context_order table in the extensions_order table */
    
    		/* now sort the new table */
    
    		/* push the table.sort function */
    		lua_getglobal(L, "table");
    		lua_getfield(L, -1, "sort");
    		lua_remove(L, -2); /* remove the 'table' table */
    
    		/* push the context_order table */
    		lua_pushvalue(L, context_name);
    		lua_gettable(L, extensions_order);
    
    		/* push the comp function */
    		lua_pushcfunction(L, &lua_extension_cmp);
    
    		if (lua_pcall(L, 2, 0, 0)) {
    			lua_insert(L, -5);
    			lua_pop(L, 4);
    			return 1;
    		}
    	}
    	
    	/* remove the extensions table and the extensions_order table */
    	lua_pop(L, 2);
    	return 0;
    }
    
    
    /*!
     * \brief Register dialplan switches for our pbx_lua contexs.
     *
     * In the event of an error, an error string will be pushed onto the lua stack.
     *
     * \retval 0 success
     * \retval 1 failure
     */
    static int lua_register_switches(lua_State *L)
    {
    	int extensions;
    	struct ast_context *con = NULL;
    
    	/* create the hash table for our contexts */
    	/* XXX do we ever need to destroy this? pbx_config does not */
    	if (!local_table)
    		local_table = ast_hashtab_create(17, ast_hashtab_compare_contexts, ast_hashtab_resize_java, ast_hashtab_newsize_java, ast_hashtab_hash_contexts, 0);
    
    	/* load the 'extensions' table */
    	lua_getglobal(L, "extensions");
    	extensions = lua_gettop(L);
    	if (lua_isnil(L, -1)) {
    		lua_pop(L, 1);
    		lua_pushstring(L, "Unable to find 'extensions' table in extensions.lua\n");
    		return 1;
    	}
    
    	/* iterate through the extensions table and register a context and
    	 * dialplan switch for each lua context
    	 */
    	for (lua_pushnil(L); lua_next(L, extensions); lua_pop(L, 1)) {
    		int context = lua_gettop(L);
    		int context_name = context - 1;
    		const char *context_str = lua_tostring(L, context_name);
    
    		/* find or create this context */
    		con = ast_context_find_or_create(&local_contexts, local_table, context_str, registrar);
    		if (!con) {
    			/* remove extensions table and context key and value */
    			lua_pop(L, 3);
    			lua_pushstring(L, "Failed to find or create context\n");
    			return 1;
    		}
    
    		/* register the switch */
    		if (ast_context_add_switch2(con, "Lua", "", 0, registrar)) {
    			/* remove extensions table and context key and value */
    			lua_pop(L, 3);
    			lua_pushstring(L, "Unable to create switch for context\n");
    			return 1;
    		}
    	}
    	
    	/* remove the extensions table */
    	lua_pop(L, 1);
    	return 0;
    }
    
    
    /*!
     * \brief Register dialplan hints for our pbx_lua contexs.
     *
     * In the event of an error, an error string will be pushed onto the lua stack.
     *
     * \retval 0 success
     * \retval 1 failure
     */
    static int lua_register_hints(lua_State *L)
    {
    	int hints;
    	struct ast_context *con = NULL;
    
    	/* create the hash table for our contexts */
    	/* XXX do we ever need to destroy this? pbx_config does not */
    	if (!local_table)
    		local_table = ast_hashtab_create(17, ast_hashtab_compare_contexts, ast_hashtab_resize_java, ast_hashtab_newsize_java, ast_hashtab_hash_contexts, 0);
    
    	/* load the 'hints' table */
    	lua_getglobal(L, "hints");
    	hints = lua_gettop(L);
    	if (lua_isnil(L, -1)) {
    		/* hints table not found, move along */
    		lua_pop(L, 1);
    		return 0;
    	}
    
    	/* iterate through the hints table and register each context and
    	 * the hints that go along with it
    	 */
    	for (lua_pushnil(L); lua_next(L, hints); lua_pop(L, 1)) {
    		int context = lua_gettop(L);
    		int context_name = context - 1;
    		const char *context_str = lua_tostring(L, context_name);
    
    		/* find or create this context */
    		con = ast_context_find_or_create(&local_contexts, local_table, context_str, registrar);
    		if (!con) {
    			/* remove hints table and context key and value */
    			lua_pop(L, 3);
    			lua_pushstring(L, "Failed to find or create context\n");
    			return 1;
    		}
    
    		/* register each hint */
    		for (lua_pushnil(L); lua_next(L, context); lua_pop(L, 1)) {
    			const char *hint_value = lua_tostring(L, -1);
    			const char *hint_name;
    
    			/* the hint value is not a string, ignore it */
    			if (!hint_value) {
    				continue;
    			}
    
    			/* copy the name then convert it to a string */
    			lua_pushvalue(L, -2);
    			if (!(hint_name = lua_tostring(L, -1))) {
    				/* ignore non-string value */
    				lua_pop(L, 1);
    				continue;
    			}
    
    			if (ast_add_extension2(con, 0, hint_name, PRIORITY_HINT, NULL, NULL, hint_value, NULL, NULL, registrar)) {
    				/* remove hints table, hint name, hint value,
    				 * key copy, context name, and contex table */
    				lua_pop(L, 6);
    				lua_pushstring(L, "Error creating hint\n");
    				return 1;
    			}
    
    			/* pop the name copy */
    			lua_pop(L, 1);
    		}
    	}
    
    	/* remove the hints table */
    	lua_pop(L, 1);
    
    	return 0;
    }
    
    /*!
     * \brief [lua_CFunction] Compare two extensions (for access from lua, don't
     * call directly)