Add pbx_lua as a method of doing extensions
authorTilghman Lesher <tilghman@meg.abyt.es>
Fri, 2 Nov 2007 15:36:34 +0000 (15:36 +0000)
committerTilghman Lesher <tilghman@meg.abyt.es>
Fri, 2 Nov 2007 15:36:34 +0000 (15:36 +0000)
Reported by: mnicholson
Patch by: mnicholson
Closes issue #11140

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@88250 65c4cc65-6c06-0410-ace0-fbb531ad65f3

build_tools/menuselect-deps.in
configs/extensions.lua.sample [new file with mode: 0644]
configure
configure.ac
include/asterisk/autoconfig.h.in
include/asterisk/pbx.h
main/pbx.c
makeopts.in
pbx/pbx_lua.c [new file with mode: 0644]
utils/build-extensions-conf.lua [new file with mode: 0755]

index 3504a01..fbb4b15 100644 (file)
@@ -11,6 +11,7 @@ IMAP_TK=@PBX_IMAP_TK@
 IXJUSER=@PBX_IXJUSER@
 KDE=@PBX_KDE@
 LTDL=@PBX_LTDL@
+LUA=@PBX_LUA@
 NBS=@PBX_NBS@
 NETSNMP=@PBX_NETSNMP@
 NEWT=@PBX_NEWT@
diff --git a/configs/extensions.lua.sample b/configs/extensions.lua.sample
new file mode 100644 (file)
index 0000000..7761281
--- /dev/null
@@ -0,0 +1,208 @@
+
+
+CONSOLE = "Console/dsp" -- Console interface for demo
+--CONSOLE = "Zap/1"
+--CONSOLE = "Phone/phone0"
+
+IAXINFO = "guest"       -- IAXtel username/password
+--IAXINFO = "myuser:mypass"
+
+TRUNK = "Zap/g2"
+TRUNKMSD = 1
+-- TRUNK = "IAX2/user:pass@provider"
+
+
+--
+-- Extensions are expected to be defined in a global table named 'extensions'.
+-- The 'extensions' table should have a group of tables in it, each
+-- representing a context.  Extensions are defined in each context.  See below
+-- for examples.
+--
+-- This file can be automatically included in the extensions.conf file using
+-- the 'utils/build-extensions-conf.lua' script and a #exec statement in
+-- extensions.conf.
+--
+--   #exec /usr/bin/utils/build-extensions.conf.lua -c
+-- 
+-- The 'execincludes' option must be set to 'yes' in the [options] section of
+-- asterisk.conf for this to work properly.
+-- 
+-- Extension names may be numbers, letters, or combinations thereof. If
+-- an extension name is prefixed by a '_' character, it is interpreted as
+-- a pattern rather than a literal.  In patterns, some characters have
+-- special meanings:
+--                                                                           
+--   X - any digit from 0-9
+--   Z - any digit from 1-9
+--   N - any digit from 2-9
+--   [1235-9] - any digit in the brackets (in this example, 1,2,3,5,6,7,8,9)
+--   . - wildcard, matches anything remaining (e.g. _9011. matches 
+--       anything starting with 9011 excluding 9011 itself)
+--   ! - wildcard, causes the matching process to complete as soon as
+--       it can unambiguously determine that no other matches are possible
+--                                                                           
+-- For example the extension _NXXXXXX would match normal 7 digit
+-- dialings, while _1NXXNXXXXXX would represent an area code plus phone
+-- number preceded by a one.
+-- 
+-- If your extension has special characters in it such as '.' and '!' you must
+-- explicitly make it a string in the tabale definition:
+--
+--   ["_special."] = function;
+--   ["_special!"] = function;
+--
+-- There are no priorities.  All extensions to asterisk appear to have a single
+-- priority as if they consist of a single priority.
+-- 
+-- Each context is defined as a table in the extensions table.  The
+-- context names should be strings.
+--
+-- One context may be included in another context using the 'includes'
+-- extension.  This extension should be set to a table containing a list
+-- of context names.  Do not put references to tables in the includes
+-- table.
+-- 
+--   include = {"a", "b", "c"};
+--
+-- Channel variables can be accessed thorugh the global 'channel' table.
+--
+--   v = channel.var_name
+--   v = channel["var_name"]
+--   v.value
+--   v:get()
+--
+--   channel.var_name = "value"
+--   channel["var_name"] = "value"
+--   v:set("value")
+--
+--   channel.func_name(1,2,3):set("value")
+--   value = channel.func_name(1,2,3):get()
+--
+--   channel["func_name(1|2|3)"]:set("value")
+--   channel["func_name(1|2|3)"] = "value"
+--   value = channel["func_name(1|2|3)"]:get()
+--
+-- Note the use of the ':' operator to access the get() and set()
+-- methods.
+--
+-- Also notice the absence of the following constructs from the examples above:
+--   channel.func_name(1,2,3) = "value"  -- this will NOT work
+--   value = channel.func_name(1,2,3)    -- this will NOT work as expected
+-- 
+--
+-- Dialplan applications can be accessed through the global 'app' table.
+--
+--    app.Dial("Zap/1")
+--    app.dial("Zap/1")
+--
+-- More examples can be found below.
+--
+-- Before starting long running operations, an autoservice should be started
+-- using the autoservice_start() function.  This autoservice will automatically
+-- be stopped before executing applications and dialplan functions and will be
+-- restarted afterwards.  The autoservice can be stopped using
+-- autoservice_stop() and the autoservice_status() function will return true if
+-- an autoservice is currently running.
+--
+
+function outgoing_local(c, e)
+       app.dial("zap/1/" .. e, "", "")
+end
+
+function demo_instruct()
+       app.background("demo-instruct")
+       app.waitexten()
+end
+
+function demo_congrats()
+       app.background("demo-congrats")
+       demo_instruct()
+end
+
+-- Answer the chanel and play the demo sound files
+function demo_start(context, exten)
+       app.wait(1)
+       app.answer()
+
+       channel.TIMEOUT("digit"):set(5)
+       channel.TIMEOUT("response"):set(10)
+       -- app.set("TIMEOUT(digit)=5")
+       -- app.set("TIMEOUT(response)=10")
+
+       demo_congrats(context, exten)
+end
+
+function demo_hangup()
+       app.playback("demo-thanks")
+       app.hangup()
+end
+
+extensions = {
+       demo = {
+               s = demo_start;
+
+               ["2"] = function()
+                       app.background("demo-moreinfo")
+                       demo_instruct()
+               end;
+               ["3"] = function ()
+                       channel.LANGUAGE():set("fr") -- set the language to french
+                       demo_congrats()
+               end;
+
+               ["1000"] = function()
+                       app.goto("default", "s", 1)
+               end;
+
+               ["1234"] = function()
+                       app.playback("transfer", "skip")
+                       -- do a dial here
+               end;
+
+               ["1235"] = function()
+                       app.voicemail("1234", "u")
+               end;
+
+               ["1236"] = function()
+                       app.dial("Console/dsp")
+                       app.voicemail(1234, "b")
+               end;
+
+               ["#"] = demo_hangup;
+               t = demo_hangup;
+                i = function()
+                        app.playback("invalid")
+                        demo_instruct()
+                end;
+
+               ["500"] = function()
+                       app.playback("demo-abouttotry")
+                       app.dial("IAX2/guest@misery.digium.com/s@default")
+                       app.playback("demo-nogo")
+                       demo_instruct()
+               end;
+
+               ["600"] = function()
+                       app.playback("demo-echotest")
+                       app.echo()
+                       app.playback("demo-echodone")
+                       demo_instruct()
+               end;
+
+               ["8500"] = function()
+                       app.voicemailmain()
+                       demo_instruct()
+               end;
+
+       };
+
+       default = {
+               -- by default, do the demo
+               include = {"demo"};
+       };
+
+       ["local"] = {
+               ["_NXXXXXX"] = outgoing_local;
+       };
+}
+
index d172f27..d3e1504 100755 (executable)
--- a/configure
+++ b/configure
@@ -1,5 +1,5 @@
 #! /bin/sh
