Skip to content
Snippets Groups Projects
pbx_lua.c 42.7 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>
    
    	<support_level>extended</support_level>
    
     ***/
    
    #include "asterisk.h"
    
    #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
    
    #else
    #define LUA_EXT_DATA_SIZE 8192
    #endif
    
    #define LUA_BUF_SIZE 4096
    
    
    /* This value is used by the lua engine to signal that a Goto or dialplan jump
     * was detected. Ensure this value does not conflict with any values dialplan
     * applications might return */
    #define LUA_GOTO_DETECTED 5
    
    
    static char *lua_read_extensions_file(lua_State *L, long *size, int *file_not_openable);
    
    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);
    
    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_concat_args(lua_State *L, int start, int nargs);
    
    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
     *
     * 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);
    
    	const char *data = "";
    	char *app_name, *context, *exten;
    
    	char tmp[80], tmp2[80], tmp3[LUA_EXT_DATA_SIZE];
    	int priority, autoservice;
    	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);
    
    	context = ast_strdupa(ast_channel_context(chan));
    	exten = ast_strdupa(ast_channel_exten(chan));
    
    	priority = ast_channel_priority(chan);
    
    	lua_concat_args(L, 2, nargs);
    	data = lua_tostring(L, -1);
    
    
    	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, ast_channel_name(chan), 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);
    
    
    	lua_pop(L, 1); /* pop data */
    	data = "";
    
    
    	if (autoservice)
    		ast_autoservice_start(chan);
    
    	/* error executing an application, report it */
    	if (res) {
    		lua_pushinteger(L, res);
    		return lua_error(L);
    	}
    
    	if (strcmp(context, ast_channel_context(chan))) {
    
    		lua_pushstring(L, ast_channel_context(chan));
    
    		lua_pushliteral(L, "context");
    
    	} else if (strcmp(exten, ast_channel_exten(chan))) {
    
    		lua_pushstring(L, ast_channel_exten(chan));
    
    		lua_pushliteral(L, "exten");
    
    	} else if (priority != ast_channel_priority(chan)) {
    
    		lua_pushinteger(L, ast_channel_priority(chan));
    
    		lua_pushliteral(L, "priority");
    
    	} else {
    		/* no goto - restore the original position back
    		 * to lua state, in case this was a recursive dialplan
    		 * call (a dialplan application re-entering dialplan) */
    		lua_update_registry(L, context, exten, priority);
    		return 0;
    
    	/* goto detected - construct error message */
    
    	lua_insert(L, -3);
    
    
    	lua_pushliteral(L, " changed from ");
    	lua_insert(L, -3);
    
    	lua_pushliteral(L, " to ");
    	lua_insert(L, -2);
    
    	lua_concat(L, 5);
    
    	ast_debug(2, "Goto detected: %s\n", lua_tostring(L, -1));
    	lua_pop(L, 1);
    
    	/* let the lua engine know it needs to return control to the pbx */
    	lua_pushinteger(L, LUA_GOTO_DETECTED);
    
    	lua_error(L);
    
    /*!
     * \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 = ast_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_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, ast_channel_varshead(chan));
    
    	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.
     *
    
     * 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.
     *
    
     * The value on the top of the stack is popped and used as the name.
     *
    
     * \param L the lua_State to use
     */
    
    static void lua_push_variable_table(lua_State *L)
    
    {
    	lua_newtable(L);
    	luaL_getmetatable(L, "variable");
    	lua_setmetatable(L, -2);
    
    
    	lua_insert(L, -2); /* move the table after the 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");
    }
    
    /*!
    
    Josh Soref's avatar
    Josh Soref committed
     * \brief Create the global 'channel' table for accessing 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_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;
    
    	const char *name = luaL_checkstring(L, 2);
    
    	char *value = NULL;
    
    	char *workspace = ast_alloca(LUA_BUF_SIZE);
    
    	workspace[0] = '\0';
    
    	lua_getfield(L, LUA_REGISTRYINDEX, "channel");
    
    	chan = lua_touserdata(L, -1);
    
    	lua_pushvalue(L, 2);
    	lua_push_variable_table(L);
    
    	/* 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, ast_channel_varshead(chan));
    
    	}
    
    	if (value) {
    		lua_pushstring(L, value);
    		lua_setfield(L, -2, "value");
    	}
    
    
    }
    
    /*!
     * \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 Concatenate a list of lua function arguments into a comma separated
     * string.
     * \param L the lua_State to use
     * \param start the index of the first argument
     * \param nargs the number of args
     *
     * The resulting string will be left on the top of the stack.
     */
    static void lua_concat_args(lua_State *L, int start, int nargs) {
    	int concat = 0;
    	int i = start + 1;
    
    
    	if (start <= nargs && !lua_isnil(L, start)) {
    
    		lua_pushvalue(L, start);
    		concat += 1;
    	}
    
    	for (; i <= nargs; i++) {
    		if (lua_isnil(L, i)) {
    			lua_pushliteral(L, ",");
    			concat += 1;
    		} else {
    			lua_pushliteral(L, ",");
    			lua_pushvalue(L, i);
    			concat += 2;
    		}
    	}
    
    	lua_concat(L, concat);
    }
    
    
    /*!
     * \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);
    
    	/* build a string in the form of "func_name(arg1,arg2,arg3)" */
    	lua_getfield(L, 1, "name");
    	lua_pushliteral(L, "(");
    	lua_concat_args(L, 2, nargs);
    	lua_pushliteral(L, ")");
    	lua_concat(L, 4);
    
    	lua_push_variable_table(L);
    
    	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.
     */
    static int lua_autoservice_start(lua_State *L)
    {
    
    	struct ast_channel *chan;
    
    
    	lua_getfield(L, LUA_REGISTRYINDEX, "autoservice");
    	if (lua_toboolean(L, -1)) {
    		/* autservice already running */
    		lua_pop(L, 1);
    		return 0;
    	}
    	lua_pop(L, 1);
    
    	lua_getfield(L, LUA_REGISTRYINDEX, "channel");
    
    	chan = lua_touserdata(L, -1);
    
    	ast_autoservice_start(chan);
    
    	lua_setfield(L, LUA_REGISTRYINDEX, "autoservice");
    
    Josh Soref's avatar
    Josh Soref committed
     * \brief [lua_CFunction] Tell pbx_lua to stop maintaining 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.
     */
    static int lua_autoservice_stop(lua_State *L)
    {
    
    	struct ast_channel *chan;
    
    
    	lua_getfield(L, LUA_REGISTRYINDEX, "autoservice");
    	if (!lua_toboolean(L, -1)) {
    		/* no autservice running */
    		lua_pop(L, 1);
    		return 0;
    	}
    	lua_pop(L, 1);
    
    	lua_getfield(L, LUA_REGISTRYINDEX, "channel");
    
    	chan = lua_touserdata(L, -1);
    
    	ast_autoservice_stop(chan);
    
    
    	lua_pushboolean(L, 0);
    	lua_setfield(L, LUA_REGISTRYINDEX, "autoservice");
    
    }
    
    /*!
     * \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");
    
    	if (!lua_istable(L, -1)) {
    		/* Have no 'debug' table for whatever reason */
    		lua_pop(L, 2);
    		/*  Original err message is on stack top now */
    		return 1;
    	}
    
    Steve Murphy's avatar
    Steve Murphy committed
    	lua_getfield(L, -1, "traceback");
    
    	if (!lua_isfunction(L, -1)) {
    		/* Same here for traceback function */
    		lua_pop(L, 3);
    		/*  Original err message is on stack top now */
    		return 1;
    	}
    
    Steve Murphy's avatar
    Steve Murphy committed
    	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);
    
    Josh Soref's avatar
    Josh Soref committed
    		/* iterate through this context an populate 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;
    
    #if LUA_VERSION_NUM < 502
    
    			lua_pushinteger(L, lua_objlen(L, context_order) + 1);
    
    #else
    			lua_pushinteger(L, lua_rawlen(L, context_order) + 1);
    #endif
    
    			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;
    }
    
    
    Josh Soref's avatar
    Josh Soref committed
     * \brief Register dialplan switches for our pbx_lua contexts.
    
     *
     * 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;
    }
    
    
    Josh Soref's avatar
    Josh Soref committed
     * \brief Register dialplan hints for our pbx_lua contexts.
    
     *
     * 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);