/* * lua_gui.c * * (c) 2006, Luis E. Garcia Ontanon * * $Id$ * * Wireshark - Network traffic analyzer * By Gerald Combs * Copyright 1998 Gerald Combs * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "elua.h" static const funnel_ops_t* ops = NULL; struct _lua_menu_data { lua_State* L; int cb_ref; int data_ref; }; static int menu_cb_error_handler(lua_State* L) { const gchar* error = lua_tostring(L,1); report_failure("Lua: Error During execution of Menu Callback:\n %s",error); return 0; } ELUA_FUNCTION lua_gui_enabled(lua_State* L) { /* Checks whether the GUI facility is enabled. */ lua_pushboolean(L,GPOINTER_TO_INT(ops)); ELUA_RETURN(1); /* A boolean: true if it is enabled, false if it isn't. */ } void lua_menu_callback(gpointer data) { struct _lua_menu_data* md = data; lua_pushcfunction(md->L,menu_cb_error_handler); lua_rawgeti(md->L, LUA_REGISTRYINDEX, md->cb_ref); lua_rawgeti(md->L, LUA_REGISTRYINDEX, md->data_ref); lua_pcall(md->L,1,0,1); return; } ELUA_FUNCTION elua_register_menu(lua_State* L) { /* Register a menu item in the Statistics menu. */ #define ELUA_ARG_register_menu_NAME 1 /* The name of the menu item. */ #define ELUA_ARG_register_menu_ACTION 2 /* The function to be called when the menu item is invoked. */ #define ELUA_OPTARG_register_menu_USERDATA 3 /* To be passed to the action. */ const gchar* name = luaL_checkstring(L,ELUA_ARG_register_menu_NAME); struct _lua_menu_data* md; gboolean retap = FALSE; if(!name) ELUA_ARG_ERROR(register_menu,NAME,"must be a string"); if (!lua_isfunction(L,ELUA_ARG_register_menu_ACTION)) ELUA_ARG_ERROR(register_menu,ACTION,"must be a function"); md = g_malloc(sizeof(struct _lua_menu_data)); md->L = L; lua_pushvalue(L, 2); md->cb_ref = luaL_ref(L, LUA_REGISTRYINDEX); if ( lua_gettop(L) > 2) { lua_pushvalue(L, ELUA_OPTARG_register_menu_USERDATA); md->data_ref = luaL_ref(L, LUA_REGISTRYINDEX); } else { md->data_ref = LUA_NOREF; } funnel_register_menu(name, REGISTER_STAT_GROUP_GENERIC, lua_menu_callback, md, retap); ELUA_RETURN(0); } struct _dlg_cb_data { lua_State* L; int func_ref; int data_ref; }; static int dlg_cb_error_handler(lua_State* L) { const gchar* error = lua_tostring(L,1); report_failure("Lua: Error During execution of dialog callback:\n %s",error); return 0; } static void lua_dialog_cb(gchar** user_input, void* data) { struct _dlg_cb_data* dcbd = data; int i = 0; gchar* input; lua_State* L = dcbd->L; lua_settop(L,0); lua_pushcfunction(L,dlg_cb_error_handler); lua_rawgeti(L, LUA_REGISTRYINDEX, dcbd->func_ref); lua_rawgeti(L, LUA_REGISTRYINDEX, dcbd->data_ref); for (i = 0; (input = user_input[i]) ; i++) { lua_pushstring(L,input); g_free(input); } g_free(user_input); switch ( lua_pcall(L,i+1,0,1) ) { case 0: break; case LUA_ERRRUN: g_warning("Runtime error while calling dialog callback"); break; case LUA_ERRMEM: g_warning("Memory alloc error while calling dialog callback"); break; default: g_assert_not_reached(); break; } } ELUA_FUNCTION elua_new_dialog(lua_State* L) { /* Pops up a new dialog */ #define ELUA_ARG_new_dialog_TITLE 1 /* Title of the dialog's window. */ #define ELUA_ARG_new_dialog_ACTION 2 /* Action to be performed when OKd. */ /* ELUA_MOREARGS new_dialog A series of strings to be used as labels of the dialog's fields */ const gchar* title; int top = lua_gettop(L); int i; GPtrArray* labels; struct _dlg_cb_data* dcbd; if (! ops) { luaL_error(L,"the GUI facility has to be enabled"); return 0; } if (! (title = luaL_checkstring(L,ELUA_ARG_new_dialog_TITLE)) ) { ELUA_ARG_ERROR(new_dialog,TITLE,"must be a string"); return 0; } if (! lua_isfunction(L,ELUA_ARG_new_dialog_ACTION)) { ELUA_ARG_ERROR(new_dialog,ACTION,"must be a function"); return 0; } if (top < 3) { ELUA_ERROR(new_dialog,"at least one field required"); return 0; } dcbd = g_malloc(sizeof(struct _dlg_cb_data)); dcbd->L = L; lua_remove(L,1); lua_pushvalue(L, 1); dcbd->func_ref = luaL_ref(L, LUA_REGISTRYINDEX); lua_remove(L,1); lua_pushvalue(L, 1); dcbd->data_ref = luaL_ref(L, LUA_REGISTRYINDEX); lua_remove(L,1); labels = g_ptr_array_new(); top -= 3; for (i = 1; i <= top; i++) { gchar* label = (void*)luaL_checkstring(L,i); /* XXX leaks labels on error */ if (! label) ELUA_ERROR(new_dialog,"all fields must be strings"); g_ptr_array_add(labels,label); } g_ptr_array_add(labels,NULL); ops->new_dialog(title, (const gchar**)labels->pdata, lua_dialog_cb, dcbd); g_ptr_array_free(labels,TRUE); ELUA_RETURN(0); } ELUA_CLASS_DEFINE(TextWindow,NOP) /* Manages a text window. */ ELUA_CONSTRUCTOR TextWindow_new(lua_State* L) { /* Creates a new TextWindow. */ #define ELUA_OPTARG_TextWindow_new_TITLE 1 /* Title of the new window. */ const gchar* title; TextWindow tw; title = luaL_optstring(L,ELUA_OPTARG_TextWindow_new_TITLE,"Untitled Window"); tw = ops->new_text_window(title); pushTextWindow(L,tw); ELUA_RETURN(1); /* The newly created TextWindow object. */ } struct _close_cb_data { lua_State* L; int func_ref; int data_ref; }; int text_win_close_cb_error_handler(lua_State* L) { const gchar* error = lua_tostring(L,1); report_failure("Lua: Error During execution of TextWindow close callback:\n %s",error); return 0; } static void text_win_close_cb(void* data) { struct _close_cb_data* cbd = data; lua_State* L = cbd->L; lua_pushcfunction(L,text_win_close_cb_error_handler); lua_rawgeti(L, LUA_REGISTRYINDEX, cbd->func_ref); lua_rawgeti(L, LUA_REGISTRYINDEX, cbd->data_ref); switch ( lua_pcall(L,1,0,1) ) { case 0: break; case LUA_ERRRUN: g_warning("Runtime error during execution of TextWindow close callback"); break; case LUA_ERRMEM: g_warning("Memory alloc error during execution of TextWindow close callback"); break; default: g_assert_not_reached(); break; } } ELUA_METHOD TextWindow_set_atclose(lua_State* L) { /* Set the function that will be called when the window closes */ #define ELUA_ARG_TextWindow_at_close_ACTION 2 /* A function to be executed when the user closes the window */ TextWindow tw = checkTextWindow(L,1); struct _close_cb_data* cbd; if (!tw) ELUA_ERROR(TextWindow_at_close,"cannot be called for something not a TextWindow"); lua_settop(L,3); if (! lua_isfunction(L,2)) ELUA_ARG_ERROR(TextWindow_at_close,ACTION,"must be a function"); cbd = g_malloc(sizeof(struct _close_cb_data)); cbd->L = L; cbd->data_ref = luaL_ref(L, LUA_REGISTRYINDEX); cbd->func_ref = luaL_ref(L, LUA_REGISTRYINDEX); ops->set_close_cb(tw,text_win_close_cb,cbd); pushTextWindow(L,tw); ELUA_RETURN(1); /* The TextWindow object. */ } ELUA_METHOD TextWindow_set(lua_State* L) { /* Sets the text. */ #define ELUA_ARG_TextWindow_set_TEXT 2 /* The text to be used. */ TextWindow tw = checkTextWindow(L,1); const gchar* text = luaL_checkstring(L,2); if (!tw) ELUA_ERROR(TextWindow_set,"cannot be called for something not a TextWindow"); if (!text) ELUA_ARG_ERROR(TextWindow_set,TEXT,"must be a string"); ops->set_text(tw,text); pushTextWindow(L,tw); ELUA_RETURN(1); /* The TextWindow object. */ } ELUA_METHOD TextWindow_append(lua_State* L) { /* Appends text */ #define ELUA_ARG_TextWindow_append_TEXT 2 /* The text to be appended */ TextWindow tw = checkTextWindow(L,1); const gchar* text = luaL_checkstring(L,2); if (!tw) ELUA_ERROR(TextWindow_append,"cannot be called for something not a TextWindow"); if (!text) ELUA_ARG_ERROR(TextWindow_append,TEXT,"must be a string"); ops->append_text(tw,text); pushTextWindow(L,tw); ELUA_RETURN(1); /* The TextWindow object. */ } ELUA_METHOD TextWindow_prepend(lua_State* L) { /* Prepends text */ #define ELUA_ARG_TextWindow_prepend_TEXT 2 /* The text to be appended */ TextWindow tw = checkTextWindow(L,1); const gchar* text = luaL_checkstring(L,2); if (!tw) ELUA_ERROR(TextWindow_prepend,"cannot be called for something not a TextWindow"); if (!text) ELUA_ARG_ERROR(TextWindow_prepend,TEXT,"must be a string"); ops->prepend_text(tw,text); pushTextWindow(L,tw); ELUA_RETURN(1); /* The TextWindow object. */ } ELUA_METHOD TextWindow_clear(lua_State* L) { /* Errases all text in the window. */ TextWindow tw = checkTextWindow(L,1); if (!tw) ELUA_ERROR(TextWindow_clear,"cannot be called for something not a TextWindow"); ops->clear_text(tw); pushTextWindow(L,tw); ELUA_RETURN(1); /* The TextWindow object. */ } ELUA_METHOD TextWindow_get_text(lua_State* L) { /* Get the text of the window */ TextWindow tw = checkTextWindow(L,1); const gchar* text; if (!tw) ELUA_ERROR(TextWindow_get_text,"cannot be called for something not a TextWindow"); text = ops->get_text(tw); lua_pushstring(L,text); ELUA_RETURN(1); /* The TextWindow's text. */ } static int TextWindow__gc(lua_State* L) { TextWindow tw = checkTextWindow(L,1); if (!tw) ELUA_ERROR(TextWindow_gc,"cannot be called for something not a TextWindow"); ops->destroy_text_window(tw); return 1; } ELUA_METHOD TextWindow_set_editable(lua_State* L) { /* Set the function that will be called when the window closes */ #define ELUA_OPTARG_TextWindow_at_close_EDITABLE 2 /* A boolean flag, defaults to true */ TextWindow tw = checkTextWindow(L,1); gboolean editable = luaL_optint(L,2,1); if (!tw) ELUA_ERROR(TextWindow_at_close,"cannot be called for something not a TextWindow"); if (ops->set_editable) ops->set_editable(tw,editable); pushTextWindow(L,tw); ELUA_RETURN(1); /* The TextWindow object. */ } typedef struct _elua_bt_cb_t { lua_State* L; int func_ref; int data_ref; } elua_bt_cb_t; static gboolean elua_button_callback(funnel_text_window_t* tw, void* data) { elua_bt_cb_t* cbd = data; lua_State* L = cbd->L; lua_settop(L,0); lua_pushcfunction(L,dlg_cb_error_handler); lua_rawgeti(L, LUA_REGISTRYINDEX, cbd->func_ref); pushTextWindow(L,tw); lua_rawgeti(L, LUA_REGISTRYINDEX, cbd->data_ref); switch ( lua_pcall(L,2,0,1) ) { case 0: break; case LUA_ERRRUN: g_warning("Runtime error while calling button callback"); break; case LUA_ERRMEM: g_warning("Memory alloc error while calling button callback"); break; default: g_assert_not_reached(); break; } return TRUE; } ELUA_METHOD TextWindow_add_button(lua_State* L) { #define ELUA_ARG_TextWindow_add_button_LABEL 2 /* The label of the button */ #define ELUA_ARG_TextWindow_add_button_FUNCTION 3 /* The function to be called when clicked */ #define ELUA_ARG_TextWindow_add_button_DATA 4 /* The data to be passed to the function (other than the window) */ TextWindow tw = checkTextWindow(L,1); const gchar* label = luaL_checkstring(L,ELUA_ARG_TextWindow_add_button_LABEL); funnel_bt_t* fbt; elua_bt_cb_t* cbd; if (!tw) ELUA_ERROR(TextWindow_at_close,"cannot be called for something not a TextWindow"); if (! lua_isfunction(L,ELUA_ARG_TextWindow_add_button_FUNCTION) ) ELUA_ARG_ERROR(TextWindow_add_button,FUNCTION,"must be a function"); lua_settop(L,4); if (ops->add_button) { fbt = ep_alloc(sizeof(funnel_bt_t)); cbd = ep_alloc(sizeof(elua_bt_cb_t)); fbt->tw = tw; fbt->func = elua_button_callback; fbt->data = cbd; cbd->L = L; cbd->data_ref = luaL_ref(L, LUA_REGISTRYINDEX); cbd->func_ref = luaL_ref(L, LUA_REGISTRYINDEX); ops->add_button(tw,fbt,label); } pushTextWindow(L,tw); ELUA_RETURN(1); /* The TextWindow object. */ } ELUA_METHODS TextWindow_methods[] = { ELUA_CLASS_FNREG(TextWindow,new), ELUA_CLASS_FNREG(TextWindow,set), ELUA_CLASS_FNREG(TextWindow,new), ELUA_CLASS_FNREG(TextWindow,append), ELUA_CLASS_FNREG(TextWindow,prepend), ELUA_CLASS_FNREG(TextWindow,clear), ELUA_CLASS_FNREG(TextWindow,set_atclose), ELUA_CLASS_FNREG(TextWindow,set_editable), ELUA_CLASS_FNREG(TextWindow,get_text), ELUA_CLASS_FNREG(TextWindow,add_button), {0, 0} }; ELUA_META TextWindow_meta[] = { {"__tostring", TextWindow_get_text}, {"__gc", TextWindow__gc}, {0, 0} }; int TextWindow_register(lua_State* L) { ops = funnel_get_funnel_ops(); ELUA_REGISTER_CLASS(TextWindow); return 1; } ELUA_FUNCTION elua_retap_packets(lua_State* L) { /* Rescan all packets and just run taps - don't reconstruct the display. */ if ( ops->retap_packets ) { ops->retap_packets(); } else { ELUA_ERROR(elua_retap_packets, "does not work on TShark"); } return 0; }