Files
wireplumber/modules/module-lua-scripting/wplua/value.c
Pauli Virtanen b65f8884a5 m-lua-scripting: check argument type is table to avoid crashing
In conf_apply_rules, check input argument is a table before assuming it
is and potentially crashing.

Add check also in wplua_table_to_properties to avoid similar bugs.
2023-10-13 22:47:57 +03:00

353 lines
10 KiB
C

/* WirePlumber
*
* Copyright © 2020 Collabora Ltd.
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
*
* SPDX-License-Identifier: MIT
*/
#include "wplua.h"
#include "private.h"
#include <wp/wp.h>
WpProperties *
wplua_table_to_properties (lua_State *L, int idx)
{
WpProperties *p = wp_properties_new_empty ();
const gchar *key, *value;
int table = lua_absindex (L, idx);
if (lua_type (L, table) != LUA_TTABLE) {
wp_critical ("skipping non-table value");
return p;
}
lua_pushnil(L);
while (lua_next (L, table) != 0) {
/* copy key & value to convert them to string */
key = luaL_tolstring (L, -2, NULL);
value = luaL_tolstring (L, -2, NULL);
wp_properties_set (p, key, value);
lua_pop (L, 3);
}
/* sort, because the lua table has a random order and it's too messy to read */
wp_properties_sort (p);
return p;
}
void
wplua_properties_to_table (lua_State *L, WpProperties *p)
{
lua_newtable (L);
if (p) {
g_autoptr (WpIterator) it = wp_properties_new_iterator (p);
GValue v = G_VALUE_INIT;
const gchar *key, *value;
while (wp_iterator_next (it, &v)) {
WpPropertiesItem *pi = g_value_get_boxed (&v);
key = wp_properties_item_get_key (pi);
value = wp_properties_item_get_value (pi);
lua_pushstring (L, key);
lua_pushstring (L, value);
lua_settable (L, -3);
g_value_unset (&v);
}
}
}
GVariant *
wplua_lua_to_gvariant (lua_State *L, int idx)
{
switch (lua_type (L, idx)) {
case LUA_TNIL:
return g_variant_new ("()");
case LUA_TBOOLEAN:
return g_variant_new_boolean (lua_toboolean (L, idx));
case LUA_TNUMBER:
if (lua_isinteger (L, idx))
return g_variant_new_int64 (lua_tointeger (L, idx));
else
return g_variant_new_double (lua_tonumber (L, idx));
case LUA_TSTRING:
return g_variant_new_string (lua_tostring (L, idx));
case LUA_TTABLE: {
GVariantBuilder b = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE_VARDICT);
const gchar *key;
int table = lua_absindex (L, idx);
lua_pushnil (L);
while (lua_next (L, table) != 0) {
/* copy key to convert it to string */
lua_pushvalue (L, -2);
key = lua_tostring (L, -1);
g_variant_builder_add (&b, "{sv}", key, wplua_lua_to_gvariant (L, -2));
lua_pop (L, 2);
}
return g_variant_builder_end (&b);
}
default:
wp_warning ("skipping bad value (its type cannot be represented in GVariant)");
return NULL;
}
}
void
wplua_gvariant_to_lua (lua_State *L, GVariant *variant)
{
if (variant == NULL || g_variant_is_of_type (variant, G_VARIANT_TYPE_UNIT)) {
lua_pushnil (L);
}
else if (g_variant_is_of_type (variant, G_VARIANT_TYPE_INT16)) {
lua_pushinteger (L, g_variant_get_int16 (variant));
}
else if (g_variant_is_of_type (variant, G_VARIANT_TYPE_INT32)) {
lua_pushinteger (L, g_variant_get_int32 (variant));
}
else if (g_variant_is_of_type (variant, G_VARIANT_TYPE_INT64)) {
lua_pushinteger (L, g_variant_get_int64 (variant));
}
else if (g_variant_is_of_type (variant, G_VARIANT_TYPE_UINT16)) {
lua_pushinteger (L, g_variant_get_uint16 (variant));
}
else if (g_variant_is_of_type (variant, G_VARIANT_TYPE_UINT32)) {
lua_pushinteger (L, g_variant_get_uint32 (variant));
}
else if (g_variant_is_of_type (variant, G_VARIANT_TYPE_UINT64)) {
lua_pushinteger (L, g_variant_get_uint64 (variant));
}
else if (g_variant_is_of_type (variant, G_VARIANT_TYPE_DOUBLE)) {
lua_pushnumber (L, g_variant_get_double (variant));
}
else if (g_variant_is_of_type (variant, G_VARIANT_TYPE_BOOLEAN)) {
lua_pushboolean (L, g_variant_get_boolean (variant));
}
else if (g_variant_is_of_type (variant, G_VARIANT_TYPE_STRING)) {
lua_pushstring (L, g_variant_get_string (variant, NULL));
}
else if (g_variant_is_of_type (variant, G_VARIANT_TYPE_VARIANT)) {
g_autoptr (GVariant) v = g_variant_get_variant (variant);
wplua_gvariant_to_lua (L, v);
}
else if (g_variant_is_of_type (variant, G_VARIANT_TYPE_DICTIONARY)) {
gsize n_children, i;
n_children = g_variant_n_children (variant);
lua_createtable (L, 0, n_children);
for (i = 0; i < n_children; i++) {
g_autoptr (GVariant) key, value;
g_variant_get_child (variant, i, "{@?@*}", &key, &value);
wplua_gvariant_to_lua (L, key);
/* if the key is a string convertible to integer, convert it */
if (lua_type (L, -1) == LUA_TSTRING) {
int isnum = 0;
lua_Integer num = lua_tointegerx (L, -1, &isnum);
if (isnum) {
lua_pop (L, 1);
lua_pushinteger (L, num);
}
}
wplua_gvariant_to_lua (L, value);
lua_settable (L, -3);
}
}
else if (g_variant_is_of_type (variant, G_VARIANT_TYPE_ARRAY)) {
gsize n_children, i;
n_children = g_variant_n_children (variant);
lua_createtable (L, n_children, 0);
for (i = 0; i < n_children; i++) {
g_autoptr (GVariant) value;
value = g_variant_get_child_value (variant, i);
wplua_gvariant_to_lua (L, value);
lua_seti (L, -2, i + 1);
}
}
else {
g_autofree gchar *type_name =
g_variant_type_dup_string (g_variant_get_type (variant));
wp_warning ("Unhandled GVariant type %s", type_name);
lua_pushnil (L);
}
}
gint
wplua_lua_to_enum (lua_State *L, int idx, GType enum_type)
{
if (lua_type (L, idx) == LUA_TSTRING) {
g_autoptr (GEnumClass) klass = g_type_class_ref (enum_type);
GEnumValue *value = g_enum_get_value_by_nick (klass, lua_tostring (L, idx));
if (value)
return value->value;
else
luaL_error (L, "Invalid enum value '%s'", lua_tostring (L, idx));
}
return lua_tointeger (L, idx);
}
void
wplua_enum_to_lua (lua_State *L, gint enum_val, GType enum_type)
{
g_autoptr (GEnumClass) klass = g_type_class_ref (enum_type);
GEnumValue *value = g_enum_get_value (klass, enum_val);
if (value)
lua_pushstring (L, value->value_nick);
else
lua_pushinteger (L, enum_val);
}
void
wplua_lua_to_gvalue (lua_State *L, int idx, GValue *v)
{
switch (g_type_fundamental (G_VALUE_TYPE (v))) {
case G_TYPE_CHAR:
if (lua_type (L, idx) == LUA_TSTRING)
g_value_set_schar (v, *lua_tostring (L, idx));
else
g_value_set_schar (v, lua_tointeger (L, idx));
break;
case G_TYPE_UCHAR:
g_value_set_uchar (v, lua_tointeger (L, idx));
break;
case G_TYPE_INT:
g_value_set_int (v, lua_tointeger (L, idx));
break;
case G_TYPE_UINT:
g_value_set_uint (v, lua_tointeger (L, idx));
break;
case G_TYPE_LONG:
g_value_set_long (v, lua_tointeger (L, idx));
break;
case G_TYPE_ULONG:
g_value_set_ulong (v, lua_tointeger (L, idx));
break;
case G_TYPE_INT64:
g_value_set_int64 (v, lua_tointeger (L, idx));
break;
case G_TYPE_UINT64:
g_value_set_uint64 (v, lua_tonumber (L, idx));
break;
case G_TYPE_FLOAT:
g_value_set_float (v, lua_tonumber (L, idx));
break;
case G_TYPE_DOUBLE:
g_value_set_double (v, lua_tonumber (L, idx));
break;
case G_TYPE_BOOLEAN:
g_value_set_boolean (v, lua_toboolean (L, idx));
break;
case G_TYPE_STRING:
g_value_set_string (v, lua_tostring (L, idx));
break;
case G_TYPE_POINTER:
if (lua_type (L, idx) == LUA_TLIGHTUSERDATA)
g_value_set_pointer (v, lua_touserdata (L, idx));
break;
case G_TYPE_BOXED:
if (_wplua_isgvalue_userdata (L, idx, G_VALUE_TYPE (v)))
g_value_set_boxed (v, wplua_toboxed (L, idx));
/* table -> WpProperties */
else if (lua_istable (L, idx) && G_VALUE_TYPE (v) == WP_TYPE_PROPERTIES)
g_value_take_boxed (v, wplua_table_to_properties (L, idx));
break;
case G_TYPE_OBJECT:
case G_TYPE_INTERFACE:
if (_wplua_isgvalue_userdata (L, idx, G_VALUE_TYPE (v)))
g_value_set_object (v, wplua_toobject (L, idx));
break;
case G_TYPE_ENUM:
g_value_set_enum (v, wplua_lua_to_enum (L, idx, G_VALUE_TYPE (v)));
break;
case G_TYPE_FLAGS:
g_value_set_flags (v, lua_tointeger (L, idx));
break;
case G_TYPE_VARIANT:
g_value_set_variant (v, wplua_lua_to_gvariant (L, idx));
break;
default:
break;
}
}
int
wplua_gvalue_to_lua (lua_State *L, const GValue *v)
{
switch (g_type_fundamental (G_VALUE_TYPE (v))) {
case G_TYPE_CHAR:
lua_pushinteger (L, g_value_get_schar (v));
break;
case G_TYPE_UCHAR:
lua_pushinteger (L, g_value_get_uchar (v));
break;
case G_TYPE_INT:
lua_pushinteger (L, g_value_get_int (v));
break;
case G_TYPE_UINT:
lua_pushinteger (L, g_value_get_uint (v));
break;
case G_TYPE_LONG:
lua_pushinteger (L, g_value_get_long (v));
break;
case G_TYPE_ULONG:
lua_pushinteger (L, g_value_get_ulong (v));
break;
case G_TYPE_INT64:
lua_pushinteger (L, g_value_get_int64 (v));
break;
case G_TYPE_UINT64:
lua_pushnumber (L, g_value_get_uint64 (v));
break;
case G_TYPE_FLOAT:
lua_pushnumber (L, g_value_get_float (v));
break;
case G_TYPE_DOUBLE:
lua_pushnumber (L, g_value_get_double (v));
break;
case G_TYPE_BOOLEAN:
lua_pushboolean (L, g_value_get_boolean (v));
break;
case G_TYPE_STRING:
lua_pushstring (L, g_value_get_string (v));
break;
case G_TYPE_POINTER:
lua_pushlightuserdata (L, g_value_get_pointer (v));
break;
case G_TYPE_BOXED:
if (G_VALUE_TYPE (v) == WP_TYPE_PROPERTIES)
wplua_properties_to_table (L, g_value_get_boxed (v));
else
wplua_pushboxed (L, G_VALUE_TYPE (v), g_value_dup_boxed (v));
break;
case G_TYPE_OBJECT:
case G_TYPE_INTERFACE: {
GObject *object = g_value_dup_object (v);
if (object)
wplua_pushobject (L, object);
else
lua_pushnil (L);
break;
}
case G_TYPE_ENUM:
wplua_enum_to_lua (L, g_value_get_enum (v), G_VALUE_TYPE (v));
break;
case G_TYPE_FLAGS:
/* FIXME: push as userdata with methods */
lua_pushinteger (L, g_value_get_flags (v));
break;
case G_TYPE_PARAM: {
GParamSpec *pspec = g_value_get_param (v);
lua_pushstring (L, pspec->name);
break;
}
case G_TYPE_VARIANT: {
GVariant *variant = g_value_get_variant (v);
wplua_gvariant_to_lua (L, variant);
break;
}
default:
lua_pushnil (L);
break;
}
return 1;
}