wireshark/epan/wslua/wslua_util.c

545 lines
18 KiB
C

/*
* wslua_util.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 = (guint32)(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 = (guint32)(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_Prefs_get_PREFERENCE 1 /* The name of the preference. */
const gchar* preference = luaL_checkstring(L,WSLUA_ARG_Prefs_get_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) {
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);
}
g_free (module_name);
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_Prefs_set_PREFERENCE 1 /* The name of the preference. */
#define WSLUA_ARG_Prefs_set_VALUE 2 /* The preference value to set. */
const gchar* preference = luaL_checkstring(L,WSLUA_ARG_Prefs_get_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);
}
if (pref) {
unsigned int changed = 0;
switch (prefs_get_type(pref)) {
case PREF_UINT:
{
guint uint_value = (guint)luaL_checkinteger(L,WSLUA_ARG_Prefs_set_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_Prefs_set_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_Prefs_set_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_Prefs_set_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_Prefs_set_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);
}
g_free(module_name);
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_Prefs_set_PREFERENCE 1 /* The name of the preference. */
const gchar* preference = luaL_checkstring(L,WSLUA_ARG_Prefs_get_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];
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;
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:
g_warning("Runtime error while calling statcmd callback");
break;
case LUA_ERRMEM:
g_warning("Memory alloc error while calling statcmd callback");
break;
case LUA_ERRERR:
g_warning("Error while running the error handler function for statcmd callback");
break;
default:
g_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_STAT_GROUP_UNSORTED; /* XXX - need group for CLI-only? */
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:
*/