static int matchmore(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data) { int res; lua_State *L; struct ast_module_user *u = ast_module_user_add(chan); if (!u) { ast_log(LOG_ERROR, "Error adjusting use count, probably could not allocate memory\n"); return 0; } L = lua_get_state(chan); if (!L) { ast_module_user_remove(u); return 0; } res = lua_find_extension(L, context, exten, priority, &matchmore, 0); if (!chan) lua_close(L); ast_module_user_remove(u); return res; }
/*! * \brief Locate an extensions and optionally push the matching function on the * stack * * \param L the lua_State to use * \param context the context to look in * \param exten the extension to look up * \param priority the priority to check, '1' is the only valid priority * \param func the calling func, used to adjust matching behavior between, * match, canmatch, and matchmore * \param push_func whether or not to push the lua function for the given * extension onto the stack */ static int lua_find_extension(lua_State *L, const char *context, const char *exten, int priority, ast_switch_f *func, int push_func) { int context_table, context_order_table, i; ast_debug(2, "Looking up %s@%s:%i\n", exten, context, priority); if (priority != 1) return 0; /* load the 'extensions' table */ lua_getglobal(L, "extensions"); if (lua_isnil(L, -1)) { ast_log(LOG_ERROR, "Unable to find 'extensions' table in extensions.lua\n"); lua_pop(L, 1); return 0; } /* load the given context */ lua_getfield(L, -1, context); if (lua_isnil(L, -1)) { lua_pop(L, 2); return 0; } /* remove the extensions table */ lua_remove(L, -2); context_table = lua_gettop(L); /* load the extensions order table for this context */ lua_getfield(L, LUA_REGISTRYINDEX, "extensions_order"); lua_getfield(L, -1, context); lua_remove(L, -2); /* remove the extensions order table */ context_order_table = lua_gettop(L); /* step through the extensions looking for a match */ for (i = 1; i < lua_objlen(L, context_order_table) + 1; i++) { int e_index_copy, match = 0; const char *e; lua_pushinteger(L, i); lua_gettable(L, context_order_table); lua_gettop(L); /* copy the key at the top of the stack for use later */ lua_pushvalue(L, -1); e_index_copy = lua_gettop(L); if (!(e = lua_tostring(L, e_index_copy))) { lua_pop(L, 2); continue; } /* make sure this is not the 'include' extension */ if (!strcasecmp(e, "include")) { lua_pop(L, 2); continue; } if (func == &matchmore) match = ast_extension_close(e, exten, E_MATCHMORE); else if (func == &canmatch) match = ast_extension_close(e, exten, E_CANMATCH); else match = ast_extension_match(e, exten); /* the extension matching functions return 0 on fail, 1 on * match, 2 on earlymatch */ if (!match) { /* pop the copy and the extension */ lua_pop(L, 2); continue; /* keep trying */ } if (func == &matchmore && match == 2) { /* We match an extension ending in '!'. The decision in * this case is final and counts as no match. */ lua_pop(L, 4); return 0; } /* remove the context table, the context order table, the * extension, and the extension copy (or replace the extension * with the corresponding function) */ if (push_func) { lua_pop(L, 1); /* pop the copy */ lua_gettable(L, context_table); lua_insert(L, -3); lua_pop(L, 2); } else { lua_pop(L, 4); } return 1; } /* load the includes for this context */ lua_getfield(L, context_table, "include"); if (lua_isnil(L, -1)) { lua_pop(L, 3); return 0; } /* remove the context and the order table*/ lua_remove(L, context_order_table); lua_remove(L, context_table); /* Now try any includes we have in this context */ for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) { const char *c = lua_tostring(L, -1); if (!c) continue; if (lua_find_extension(L, c, exten, priority, func, push_func)) { /* remove the value, the key, and the includes table * from the stack. Leave the function behind if * necessary */ if (push_func) lua_insert(L, -4); lua_pop(L, 3); return 1; } } /* pop the includes table */ lua_pop(L, 1); return 0; }
static int exec(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data) { int res, error_func; lua_State *L; struct ast_module_user *u = ast_module_user_add(chan); if (!u) { ast_log(LOG_ERROR, "Error adjusting use count, probably could not allocate memory\n"); return -1; } L = lua_get_state(chan); if (!L) { ast_module_user_remove(u); return -1; } lua_pushcfunction(L, &lua_error_function); error_func = lua_gettop(L); /* push the extension function onto the stack */ if (!lua_find_extension(L, context, exten, priority, &exists, 1)) { lua_pop(L, 1); /* pop the debug function */ ast_log(LOG_ERROR, "Could not find extension %s in context %s\n", exten, context); if (!chan) lua_close(L); ast_module_user_remove(u); return -1; } lua_getfield(L, LUA_REGISTRYINDEX, "autoservice"); if (lua_toboolean(L, -1)) { ast_autoservice_start(chan); } lua_pop(L, 1); lua_update_registry(L, context, exten, priority); lua_pushstring(L, context); lua_pushstring(L, exten); res = lua_pcall(L, 2, 0, error_func); if (res) { if (res == LUA_ERRRUN) { res = -1; if (lua_isnumber(L, -1)) { res = lua_tointeger(L, -1); if (res == LUA_GOTO_DETECTED) { res = 0; } } else if (lua_isstring(L, -1)) { const char *error = lua_tostring(L, -1); ast_log(LOG_ERROR, "Error executing lua extension: %s\n", error); } } else if (res == LUA_ERRERR) { res = -1; ast_log(LOG_ERROR, "Error in the lua error handler (this is probably a bug in pbx_lua)\n"); } else if (res == LUA_ERRMEM) { res = -1; ast_log(LOG_ERROR, "Memory allocation error\n"); } lua_pop(L, 1); } lua_remove(L, error_func); lua_getfield(L, LUA_REGISTRYINDEX, "autoservice"); if (lua_toboolean(L, -1)) { ast_autoservice_stop(chan); } lua_pop(L, 1); if (!chan) lua_close(L); ast_module_user_remove(u); return res; }
/*! * \brief Locate an extensions and optionally push the matching function on the * stack * * \param L the lua_State to use * \param context the context to look in * \param exten the extension to look up * \param priority the priority to check, '1' is the only valid priority * \param func the calling func, used to adjust matching behavior between, * match, canmatch, and matchmore * \param push_func whether or not to push the lua function for the given * extension onto the stack */ static int lua_find_extension(lua_State *L, const char *context, const char *exten, int priority, ast_switch_f *func, int push_func) { int context_table, context_order_table, i; ast_debug(2, "Looking up %s@%s:%i\n", exten, context, priority); if (priority != 1) return 0; /* load the 'extensions' table */ lua_getglobal(L, "extensions"); if (lua_isnil(L, -1)) { ast_log(LOG_ERROR, "Unable to find 'extensions' table in extensions.lua\n"); lua_pop(L, 1); return 0; } /* load the given context */ lua_getfield(L, -1, context); if (lua_isnil(L, -1)) { lua_pop(L, 2); return 0; } /* remove the extensions table */ lua_remove(L, -2); context_table = lua_gettop(L); /* load the extensions order table for this context */ lua_getfield(L, LUA_REGISTRYINDEX, "extensions_order"); lua_getfield(L, -1, context); lua_remove(L, -2); /* remove the extensions order table */ context_order_table = lua_gettop(L); /* step through the extensions looking for a match */ #if LUA_VERSION_NUM < 502 for (i = 1; i < lua_objlen(L, context_order_table) + 1; i++) { #else for (i = 1; i < lua_rawlen(L, context_order_table) + 1; i++) { #endif int e_index_copy, match = 0; const char *e; lua_pushinteger(L, i); lua_gettable(L, context_order_table); lua_gettop(L); /* copy the key at the top of the stack for use later */ lua_pushvalue(L, -1); e_index_copy = lua_gettop(L); if (!(e = lua_tostring(L, e_index_copy))) { lua_pop(L, 2); continue; } /* make sure this is not the 'include' extension */ if (!strcasecmp(e, "include")) { lua_pop(L, 2); continue; } if (func == &matchmore) match = ast_extension_close(e, exten, E_MATCHMORE); else if (func == &canmatch) match = ast_extension_close(e, exten, E_CANMATCH); else match = ast_extension_match(e, exten); /* the extension matching functions return 0 on fail, 1 on * match, 2 on earlymatch */ if (!match) { /* pop the copy and the extension */ lua_pop(L, 2); continue; /* keep trying */ } if (func == &matchmore && match == 2) { /* We match an extension ending in '!'. The decision in * this case is final and counts as no match. */ lua_pop(L, 4); return 0; } /* remove the context table, the context order table, the * extension, and the extension copy (or replace the extension * with the corresponding function) */ if (push_func) { lua_pop(L, 1); /* pop the copy */ lua_gettable(L, context_table); lua_insert(L, -3); lua_pop(L, 2); } else { lua_pop(L, 4); } return 1; } /* load the includes for this context */ lua_getfield(L, context_table, "include"); if (lua_isnil(L, -1)) { lua_pop(L, 3); return 0; } /* remove the context and the order table*/ lua_remove(L, context_order_table); lua_remove(L, context_table); /* Now try any includes we have in this context */ for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) { const char *c = lua_tostring(L, -1); if (!c) continue; if (lua_find_extension(L, c, exten, priority, func, push_func)) { /* remove the value, the key, and the includes table * from the stack. Leave the function behind if * necessary */ if (push_func) lua_insert(L, -4); lua_pop(L, 3); return 1; } } /* pop the includes table */ lua_pop(L, 1); return 0; } static struct ast_switch lua_switch = { .name = "Lua", .description = "Lua PBX Switch", .exists = exists, .canmatch = canmatch, .exec = exec, .matchmore = matchmore, }; static int load_or_reload_lua_stuff(void) { int res = AST_MODULE_LOAD_SUCCESS; lua_State *L = luaL_newstate(); if (!L) { ast_log(LOG_ERROR, "Error allocating lua_State, no memory\n"); return AST_MODULE_LOAD_DECLINE; } if (lua_reload_extensions(L)) { const char *error = lua_tostring(L, -1); ast_log(LOG_ERROR, "Error loading extensions.lua: %s\n", error); res = AST_MODULE_LOAD_DECLINE; } lua_close(L); return res; } static int unload_module(void) { ast_context_destroy(NULL, registrar); ast_unregister_switch(&lua_switch); lua_free_extensions(); return 0; }