-# From configure.ac Revision: 87325 .
+# From configure.ac Revision: 88184 .
 # Guess values for system-dependent variables and create Makefiles.
 # Generated by GNU Autoconf 2.61.
 #
@@ -765,6 +765,10 @@ LTDL_LIB
 LTDL_INCLUDE
 LTDL_DIR
 PBX_LTDL
+LUA_LIB
+LUA_INCLUDE
+LUA_DIR
+PBX_LUA
 MISDN_LIB
 MISDN_INCLUDE
 MISDN_DIR
@@ -1537,6 +1541,7 @@ Optional Packages:
   --with-isdnnet=PATH     use ISDN4Linux Library files in PATH
   --with-kde=PATH         use KDE files in PATH
   --with-ltdl=PATH        use libtool files in PATH
+  --with-lua=PATH         use Lua files in PATH
   --with-misdn=PATH       use mISDN User Library files in PATH
   --with-nbs=PATH         use Network Broadcast Sound files in PATH
   --with-ncurses=PATH     use ncurses files in PATH
@@ -7991,6 +7996,34 @@ PBX_LTDL=0
 
 
 
+LUA_DESCRIP="Lua"
+LUA_OPTION="lua"
+
+# Check whether --with-lua was given.
+if test "${with_lua+set}" = set; then
+  withval=$with_lua;
+case ${withval} in
+     n|no)
+     USE_LUA=no
+     ;;
+     y|ye|yes)
+     ac_mandatory_list="${ac_mandatory_list} LUA"
+     ;;
+     *)
+     LUA_DIR="${withval}"
+     ac_mandatory_list="${ac_mandatory_list} LUA"
+     ;;
+esac
+
+fi
+
+PBX_LUA=0
+
+
+
+
+
+
 MISDN_DESCRIP="mISDN User Library"
 MISDN_OPTION="misdn"
 
@@ -47072,6 +47105,399 @@ ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $
 ac_compiler_gnu=$ac_cv_c_compiler_gnu
 
 
