diff options
Diffstat (limited to 'epan/wslua/wslua_utility.c')
-rw-r--r-- | epan/wslua/wslua_utility.c | 544 |
1 files changed, 544 insertions, 0 deletions
diff --git a/epan/wslua/wslua_utility.c b/epan/wslua/wslua_utility.c new file mode 100644 index 00000000..3756c511 --- /dev/null +++ b/epan/wslua/wslua_utility.c @@ -0,0 +1,544 @@ +/* + * wslua_utility.c + * + * (c) 2006, Luis E. Garcia Ontanon <luis@ontanon.org> + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "config.h" + +/* WSLUA_MODULE Utility Utility Functions */ + +#include "wslua.h" +#include <math.h> +#include <epan/stat_tap_ui.h> +#include <epan/prefs.h> +#include <epan/prefs-int.h> + + +WSLUA_FUNCTION wslua_get_version(lua_State* L) { /* Gets the Wireshark version as a string. */ + const gchar* str = VERSION; + lua_pushstring(L,str); + WSLUA_RETURN(1); /* The version string, e.g. "3.2.5". */ +} + + +static gchar* current_plugin_version = NULL; + +const gchar* get_current_plugin_version(void) { + return current_plugin_version ? current_plugin_version : ""; +} + +void clear_current_plugin_version(void) { + if (current_plugin_version != NULL) { + g_free(current_plugin_version); + current_plugin_version = NULL; + } +} + +WSLUA_FUNCTION wslua_set_plugin_info(lua_State* L) { + /* + Set a Lua table with meta-data about the plugin, such as version. + + The passed-in Lua table entries need to be keyed/indexed by the following: + + * "version" with a string value identifying the plugin version (required) + * "description" with a string value describing the plugin (optional) + * "author" with a string value of the author's name(s) (optional) + * "repository" with a string value of a URL to a repository (optional) + + Not all of the above key entries need to be in the table. The 'version' + entry is required, however. The others are not currently used for anything, but + might be in the future and thus using them might be useful. Table entries keyed + by other strings are ignored, and do not cause an error. + + ===== Example + + [source,lua] + ---- + local my_info = { + version = "1.0.1", + author = "Jane Doe", + repository = "https://github.com/octocat/Spoon-Knife" + } + + set_plugin_info(my_info) + ---- + + @since 1.99.8 + */ +#define WSLUA_ARG_set_plugin_info_TABLE 1 /* The Lua table of information. */ + + if ( lua_istable(L,WSLUA_ARG_set_plugin_info_TABLE) ) { + int top; + lua_getfield(L, WSLUA_ARG_set_plugin_info_TABLE, "version"); + top = lua_gettop(L); + if (lua_isstring(L, top)) { + clear_current_plugin_version(); + current_plugin_version = g_strdup( luaL_checkstring(L, top) ); + /* pop the string */ + lua_pop(L, 1); + } + else { + return luaL_error(L,"the Lua table must have a 'version' key entry with a string value"); + } + } else { + return luaL_error(L,"a Lua table with at least a 'version' string entry"); + } + + return 0; +} + + +WSLUA_FUNCTION wslua_format_date(lua_State* LS) { /* Formats an absolute timestamp into a human readable date. */ +#define WSLUA_ARG_format_date_TIMESTAMP 1 /* A timestamp value to convert. */ + lua_Number timestamp = luaL_checknumber(LS,WSLUA_ARG_format_date_TIMESTAMP); + nstime_t then; + gchar* str; + + then.secs = (time_t)(floor(timestamp)); + then.nsecs = (guint32) ( (timestamp-(double)(then.secs))*1000000000); + str = abs_time_to_str(NULL, &then, ABSOLUTE_TIME_LOCAL, TRUE); + lua_pushstring(LS,str); + wmem_free(NULL, str); + + WSLUA_RETURN(1); /* A string with the formated date */ +} + +WSLUA_FUNCTION wslua_format_time(lua_State* LS) { /* Formats a relative timestamp in a human readable time. */ +#define WSLUA_ARG_format_time_TIMESTAMP 1 /* A timestamp value to convert. */ + lua_Number timestamp = luaL_checknumber(LS,WSLUA_ARG_format_time_TIMESTAMP); + nstime_t then; + gchar* str; + + then.secs = (time_t)(floor(timestamp)); + then.nsecs = (guint32) ( (timestamp-(double)(then.secs))*1000000000); + str = rel_time_to_str(NULL, &then); + lua_pushstring(LS,str); + wmem_free(NULL, str); + + WSLUA_RETURN(1); /* A string with the formated time */ +} + +WSLUA_FUNCTION wslua_get_preference(lua_State *L) { + /* Get a preference value. @since 3.5.0 */ +#define WSLUA_ARG_get_preference_PREFERENCE 1 /* The name of the preference. */ + const gchar* preference = luaL_checkstring(L,WSLUA_ARG_get_preference_PREFERENCE); + + /* Split preference from module.preference */ + gchar *module_name = g_strdup(preference); + gchar *preference_name = strchr(module_name, '.'); + pref_t *pref = NULL; + + if (preference_name) { + *preference_name = '\0'; + preference_name++; + + module_t *module = prefs_find_module(module_name); + pref = prefs_find_preference(module, preference_name); + } + g_free (module_name); + + if (pref) { + switch (prefs_get_type(pref)) { + case PREF_UINT: + { + guint uint_value = prefs_get_uint_value_real(pref, pref_current); + lua_pushinteger(L, uint_value); + break; + } + case PREF_BOOL: + { + gboolean bool_value = prefs_get_bool_value(pref, pref_current); + lua_pushboolean(L, bool_value); + break; + } + case PREF_ENUM: + { + const enum_val_t *enums; + gint enum_value = prefs_get_enum_value(pref, pref_current); + + for (enums = prefs_get_enumvals(pref); enums->name; enums++) { + if (enums->value == enum_value) { + lua_pushstring(L,enums->name); + break; + } + } + + if (!enums || !enums->name) { + /* Enum preference has an unknown value. */ + lua_pushstring(L,""); + } + break; + } + case PREF_STRING: + case PREF_SAVE_FILENAME: + case PREF_OPEN_FILENAME: + case PREF_DIRNAME: + { + const gchar *string_value = prefs_get_string_value(pref, pref_current); + lua_pushstring(L,string_value); + break; + } + case PREF_RANGE: + { + char *range_value = range_convert_range(NULL, prefs_get_range_value_real(pref, pref_current)); + lua_pushstring(L,range_value); + wmem_free(NULL, range_value); + break; + } + default: + /* Get not supported for this type. */ + return luaL_error(L, "preference type %d is not supported.", prefs_get_type(pref)); + } + } else { + /* No such preference. */ + lua_pushnil(L); + } + + WSLUA_RETURN(1); /* The preference value, or nil if not found. */ +} + +WSLUA_FUNCTION wslua_set_preference(lua_State *L) { + /* Set a preference value. @since 3.5.0 */ +#define WSLUA_ARG_set_preference_PREFERENCE 1 /* The name of the preference. */ +#define WSLUA_ARG_set_preference_VALUE 2 /* The preference value to set. */ + const gchar* preference = luaL_checkstring(L,WSLUA_ARG_set_preference_PREFERENCE); + + /* Split preference from module.preference */ + gchar *module_name = g_strdup(preference); + gchar *preference_name = strchr(module_name, '.'); + module_t *module = NULL; + pref_t *pref = NULL; + + if (preference_name) { + *preference_name = '\0'; + preference_name++; + + module = prefs_find_module(module_name); + pref = prefs_find_preference(module, preference_name); + } + g_free (module_name); + + if (pref) { + unsigned int changed = 0; + switch (prefs_get_type(pref)) { + case PREF_UINT: + { + guint uint_value = (guint)luaL_checkinteger(L,WSLUA_ARG_set_preference_VALUE); + changed = prefs_set_uint_value(pref, uint_value, pref_current); + module->prefs_changed_flags |= changed; + lua_pushboolean(L, changed); + break; + } + case PREF_BOOL: + { + gboolean bool_value = wslua_checkboolean(L, WSLUA_ARG_set_preference_VALUE); + changed = prefs_set_bool_value(pref, bool_value, pref_current); + module->prefs_changed_flags |= changed; + lua_pushboolean(L, changed); + break; + } + case PREF_ENUM: + { + const gchar *enum_value = luaL_checkstring(L,WSLUA_ARG_set_preference_VALUE); + changed = prefs_set_enum_string_value(pref, enum_value, pref_current); + module->prefs_changed_flags |= changed; + lua_pushboolean(L, changed); + break; + } + case PREF_STRING: + case PREF_SAVE_FILENAME: + case PREF_OPEN_FILENAME: + case PREF_DIRNAME: + { + const gchar *string_value = luaL_checkstring(L,WSLUA_ARG_set_preference_VALUE); + changed = prefs_set_string_value(pref, string_value, pref_current); + module->prefs_changed_flags |= changed; + lua_pushboolean(L, changed); + break; + } + case PREF_RANGE: + { + const gchar *range_value = luaL_checkstring(L,WSLUA_ARG_set_preference_VALUE); + range_t *range = NULL; + convert_ret_t ret = range_convert_str(NULL, &range, range_value, prefs_get_max_value(pref)); + if (ret == CVT_NUMBER_TOO_BIG) { + return luaL_error(L, "illegal range (number too big)"); + } else if (ret != CVT_NO_ERROR) { + return luaL_error(L, "illegal range (syntax error)"); + } + changed = prefs_set_range_value(pref, range, pref_current); + wmem_free(NULL, range); + module->prefs_changed_flags |= changed; + lua_pushboolean(L, changed); + break; + } + default: + /* Set not supported for this type. */ + return luaL_error(L, "preference type %d is not supported.", prefs_get_type(pref)); + } + } else { + /* No such preference. */ + lua_pushnil(L); + } + + WSLUA_RETURN(1); /* true if changed, false if unchanged or nil if not found. */ +} + +WSLUA_FUNCTION wslua_reset_preference(lua_State *L) { + /* Reset a preference to default value. @since 3.5.0 */ +#define WSLUA_ARG_reset_preference_PREFERENCE 1 /* The name of the preference. */ + const gchar* preference = luaL_checkstring(L,WSLUA_ARG_reset_preference_PREFERENCE); + + // Split preference from module.preference + gchar *module_name = g_strdup(preference); + gchar *preference_name = strchr(module_name, '.'); + pref_t *pref = NULL; + + if (preference_name) { + *preference_name = '\0'; + preference_name++; + + module_t *module = prefs_find_module(module_name); + pref = prefs_find_preference(module, preference_name); + } + + if (pref) { + reset_pref(pref); + lua_pushboolean(L, TRUE); + } else { + /* No such preference. */ + lua_pushnil(L); + } + + g_free(module_name); + WSLUA_RETURN(1); /* true if valid preference */ +} + +WSLUA_FUNCTION wslua_apply_preferences(lua_State *L) { + /* Write preferences to file and apply changes. @since 3.5.0 */ + char *pf_path = NULL; + int err = write_prefs(&pf_path); + + if (err) { + /* Make a copy of pf_path because luaL_error() will return */ + gchar pf_path_copy[256]; + (void) g_strlcpy(pf_path_copy, pf_path, sizeof pf_path_copy); + g_free(pf_path); + + return luaL_error(L, "can't open preferences file\n\"%s\": %s.", + pf_path_copy, g_strerror(err)); + } else { + prefs_apply_all(); + } + + return 0; +} + +WSLUA_FUNCTION wslua_report_failure(lua_State* LS) { /* Reports a failure to the user. */ +#define WSLUA_ARG_report_failure_TEXT 1 /* Message text to report. */ + const gchar* s = luaL_checkstring(LS,WSLUA_ARG_report_failure_TEXT); + report_failure("%s",s); + return 0; +} + +/* The returned filename is g_malloc()'d so the caller must free it */ +/* except when NULL is returned if file doesn't exist */ +char* wslua_get_actual_filename(const char* fname) { + char fname_clean[256]; + char* f; + char* filename; + + (void) g_strlcpy(fname_clean,fname,255); + fname_clean[255] = '\0'; + + for(f = fname_clean; *f; f++) { + switch(*f) { + case '/': case '\\': + *f = *(G_DIR_SEPARATOR_S); + break; + default: + break; + } + } + + if ( file_exists(fname_clean) ) { + return g_strdup(fname_clean); + } + + filename = get_persconffile_path(fname_clean,FALSE); + + if ( file_exists(filename) ) { + return filename; + } + g_free(filename); + + /* + * Try to look in global data directory, nothing extraordinary for normal + * installations. For executions from the build dir, it will look for files + * copied to DATAFILE_DIR. + */ + filename = get_datafile_path(fname_clean); + if ( file_exists(filename) ) { + return filename; + } + g_free(filename); + + return NULL; +} + +WSLUA_FUNCTION wslua_loadfile(lua_State* L) { + /* + Loads a Lua file and compiles it into a Lua chunk, similar to the standard + https://www.lua.org/manual/5.1/manual.html#pdf-loadfile[loadfile] + but searches additional directories. + The search order is the current directory, followed by the user's + https://www.wireshark.org/docs/wsug_html_chunked/ChAppFilesConfigurationSection.html[personal configuration] + directory, and finally the + https://www.wireshark.org/docs/wsug_html_chunked/ChAppFilesConfigurationSection.html[global configuration] + directory. + + ===== Example + + [source,lua] + ---- + -- Assume foo.lua contains definition for foo(a,b). Load the chunk + -- from the file and execute it to add foo(a,b) to the global table. + -- These two lines are effectively the same as dofile('foo.lua'). + local loaded_chunk = assert(loadfile('foo.lua')) + loaded_chunk() + + -- ok to call foo at this point + foo(1,2) + ---- + */ +#define WSLUA_ARG_loadfile_FILENAME 1 /* Name of the file to be loaded. If the file does not exist in the current directory, the user and system directories are searched. */ + const char *given_fname = luaL_checkstring(L, WSLUA_ARG_loadfile_FILENAME); + char* filename; + + filename = wslua_get_actual_filename(given_fname); + + if (!filename) { + WSLUA_ARG_ERROR(loadfile,FILENAME,"file does not exist"); + return 0; + } + + if (luaL_loadfile(L, filename) == 0) { + g_free(filename); + return 1; + } else { + g_free(filename); + lua_pushnil(L); + lua_insert(L, -2); + return 2; + } +} + +WSLUA_FUNCTION wslua_dofile(lua_State* L) { + /* + Loads a Lua file and executes it as a Lua chunk, similar to the standard + https://www.lua.org/manual/5.1/manual.html#pdf-dofile[dofile] + but searches additional directories. + The search order is the current directory, followed by the user's + https://www.wireshark.org/docs/wsug_html_chunked/ChAppFilesConfigurationSection.html[personal configuration] + directory, and finally the + https://www.wireshark.org/docs/wsug_html_chunked/ChAppFilesConfigurationSection.html[global configuration] + directory. + */ +#define WSLUA_ARG_dofile_FILENAME 1 /* Name of the file to be run. If the file does not exist in the current directory, the user and system directories are searched. */ + const char *given_fname = luaL_checkstring(L, WSLUA_ARG_dofile_FILENAME); + char* filename = wslua_get_actual_filename(given_fname); + int n; + + if (!filename) { + WSLUA_ARG_ERROR(dofile,FILENAME,"file does not exist"); + return 0; + } + + n = lua_gettop(L); + if (luaL_loadfile(L, filename) != 0) lua_error(L); + g_free(filename); + lua_call(L, 0, LUA_MULTRET); + return lua_gettop(L) - n; +} + + +typedef struct _statcmd_t { + lua_State* L; + int func_ref; +} statcmd_t; + +static int statcmd_init_cb_error_handler(lua_State* L _U_) { + return 0; +} + +static void statcmd_init(const char *opt_arg, void* userdata) { + statcmd_t* sc = (statcmd_t *)userdata; + lua_State* L = sc->L; + + lua_settop(L,0); + lua_pushcfunction(L,statcmd_init_cb_error_handler); + lua_rawgeti(L, LUA_REGISTRYINDEX, sc->func_ref); + + lua_pushstring(L,opt_arg); + + switch ( lua_pcall(L,1,0,1) ) { + case 0: + break; + case LUA_ERRRUN: + ws_warning("Runtime error while calling statcmd callback"); + break; + case LUA_ERRMEM: + ws_warning("Memory alloc error while calling statcmd callback"); + break; + case LUA_ERRERR: + ws_warning("Error while running the error handler function for statcmd callback"); + break; + default: + ws_assert_not_reached(); + break; + } + +} + +WSLUA_FUNCTION wslua_register_stat_cmd_arg(lua_State* L) { + /* Register a function to handle a `-z` option */ +#define WSLUA_ARG_register_stat_cmd_arg_ARGUMENT 1 /* The name of the option argument. */ +#define WSLUA_OPTARG_register_stat_cmd_arg_ACTION 2 /* The function to be called when the command is invoked. */ + const char* arg = luaL_checkstring(L,WSLUA_ARG_register_stat_cmd_arg_ARGUMENT); + statcmd_t* sc = g_new0(statcmd_t, 1); /* XXX leaked */ + stat_tap_ui ui_info; + + sc->L = L; + lua_pushvalue(L, WSLUA_OPTARG_register_stat_cmd_arg_ACTION); + sc->func_ref = luaL_ref(L, LUA_REGISTRYINDEX); + lua_remove(L,1); + + ui_info.group = REGISTER_PACKET_STAT_GROUP_UNSORTED; /* XXX - need an argument? */ + ui_info.title = NULL; + ui_info.cli_string = arg; + ui_info.tap_init_cb = statcmd_init; + ui_info.nparams = 0; + ui_info.params = NULL; + register_stat_tap_ui(&ui_info, sc); + return 0; +} + +/* + * Editor modelines - https://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 4 + * tab-width: 8 + * indent-tabs-mode: nil + * End: + * + * vi: set shiftwidth=4 tabstop=8 expandtab: + * :indentSize=4:tabSize=8:noTabs=true: + */ |