Newer
Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
/*
* 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"
#include "asterisk/hashtab.h"
Tilghman Lesher
committed
#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);
Jason Parker
committed
static int lua_check_hangup(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);
Jason Parker
committed
static void lua_create_hangup_function(lua_State *L);
Tilghman Lesher
committed
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
*/
Tilghman Lesher
committed
static void lua_state_destroy(void *data)
{
if (data)
lua_close(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");
}
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
/*!
* \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");
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);
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
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");
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);
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
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
* \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");
lua_pop(L, 1);
lua_getfield(L, LUA_REGISTRYINDEX, "autoservice");
autoservice = lua_toboolean(L, -1);
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
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");
}
Jason Parker
committed
/*!
* \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)
{
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");
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
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");
lua_pop(L, 1);
lua_getfield(L, LUA_REGISTRYINDEX, "autoservice");
autoservice = lua_toboolean(L, -1);
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
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) {
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
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");
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");
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;
}
Jason Parker
committed
/*!
* \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;
}
/*!
* \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");
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);
/*!
* \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");
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;
/* 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 */
context_order = lua_gettop(L);
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
/* 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;
}
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
/*!
* \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;
}
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
/*!
* \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)