+LUA_INCLUDE="-I/usr/include/lua5.1"
+LUA_LIB="-llua5.1"
+
+if test "x${PBX_LUA}" != "x1" -a "${USE_LUA}" != "no"; then
+   pbxlibdir=""
+   if test "x${LUA_DIR}" != "x"; then
+      if test -d ${LUA_DIR}/lib; then
+        pbxlibdir="-L${LUA_DIR}/lib"
+      else
+        pbxlibdir="-L${LUA_DIR}"
+      fi
+   fi
+   pbxfuncname="luaL_newstate"
+   if test "x${pbxfuncname}" = "x" ; then   # empty lib, assume only headers
+      AST_LUA_FOUND=yes
+   else
+      as_ac_Lib=`echo "ac_cv_lib_lua5.1_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -llua5.1" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -llua5.1... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-llua5.1 ${pbxlibdir}  $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext &&
+       $as_test_x conftest$ac_exeext; then
+  eval "$as_ac_Lib=yes"
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+              { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+  AST_LUA_FOUND=yes
+else
+  AST_LUA_FOUND=no
+fi
+
+   fi
+
+   if test "${AST_LUA_FOUND}" = "yes"; then
+      LUA_LIB="-llua5.1 "
+      LUA_HEADER_FOUND="1"
+      if test "x${LUA_DIR}" != "x"; then
+         LUA_LIB="${pbxlibdir} ${LUA_LIB}"
+        LUA_INCLUDE="-I${LUA_DIR}/include"
+        saved_cppflags="${CPPFLAGS}"
+        CPPFLAGS="${CPPFLAGS} -I${LUA_DIR}/include"
+        if test "xlua5.1/lua.h" != "x" ; then
+           as_ac_Header=`echo "ac_cv_header_${LUA_DIR}/include/lua5.1/lua.h" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  { echo "$as_me:$LINENO: checking for ${LUA_DIR}/include/lua5.1/lua.h" >&5
+echo $ECHO_N "checking for ${LUA_DIR}/include/lua5.1/lua.h... $ECHO_C" >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+ac_res=`eval echo '${'$as_ac_Header'}'`
+              { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+else
+  # Is the header compilable?
+{ echo "$as_me:$LINENO: checking ${LUA_DIR}/include/lua5.1/lua.h usability" >&5
+echo $ECHO_N "checking ${LUA_DIR}/include/lua5.1/lua.h usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+#include <${LUA_DIR}/include/lua5.1/lua.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_header_compiler=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking ${LUA_DIR}/include/lua5.1/lua.h presence" >&5
+echo $ECHO_N "checking ${LUA_DIR}/include/lua5.1/lua.h presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <${LUA_DIR}/include/lua5.1/lua.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null && {
+        test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       }; then
+  ac_header_preproc=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+  yes:no: )
+    { echo "$as_me:$LINENO: WARNING: ${LUA_DIR}/include/lua5.1/lua.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: ${LUA_DIR}/include/lua5.1/lua.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { echo "$as_me:$LINENO: WARNING: ${LUA_DIR}/include/lua5.1/lua.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: ${LUA_DIR}/include/lua5.1/lua.h: proceeding with the compiler's result" >&2;}
+    ac_header_preproc=yes
+    ;;
+  no:yes:* )
+    { echo "$as_me:$LINENO: WARNING: ${LUA_DIR}/include/lua5.1/lua.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: ${LUA_DIR}/include/lua5.1/lua.h: present but cannot be compiled" >&2;}
+    { echo "$as_me:$LINENO: WARNING: ${LUA_DIR}/include/lua5.1/lua.h:     check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: ${LUA_DIR}/include/lua5.1/lua.h:     check for missing prerequisite headers?" >&2;}
+    { echo "$as_me:$LINENO: WARNING: ${LUA_DIR}/include/lua5.1/lua.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: ${LUA_DIR}/include/lua5.1/lua.h: see the Autoconf documentation" >&2;}
+    { echo "$as_me:$LINENO: WARNING: ${LUA_DIR}/include/lua5.1/lua.h:     section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: ${LUA_DIR}/include/lua5.1/lua.h:     section \"Present But Cannot Be Compiled\"" >&2;}
+    { echo "$as_me:$LINENO: WARNING: ${LUA_DIR}/include/lua5.1/lua.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: ${LUA_DIR}/include/lua5.1/lua.h: proceeding with the preprocessor's result" >&2;}
+    { echo "$as_me:$LINENO: WARNING: ${LUA_DIR}/include/lua5.1/lua.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: ${LUA_DIR}/include/lua5.1/lua.h: in the future, the compiler will take precedence" >&2;}
+
+    ;;
+esac
+{ echo "$as_me:$LINENO: checking for ${LUA_DIR}/include/lua5.1/lua.h" >&5
+echo $ECHO_N "checking for ${LUA_DIR}/include/lua5.1/lua.h... $ECHO_C" >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval echo '${'$as_ac_Header'}'`
+              { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+
+fi
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+  LUA_HEADER_FOUND=1
+else
+  LUA_HEADER_FOUND=0
+fi
+
+
+        fi
+        CPPFLAGS="${saved_cppflags}"
+      else
+        if test "xlua5.1/lua.h" != "x" ; then
+            if test "${ac_cv_header_lua5_1_lua_h+set}" = set; then
+  { echo "$as_me:$LINENO: checking for lua5.1/lua.h" >&5
+echo $ECHO_N "checking for lua5.1/lua.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_lua5_1_lua_h+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_lua5_1_lua_h" >&5
+echo "${ECHO_T}$ac_cv_header_lua5_1_lua_h" >&6; }
+else
+  # Is the header compilable?
+{ echo "$as_me:$LINENO: checking lua5.1/lua.h usability" >&5
+echo $ECHO_N "checking lua5.1/lua.h usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+#include <lua5.1/lua.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_header_compiler=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking lua5.1/lua.h presence" >&5
+echo $ECHO_N "checking lua5.1/lua.h presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <lua5.1/lua.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null && {
+        test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       }; then
+  ac_header_preproc=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+  yes:no: )
+    { echo "$as_me:$LINENO: WARNING: lua5.1/lua.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: lua5.1/lua.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { echo "$as_me:$LINENO: WARNING: lua5.1/lua.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: lua5.1/lua.h: proceeding with the compiler's result" >&2;}
+    ac_header_preproc=yes
+    ;;
+  no:yes:* )
+    { echo "$as_me:$LINENO: WARNING: lua5.1/lua.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: lua5.1/lua.h: present but cannot be compiled" >&2;}
+    { echo "$as_me:$LINENO: WARNING: lua5.1/lua.h:     check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: lua5.1/lua.h:     check for missing prerequisite headers?" >&2;}
+    { echo "$as_me:$LINENO: WARNING: lua5.1/lua.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: lua5.1/lua.h: see the Autoconf documentation" >&2;}
+    { echo "$as_me:$LINENO: WARNING: lua5.1/lua.h:     section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: lua5.1/lua.h:     section \"Present But Cannot Be Compiled\"" >&2;}
+    { echo "$as_me:$LINENO: WARNING: lua5.1/lua.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: lua5.1/lua.h: proceeding with the preprocessor's result" >&2;}
+    { echo "$as_me:$LINENO: WARNING: lua5.1/lua.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: lua5.1/lua.h: in the future, the compiler will take precedence" >&2;}
+
+    ;;
+esac
+{ echo "$as_me:$LINENO: checking for lua5.1/lua.h" >&5
+echo $ECHO_N "checking for lua5.1/lua.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_lua5_1_lua_h+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_cv_header_lua5_1_lua_h=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_lua5_1_lua_h" >&5
+echo "${ECHO_T}$ac_cv_header_lua5_1_lua_h" >&6; }
+
+fi
+if test $ac_cv_header_lua5_1_lua_h = yes; then
+  LUA_HEADER_FOUND=1
+else
+  LUA_HEADER_FOUND=0
+fi
+
+
+        fi
+      fi
+      if test "x${LUA_HEADER_FOUND}" = "x0" ; then
+         LUA_LIB=""
+         LUA_INCLUDE=""
+      else
+         if test "x${pbxfuncname}" = "x" ; then                # only checking headers -> no library
+           LUA_LIB=""
+        fi
+         PBX_LUA=1
+         # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_LUA 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_LUA_VERSION
+_ACEOF
+
+      fi
+   fi
+fi
+
+
 
 if test "x${PBX_RADIUS}" != "x1" -a "${USE_RADIUS}" != "no"; then
    pbxlibdir=""
@@ -56493,6 +56919,10 @@ LTDL_LIB!$LTDL_LIB$ac_delim
 LTDL_INCLUDE!$LTDL_INCLUDE$ac_delim
 LTDL_DIR!$LTDL_DIR$ac_delim
 PBX_LTDL!$PBX_LTDL$ac_delim
+LUA_LIB!$LUA_LIB$ac_delim
+LUA_INCLUDE!$LUA_INCLUDE$ac_delim
+LUA_DIR!$LUA_DIR$ac_delim
+PBX_LUA!$PBX_LUA$ac_delim
 MISDN_LIB!$MISDN_LIB$ac_delim
 MISDN_INCLUDE!$MISDN_INCLUDE$ac_delim
 MISDN_DIR!$MISDN_DIR$ac_delim
@@ -56537,10 +56967,6 @@ PGSQL_LIB!$PGSQL_LIB$ac_delim
 PGSQL_INCLUDE!$PGSQL_INCLUDE$ac_delim
 PGSQL_DIR!$PGSQL_DIR$ac_delim
 PBX_PGSQL!$PBX_PGSQL$ac_delim
-PRI_LIB!$PRI_LIB$ac_delim
-PRI_INCLUDE!$PRI_INCLUDE$ac_delim
-PRI_DIR!$PRI_DIR$ac_delim
-PBX_PRI!$PBX_PRI$ac_delim
 _ACEOF
 
   if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 97; then
@@ -56582,6 +57008,10 @@ _ACEOF
 ac_delim='%!_!# '
 for ac_last_try in false false false false false :; do
   cat >conf$$subs.sed <<_ACEOF
+PRI_LIB!$PRI_LIB$ac_delim
+PRI_INCLUDE!$PRI_INCLUDE$ac_delim
+PRI_DIR!$PRI_DIR$ac_delim
+PBX_PRI!$PBX_PRI$ac_delim
 SS7_LIB!$SS7_LIB$ac_delim
 SS7_INCLUDE!$SS7_INCLUDE$ac_delim
 SS7_DIR!$SS7_DIR$ac_delim
@@ -56675,10 +57105,6 @@ AST_DECLARATION_AFTER_STATEMENT!$AST_DECLARATION_AFTER_STATEMENT$ac_delim
 GSM_INTERNAL!$GSM_INTERNAL$ac_delim
 KDEINIT!$KDEINIT$ac_delim
 KDEDIR!$KDEDIR$ac_delim
-NETSNMP_CONFIG!$NETSNMP_CONFIG$ac_delim
-PG_CONFIG!$PG_CONFIG$ac_delim
-PTLIB_CONFIG!$PTLIB_CONFIG$ac_delim
-PWLIBDIR!$PWLIBDIR$ac_delim
 _ACEOF
 
   if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 97; then
@@ -56720,6 +57146,10 @@ _ACEOF
 ac_delim='%!_!# '
 for ac_last_try in false false false false false :; do
   cat >conf$$subs.sed <<_ACEOF
+NETSNMP_CONFIG!$NETSNMP_CONFIG$ac_delim
+PG_CONFIG!$PG_CONFIG$ac_delim
+PTLIB_CONFIG!$PTLIB_CONFIG$ac_delim
+PWLIBDIR!$PWLIBDIR$ac_delim
 PWLIB_INCDIR!$PWLIB_INCDIR$ac_delim
 PWLIB_LIBDIR!$PWLIB_LIBDIR$ac_delim
 PWLIB_PLATFORM!$PWLIB_PLATFORM$ac_delim
@@ -56744,7 +57174,7 @@ CURL_CONFIG!$CURL_CONFIG$ac_delim
 LTLIBOBJS!$LTLIBOBJS$ac_delim
 _ACEOF
 
-  if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 22; then
+  if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 26; then
     break
   elif $ac_last_try; then
     { { echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5
index d07d75b..f25847f 100644 (file)
@@ -193,6 +193,7 @@ AST_EXT_LIB_SETUP([IMAP_TK], [UW IMAP Toolkit], [imap])
 AST_EXT_LIB_SETUP([ISDNNET], [ISDN4Linux Library], [isdnnet])
 AST_EXT_LIB_SETUP([KDE], [KDE], [kde])
 AST_EXT_LIB_SETUP([LTDL], [libtool], [ltdl])
+AST_EXT_LIB_SETUP([LUA], [Lua], [lua])
 AST_EXT_LIB_SETUP([MISDN], [mISDN User Library], [misdn])
 AST_EXT_LIB_SETUP([NBS], [Network Broadcast Sound], [nbs])
 AST_EXT_LIB_SETUP([NCURSES], [ncurses], [ncurses])
@@ -980,6 +981,10 @@ fi
 
 AC_LANG_POP
 
+LUA_INCLUDE="-I/usr/include/lua5.1"
+LUA_LIB="-llua5.1"
+AST_EXT_LIB_CHECK([LUA], [lua5.1], [luaL_newstate], [lua5.1/lua.h]) 
+
 AST_EXT_LIB_CHECK([RADIUS], [radiusclient-ng], [rc_read_config], [radiusclient-ng.h])
 
 AST_EXT_LIB_CHECK([SPEEX], [speex], [speex_encode], [speex/speex.h], [-lm])
index b906bc5..daf8a5c 100644 (file)
 /* Define to indicate the ${LTDL_DESCRIP} library version */
 #undef HAVE_LTDL_VERSION
 
+/* Define this to indicate the ${LUA_DESCRIP} library */
+#undef HAVE_LUA
+
+/* Define to indicate the ${LUA_DESCRIP} library version */
+#undef HAVE_LUA_VERSION
+
 /* Define to 1 if you have the <malloc.h> header file. */
 #undef HAVE_MALLOC_H
 
index 417b515..2b6ad75 100644 (file)
@@ -508,6 +508,20 @@ int ast_extension_match(const char *pattern, const char *extension);
 int ast_extension_close(const char *pattern, const char *data, int needmore);
 
 /*! 
+ * \brief Determine if one extension should match before another
+ * 
+ * \param a extension to compare with b
+ * \param b extension to compare with a
+ *
+ * Checks whether or extension a should match before extension b
+ *
+ * \retval 0 if the two extensions have equal matching priority
+ * \retval 1 on a > b
+ * \retval -1 on a < b
+ */
+int ast_extension_cmp(const char *a, const char *b);
+
+/*! 
  * \brief Launch a new extension (i.e. new stack)
  * 
  * \param c not important
index 46fc993..54e464c 100644 (file)
@@ -806,6 +806,11 @@ static int ext_cmp(const char *a, const char *b)
                return (ret > 0) ? 1 : -1;
 }
 
+int ast_extension_cmp(const char *a, const char *b)
+{
+       return ext_cmp(a, b);
+}
+
 /*!
  * \internal
  * \brief used ast_extension_{match|close}
index 3c09112..76ca94a 100644 (file)
@@ -99,6 +99,9 @@ KDEDIR=@KDEDIR@
 KDE_INCLUDE=@KDE_INCLUDE@
 KDE_LIB=@KDE_LIB@
 
+LUA_INCLUDE=@LUA_INCLUDE@
+LUA_LIB=@LUA_LIB@
+
 NBS_INCLUDE=@NBS_INCLUDE@
 NBS_LIB=@NBS_LIB@
 
diff --git a/pbx/pbx_lua.c b/pbx/pbx_lua.c
new file mode 100644 (file)
index 0000000..8c566b1
--- /dev/null
@@ -0,0 +1,1290 @@
+/*
+ * 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 <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.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"
+
+#include <lua.h>
+#include <lauxlib.h>
+#include <lualib.h>
+
+static char *config = "extensions.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_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 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);
+
+void lua_state_destroy(void *data);
+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);
+char *config_file_data = NULL;
+long config_file_size = 0;
+
+static const struct ast_datastore_info lua_datastore = {
+       .type = "lua",
+       .destroy = lua_state_destroy,
+};
+
+
+/*!
+ * \brief The destructor for lua_datastore
+ */
+void lua_state_destroy(void *data)
+{
+       if (data)
+               lua_close(data);
+}
+
+/*!
+ * \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 nargs = lua_gettop(L);
+       char data[LUA_EXT_DATA_SIZE] = "";
+       char *data_next = data;
+       size_t data_left = sizeof(data);
+       int res;
+       
+       lua_getfield(L, 1, "name");
+       char *app_name = ast_strdupa(lua_tostring(L, -1));
+       lua_pop(L, 1);
+       
+       struct ast_app *app = pbx_findapp(app_name);
+       if (!app) {
+               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");
+       struct ast_channel *chan = lua_touserdata(L, -1);
+       lua_pop(L, 1);
+       
+       
+       lua_getfield(L, LUA_REGISTRYINDEX, "context");
+       char *context = ast_strdupa(lua_tostring(L, -1));
+       lua_pop(L, 1);
+       
+       lua_getfield(L, LUA_REGISTRYINDEX, "exten");
+       char *exten = ast_strdupa(lua_tostring(L, -1));
+       lua_pop(L, 1);
+       
+       lua_getfield(L, LUA_REGISTRYINDEX, "priority");
+       int 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));
+
+               int i;
+               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));
+               }
+       }
+       
+       char tmp[80], tmp2[80], tmp3[LUA_EXT_DATA_SIZE];
+       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");
+       int 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)
+{
+       char *value = NULL;
+       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");
+       struct ast_channel *chan = lua_touserdata(L, -1);
+       lua_pop(L, 1);
+
+       lua_getfield(L, 1, "name");
+       char *name = ast_strdupa(lua_tostring(L, -1));
+       lua_pop(L, 1);
+       
+       lua_getfield(L, LUA_REGISTRYINDEX, "autoservice");
+       int 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()
+ * \endcode
+ */
+static int lua_set_variable_value(lua_State *L)
+{
+       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");
+       const char *name = ast_strdupa(lua_tostring(L, -1));
+       lua_pop(L, 1);
+
+       const char *value = luaL_checkstring(L, 2);
+       
+       lua_getfield(L, LUA_REGISTRYINDEX, "channel");
+       struct ast_channel *chan = lua_touserdata(L, -1);
+       lua_pop(L, 1);
+
+       lua_getfield(L, LUA_REGISTRYINDEX, "autoservice");
+       int 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 [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");
+       struct ast_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)
+{
+       const char *name = luaL_checkstring(L, 2);
+       const char *value = luaL_checkstring(L, 3);
+
+       lua_getfield(L, LUA_REGISTRYINDEX, "channel");
+       struct ast_channel *chan = lua_touserdata(L, -1);
+       lua_pop(L, 1);
+
+       lua_getfield(L, LUA_REGISTRYINDEX, "autoservice");
+       int 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;
+       size_t fullname_left = sizeof(fullname);
+       
+       lua_getfield(L, 1, "name");
+       char *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));
+
+               int i;
+               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)
+{
+       lua_getfield(L, LUA_REGISTRYINDEX, "channel");
+       struct ast_channel *chan = lua_touserdata(L, -1);
+       lua_pop(L, 1);
+
+       int 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)
+{
+       lua_getfield(L, LUA_REGISTRYINDEX, "channel");
+       struct ast_channel *chan = lua_touserdata(L, -1);
+       lua_pop(L, 1);
+
+       int 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 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)
+{
+       /* create the extensions_order table */
+       lua_newtable(L);
+       lua_setfield(L, LUA_REGISTRYINDEX, "extensions_order");
+       lua_getfield(L, LUA_REGISTRYINDEX, "extensions_order");
+       int extensions_order = lua_gettop(L);
+
+       /* sort each context in the extensions table */
+       /* load the 'extensions' table */
+       lua_getglobal(L, "extensions");
+       int 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;
+
+               lua_pushvalue(L, context_name);
+               lua_newtable(L);
+               int 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 [lua_CFunction] Compare two extensions (for access from lua, don't
+ * call directly)
+ *
+ * This function returns true if the first extension passed should match after
+ * the second.  It behaves like the '<' operator.
+ */
+static int lua_extension_cmp(lua_State *L)
+{
+       const char *a = luaL_checkstring(L, -2);
+       const char *b = luaL_checkstring(L, -1);
+
+       if (ast_extension_cmp(a, b) == -1)
+               lua_pushboolean(L, 1);
+       else
+               lua_pushboolean(L, 0);
+
+       return 1;
+}
+
+/*!
+ * \brief Load the extensions.lua file in to a buffer and execute the file
+ *
+ * \param L the lua_State to use
+ * \param size a pointer to store the size of the buffer
+ *
+ * \note The caller is expected to free the buffer at some point.
+ *
+ * \return a pointer to the buffer
+ */
+static char *lua_read_extensions_file(lua_State *L, long *size)
+{
+       char *path = alloca(strlen(config) + strlen(ast_config_AST_CONFIG_DIR) + 2);
+       sprintf(path, "%s/%s", ast_config_AST_CONFIG_DIR, config);
+
+       FILE *f = fopen(path, "r");
+       if (!f) {
+               lua_pushstring(L, "cannot open '");
+               lua_pushstring(L, path);
+               lua_pushstring(L, "' for reading: ");
+               lua_pushstring(L, strerror(errno));
+               lua_concat(L, 4);
+
+               return NULL;
+       }
+
+       fseek(f, 0l, SEEK_END);
+       *size = ftell(f);
+
+       fseek(f, 0l, SEEK_SET);
+
+       char *data = ast_malloc(*size);
+       if (!data) {
+               *size = 0;
+               fclose(f);
+               lua_pushstring(L, "not enough memory");
+               return NULL;
+       }
+
+       fread(data, sizeof(char), *size, f);
+       fclose(f);
+
+       if (luaL_loadbuffer(L, data, *size, "extensions.lua")
+                       || lua_pcall(L, 0, LUA_MULTRET, 0)
+                       || lua_sort_extensions(L)) {
+               ast_free(data);
+               data = NULL;
+               *size = 0;
+       }
+       return data;
+}
+
+/*!
+ * \brief Load the extensions.lua file from the internal buffer
+ *
+ * \param L the lua_State to use
+ *
+ * This function also sets up some constructs used by the extensions.lua file.
+ *
+ * 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_load_extensions(lua_State *L, struct ast_channel *chan)
+{
+       
+       /* store a pointer to this channel */
+       lua_pushlightuserdata(L, chan);
+       lua_setfield(L, LUA_REGISTRYINDEX, "channel");
+       
+       luaL_openlibs(L);
+
+       /* load and sort extensions */
+       ast_mutex_lock(&config_file_lock);
+       if (luaL_loadbuffer(L, config_file_data, config_file_size, "extensions.lua")
+                       || lua_pcall(L, 0, LUA_MULTRET, 0)
+                       || lua_sort_extensions(L)) {
+               ast_mutex_unlock(&config_file_lock);
+               return 1;
+       }
+       ast_mutex_unlock(&config_file_lock);
+
+       /* now we setup special tables and functions */
+
+       lua_create_app_table(L);
+       lua_create_channel_table(L);
+
+       lua_create_variable_metatable(L);
+       lua_create_application_metatable(L);
+
+       lua_create_autoservice_functions(L);
+
+       return 0;
+}
+
+/*!
+ * \brief Reload the extensions file and update the internal buffers if it
+ * loads correctly.
+ *
+ * \warning This function should not be called on a lua_State returned from
+ * lua_get_state().
+ *
+ * \param L the lua_State to use (must be freshly allocated with
+ * luaL_newstate(), don't use lua_get_state())
+ */
+static int lua_reload_extensions(lua_State *L)
+{
+       long size = 0;
+       char *data = NULL;
+
+       luaL_openlibs(L);
+
+       if (!(data = lua_read_extensions_file(L, &size))) {
+               return 1;
+       }
+
+       ast_mutex_lock(&config_file_lock);
+
+       if (config_file_data)
+               ast_free(config_file_data);
+
+       config_file_data = data;
+       config_file_size = size;
+
+       ast_mutex_unlock(&config_file_lock);
+       return 0;
+}
+
+/*!
+ * \brief Free the internal extensions buffer.
+ */
+static void lua_free_extensions()
+{
+       ast_mutex_lock(&config_file_lock);
+       config_file_size = 0;
+       ast_free(config_file_data);
+       ast_mutex_unlock(&config_file_lock);
+}
+
+/*!
+ * \brief Get the lua_State for this channel
+ *
+ * If no channel is passed then a new state is allocated.  States with no
+ * channel assocatied with them should only be used for matching extensions.
+ * If the channel does not yet have a lua state associated with it, one will be
+ * created.
+ *
+ * \note If no channel was passed then the caller is expected to free the state
+ * using lua_close().
+ *
+ * \return a lua_State
+ */
+static lua_State *lua_get_state(struct ast_channel *chan)
+{
+       struct ast_datastore *datastore = NULL;
+       if (!chan) {
+               lua_State *L = luaL_newstate();
+               if (!L) {
+                       ast_log(LOG_ERROR, "Error allocating lua_State, no memory\n");
+                       return NULL;
+               }
+
+               if (lua_load_extensions(L, NULL)) {
+                       const char *error = lua_tostring(L, -1);
+                       ast_log(LOG_ERROR, "Error loading extensions.lua: %s\n", error);
+                       lua_close(L);
+                       return NULL;
+               }
+               return L;
+       } else {
+               datastore = ast_channel_datastore_find(chan, &lua_datastore, NULL);
+
+               if (!datastore) {
+                       /* nothing found, allocate a new lua state */
+                       datastore = ast_channel_datastore_alloc(&lua_datastore, NULL);
+                       if (!datastore) {
+                               ast_log(LOG_ERROR, "Error allocation channel datastore for lua_State\n");
+                               return NULL;
+                       }
+
+                       datastore->data = luaL_newstate();
+                       if (!datastore->data) {
+                               ast_channel_datastore_free(datastore);
+                               ast_log(LOG_ERROR, "Error allocating lua_State, no memory\n");
+                               return NULL;
+                       }
+
+                       ast_channel_lock(chan);
+                       ast_channel_datastore_add(chan, datastore);
+                       ast_channel_unlock(chan);
+
+                       lua_State *L = datastore->data;
+
+                       if (lua_load_extensions(L, chan)) {
+                               const char *error = lua_tostring(L, -1);
+                               ast_log(LOG_ERROR, "Error loading extensions.lua for %s: %s\n", chan->name, error);
+
+                               ast_channel_lock(chan);
+                               ast_channel_datastore_remove(chan, datastore);
+                               ast_channel_unlock(chan);
+
+                               ast_channel_datastore_free(datastore);
+                               return NULL;
+                       }
+               }
+
+               return datastore->data;
+       }
+}
+
+static int exists(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
+{
+       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;
+       }
+
+       lua_State *L = lua_get_state(chan);
+       if (!L) {
+               ast_module_user_remove(u);
+               return 0;
+       }
+
+       int res = lua_find_extension(L, context, exten, priority, &exists, 0);
+
+       if (!chan) lua_close(L);
+       ast_module_user_remove(u);
+       return res;
+}
+
+static int canmatch(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
+{
+       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;
+       }
+
+       lua_State *L = lua_get_state(chan);
+       if (!L) {
+               ast_module_user_remove(u);
+               return 0;
+       }
+
+       int res =  lua_find_extension(L, context, exten, priority, &canmatch, 0);
+
+       if (!chan) lua_close(L);
+       ast_module_user_remove(u);
+       return res;
+}
+
+static int matchmore(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
+{
+       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;
+       }
+
+       lua_State *L = lua_get_state(chan);
+       if (!L) {
+               ast_module_user_remove(u);
+               return 0;
+       }
+       
+       int res =  lua_find_extension(L, context, exten, priority, &matchmore, 0);
+
+       if (!chan) lua_close(L);
+       ast_module_user_remove(u);
+       return res;
+}
+
+
+static int exec(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
+{
+       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;
+       }
+       
+       int res;
+       
+       lua_State *L = lua_get_state(chan);
+       if (!L) {
+               ast_module_user_remove(u);
+               return -1;
+       }
+
+       /* push the extension function onto the stack */
+       if (!lua_find_extension(L, context, exten, priority, &exists, 1)) {
+               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_update_registry(L, context, exten, priority);
+       
+       lua_pushstring(L, context);
+       lua_pushstring(L, exten);
+       
+       res = lua_pcall(L, 2, 0, 0);
+       if (res) {
+               if (res == LUA_ERRRUN) {
+                       if (lua_isnumber(L, -1)) {
+                               res = lua_tointeger(L, -1);
+                       } else if (lua_isstring(L, -1)) {
+                               const char *error = lua_tostring(L, -1);
+                               ast_log(LOG_ERROR, "Error executing lua extension: %s\n", error);
+                               res = -1;
+                       }
+               } else {
+                       res = -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)
+{
+       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);
+
+       int 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 */
+
+       int context_order_table = lua_gettop(L);
+       
+       /* step through the extensions looking for a match */
+       int i;
+       for (i = 1; i < lua_objlen(L, context_order_table) + 1; i++) {
+               lua_pushinteger(L, i);
+               lua_gettable(L, context_order_table);
+               int e_index = lua_gettop(L);
+               int isnumber = lua_isnumber(L, e_index);
+
+               const char *e = lua_tostring(L, e_index);
+               if (!e) {
+                       lua_pop(L, 1);
+                       continue;
+               }
+
+               /* make sure this is not the 'include' extension */
+               if(!strcasecmp(e, "include")) {
+                       lua_pop(L, 1);
+                       continue;
+               }
+
+               int match = 0;
+               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) {
+                       lua_pop(L, 1);
+                       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, 3);
+                       return 0;
+               }
+
+               /* remove the context table, the context order table, and the
+                * extension (or replace the extension with the corisponding
+                * function) */
+               if (push_func) {
+                       /* here we must convert the exten back to an integer
+                        * because lua_tostring will change the value on the
+                        * stack to a string */
+                       if (isnumber) {
+                               int e_int = lua_tointeger(L, e_index);
+                               lua_pop(L, 1);  /* the exten should be the top of the stack */
+                               lua_pushinteger(L, e_int);
+                       }
+                       lua_gettable(L, context_table);
+                       lua_insert(L, -3);
+                       lua_pop(L, 2);
+               } else {
+                       lua_pop(L, 3);
+               }
+
+               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)
+{
+       lua_State *L = luaL_newstate();
+       if (!L) {
+               ast_log(LOG_ERROR, "Error allocating lua_State, no memory\n");
+               return AST_MODULE_LOAD_FAILURE;
+       }
+
+       int res = AST_MODULE_LOAD_SUCCESS;
+
+       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_FAILURE;
+       }
+
+       lua_close(L);
+       return res;
+}
+
+static int unload_module(void)
+{
+       ast_unregister_switch(&lua_switch);
+       lua_free_extensions();
+       return 0;
+}
+
+static int reload(void)
+{
+       return load_or_reload_lua_stuff();
+}
+
+static int load_module(void)
+{
+       if (load_or_reload_lua_stuff())
+               return AST_MODULE_LOAD_FAILURE;
+
+       if (ast_register_switch(&lua_switch)) {
+               ast_log(LOG_ERROR, "Unable to register LUA PBX switch\n");
+               return AST_MODULE_LOAD_FAILURE;
+       }
+
+       return AST_MODULE_LOAD_SUCCESS;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Lua PBX Switch",
+               .load = load_module,
+               .unload = unload_module,
+               .reload = reload,
+              );
+
diff --git a/utils/build-extensions-conf.lua b/utils/build-extensions-conf.lua
new file mode 100755 (executable)
index 0000000..a3f159d
--- /dev/null
@@ -0,0 +1,81 @@
+#!/usr/bin/env lua
+--[[
+
+This utility can be used to generate an extensions.conf file to match an
+existing extensions.lua file.  As an argument it takes the patch of the
+extensions.lua file to read from, otherwise it uses
+/etc/asterisk/extensions.lua.
+
+This script can also be used to automatically include extensions.lua in
+extensions.conf via a #exec as well.
+
+#exec /usr/bin/build-extensions-conf.lua -c
+
+--]]
+
+usage = [[
+
+Usage:
+  ]] .. arg[0] .. [[ [options] [extensions.lua path]
+
+This utility can generate an extensions.conf file with all of the contexts in
+your extensions.lua file defined as including the Lua switch.  This is useful
+if you want to use your extensions.lua file exclusively.  By using this utility
+you dont't have to create each extension in extensions.conf manually.
+
+The resulting extensions.conf file is printed to standard output.
+
+  --contexts-only, -c  Don't print the [global] or [general] sections.  This
+                       is useful for including the generated file into an
+                       existing extensions.conf via #include or #exec.
+
+  --help, -h           Print this message.
+
+]]
+
+extensions_file = "/etc/asterisk/extensions.lua"
+
+options = {}
+
+for k, v in ipairs(arg) do
+       if v:sub(1, 1) == "-" then
+               if v == "-h" or v == "--help" then
+                       print("match")
+                       options["help"] = true
+               elseif v == "-c" or v == "--contexts-only" then
+                       options["contexts-only"] = true
+               end
+       else
+               options["extensions-file"] = v
+       end
+end
+
+if options["help"] then
+       io.stderr:write(usage)
+       os.exit(0)
+end
+
+if options["extensions-file"] then
+       extensions_file = options["extensions-file"]
+end
+
+result, error_message = pcall(dofile, extensions_file)
+
+if not result then
+       io.stderr:write(error_message .. "\n")
+       os.exit(1)
+end
+
+if not extensions then
+       io.stderr:write("Error: extensions table not found in '" .. extensions_file .. "'\n")
+       os.exit(1)
+end
+
+if not options["contexts-only"] then
+       io.stdout:write("[general]\n\n[globals]\n\n")
+end
+
+for context, extens in pairs(extensions) do
+       io.stdout:write("[" .. tostring(context) ..  "]\nswitch => Lua\n\n")
+end
+