From: Vsevolod Stakhov Date: Thu, 17 Jul 2025 08:39:54 +0000 (+0100) Subject: [Project] Fix Lua API and some constexpr compatibility X-Git-Tag: 3.13.0~43^2~3 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=e6b70c089a96089ad4661a73f36c32e38aed3eb9;p=thirdparty%2Frspamd.git [Project] Fix Lua API and some constexpr compatibility --- diff --git a/src/libserver/html/html.cxx b/src/libserver/html/html.cxx index 374fb349c9..fff2692da6 100644 --- a/src/libserver/html/html.cxx +++ b/src/libserver/html/html.cxx @@ -845,6 +845,28 @@ auto html_tag::find_component_by_name(std::string_view attr_name) const -> std:: return find_unknown_component(attr_name); } +auto html_tag::get_all_attributes() const -> std::vector> +{ + std::vector> result; + + // First, get all known attributes using the component_extractors map + for (const auto &[attr_name, extractor_func]: component_extractors) { + if (auto value = extractor_func(this)) { + // Convert frozen::string to std::string_view for the key + std::string_view name_view{attr_name.data(), attr_name.size()}; + result.emplace_back(name_view, value.value()); + } + } + + // Then add all unknown attributes + auto unknown_attrs = get_unknown_components(); + for (const auto &[name, value]: unknown_attrs) { + result.emplace_back(name, value); + } + + return result; +} + enum tag_parser_state { parse_start = 0, parse_name, diff --git a/src/libserver/html/html_tag.hxx b/src/libserver/html/html_tag.hxx index 5948b91bf0..6d41f13376 100644 --- a/src/libserver/html/html_tag.hxx +++ b/src/libserver/html/html_tag.hxx @@ -268,7 +268,7 @@ struct html_component_width : html_component_base { { return raw_value; } - constexpr std::optional get_numeric_value() const + std::optional get_numeric_value() const { return numeric_value; } @@ -291,7 +291,7 @@ struct html_component_height : html_component_base { { return raw_value; } - constexpr std::optional get_numeric_value() const + std::optional get_numeric_value() const { return numeric_value; } @@ -314,7 +314,7 @@ struct html_component_size : html_component_base { { return raw_value; } - constexpr std::optional get_numeric_value() const + std::optional get_numeric_value() const { return numeric_value; } @@ -386,7 +386,7 @@ struct html_component_font_size : html_component_base { { return raw_value; } - constexpr std::optional get_numeric_value() const + std::optional get_numeric_value() const { return numeric_value; } @@ -1299,7 +1299,7 @@ struct html_tag { // Template method to find component by type template - constexpr auto find_component() const -> std::optional + auto find_component() const -> std::optional { for (const auto &comp: components) { if (std::holds_alternative(comp)) { @@ -1310,7 +1310,7 @@ struct html_tag { } // Helper methods for common component access - constexpr auto find_href() const -> std::optional + auto find_href() const -> std::optional { if (auto comp = find_component()) { return comp.value()->value; @@ -1318,7 +1318,7 @@ struct html_tag { return std::nullopt; } - constexpr auto find_class() const -> std::optional + auto find_class() const -> std::optional { if (auto comp = find_component()) { return comp.value()->value; @@ -1326,7 +1326,7 @@ struct html_tag { return std::nullopt; } - constexpr auto find_id() const -> std::optional + auto find_id() const -> std::optional { if (auto comp = find_component()) { return comp.value()->value; @@ -1334,7 +1334,7 @@ struct html_tag { return std::nullopt; } - constexpr auto find_width() const -> std::optional + auto find_width() const -> std::optional { if (auto comp = find_component()) { return comp.value()->get_numeric_value(); @@ -1342,7 +1342,7 @@ struct html_tag { return std::nullopt; } - constexpr auto find_height() const -> std::optional + auto find_height() const -> std::optional { if (auto comp = find_component()) { return comp.value()->get_numeric_value(); @@ -1350,7 +1350,7 @@ struct html_tag { return std::nullopt; } - constexpr auto find_style() const -> std::optional + auto find_style() const -> std::optional { if (auto comp = find_component()) { return comp.value()->value; @@ -1358,7 +1358,7 @@ struct html_tag { return std::nullopt; } - constexpr auto find_alt() const -> std::optional + auto find_alt() const -> std::optional { if (auto comp = find_component()) { return comp.value()->value; @@ -1366,7 +1366,7 @@ struct html_tag { return std::nullopt; } - constexpr auto find_rel() const -> std::optional + auto find_rel() const -> std::optional { if (auto comp = find_component()) { return comp.value()->value; @@ -1374,12 +1374,12 @@ struct html_tag { return std::nullopt; } - constexpr auto is_hidden() const -> bool + auto is_hidden() const -> bool { return find_component().has_value(); } - constexpr auto find_unknown_component(std::string_view attr_name) const -> std::optional + auto find_unknown_component(std::string_view attr_name) const -> std::optional { for (const auto &comp: components) { if (std::holds_alternative(comp)) { @@ -1392,7 +1392,7 @@ struct html_tag { return std::nullopt; } - constexpr auto get_unknown_components() const -> std::vector> + auto get_unknown_components() const -> std::vector> { std::vector> unknown_attrs; for (const auto &comp: components) { @@ -1416,6 +1416,9 @@ struct html_tag { // Find any component by attribute name auto find_component_by_name(std::string_view attr_name) const -> std::optional; + // Get all attributes as name-value pairs + auto get_all_attributes() const -> std::vector>; + auto clear(void) -> void { id = Tag_UNKNOWN; @@ -1428,7 +1431,7 @@ struct html_tag { closing.clear(); } - constexpr auto get_content_length() const -> std::size_t + auto get_content_length() const -> std::size_t { if (flags & (FL_IGNORE | CM_HEAD)) { return 0; diff --git a/src/lua/lua_html.cxx b/src/lua/lua_html.cxx index a03247c8ad..9b0deed45c 100644 --- a/src/lua/lua_html.cxx +++ b/src/lua/lua_html.cxx @@ -179,6 +179,44 @@ LUA_FUNCTION_DEF(html_tag, get_style); */ LUA_FUNCTION_DEF(html_tag, get_attribute); +/*** + * @method html_tag:get_all_attributes() + * Returns table of all attributes for the element + * @return {table} table with attribute names as keys and values as strings + */ +LUA_FUNCTION_DEF(html_tag, get_all_attributes); + +/*** + * @method html_tag:get_unknown_attributes() + * Returns table of unknown/unrecognized attributes for the element + * @return {table} table with unknown attribute names as keys and values as strings + */ +LUA_FUNCTION_DEF(html_tag, get_unknown_attributes); + +/*** + * @method html_tag:get_children() + * Returns array of child tags for the element + * @return {table} array of child html_tag objects + */ +LUA_FUNCTION_DEF(html_tag, get_children); + +/*** + * @method html_tag:has_attribute(name) + * Checks if element has the specified attribute + * @param {string} name attribute name to check + * @return {boolean} true if attribute exists + */ +LUA_FUNCTION_DEF(html_tag, has_attribute); + +/*** + * @method html_tag:get_numeric_attribute(name) + * Returns numeric value of attribute (if supported and parseable) + * Works for attributes like width, height, font-size, etc. + * @param {string} name attribute name + * @return {number|nil} numeric value or nil if not numeric/parseable + */ +LUA_FUNCTION_DEF(html_tag, get_numeric_attribute); + static const struct luaL_reg taglib_m[] = { LUA_INTERFACE_DEF(html_tag, get_type), LUA_INTERFACE_DEF(html_tag, get_extra), @@ -188,6 +226,11 @@ static const struct luaL_reg taglib_m[] = { LUA_INTERFACE_DEF(html_tag, get_content_length), LUA_INTERFACE_DEF(html_tag, get_style), LUA_INTERFACE_DEF(html_tag, get_attribute), + LUA_INTERFACE_DEF(html_tag, get_all_attributes), + LUA_INTERFACE_DEF(html_tag, get_unknown_attributes), + LUA_INTERFACE_DEF(html_tag, get_children), + LUA_INTERFACE_DEF(html_tag, has_attribute), + LUA_INTERFACE_DEF(html_tag, get_numeric_attribute), {"__tostring", rspamd_lua_class_tostring}, {NULL, NULL}}; @@ -703,6 +746,29 @@ lua_html_tag_get_style(lua_State *L) return 1; } +static int +lua_html_tag_get_all_attributes(lua_State *L) +{ + LUA_TRACE_POINT; + struct lua_html_tag *ltag = lua_check_html_tag(L, 1); + + if (ltag) { + auto all_attrs = ltag->tag->get_all_attributes(); + lua_createtable(L, 0, all_attrs.size()); + + for (const auto &[name, value]: all_attrs) { + lua_pushlstring(L, name.data(), name.size()); + lua_pushlstring(L, value.data(), value.size()); + lua_settable(L, -3); + } + } + else { + return luaL_error(L, "invalid arguments"); + } + + return 1; +} + static int lua_html_tag_get_attribute(lua_State *L) { @@ -728,6 +794,206 @@ lua_html_tag_get_attribute(lua_State *L) return 1; } +static int +lua_html_tag_get_unknown_attributes(lua_State *L) +{ + LUA_TRACE_POINT; + struct lua_html_tag *ltag = lua_check_html_tag(L, 1); + + if (ltag) { + auto unknown_attrs = ltag->tag->get_unknown_components(); + lua_createtable(L, 0, unknown_attrs.size()); + + for (const auto &[name, value]: unknown_attrs) { + lua_pushlstring(L, name.data(), name.size()); + lua_pushlstring(L, value.data(), value.size()); + lua_settable(L, -3); + } + } + else { + return luaL_error(L, "invalid arguments"); + } + + return 1; +} + +static int +lua_html_tag_get_children(lua_State *L) +{ + LUA_TRACE_POINT; + struct lua_html_tag *ltag = lua_check_html_tag(L, 1); + + if (ltag) { + lua_createtable(L, ltag->tag->children.size(), 0); + + for (int i = 0; i < ltag->tag->children.size(); i++) { + auto *child_tag = static_cast(lua_newuserdata(L, sizeof(lua_html_tag))); + child_tag->tag = ltag->tag->children[i]; + child_tag->html = ltag->html; + rspamd_lua_setclass(L, rspamd_html_tag_classname, -1); + lua_rawseti(L, -2, i + 1); + } + } + else { + return luaL_error(L, "invalid arguments"); + } + + return 1; +} + +static int +lua_html_tag_has_attribute(lua_State *L) +{ + LUA_TRACE_POINT; + struct lua_html_tag *ltag = lua_check_html_tag(L, 1); + gsize slen; + const char *attr_name = luaL_checklstring(L, 2, &slen); + + if (ltag && attr_name) { + auto maybe_attr = ltag->tag->find_component_by_name({attr_name, slen}); + lua_pushboolean(L, maybe_attr.has_value()); + } + else { + return luaL_error(L, "invalid arguments"); + } + + return 1; +} + +static int +lua_html_tag_get_numeric_attribute(lua_State *L) +{ + LUA_TRACE_POINT; + struct lua_html_tag *ltag = lua_check_html_tag(L, 1); + gsize slen; + const char *attr_name = luaL_checklstring(L, 2, &slen); + + if (ltag && attr_name) { + std::string_view name_view{attr_name, slen}; + + // Check for numeric components + if (name_view == "width") { + if (auto comp = ltag->tag->find_component()) { + if (auto numeric_val = comp.value()->get_numeric_value()) { + lua_pushinteger(L, numeric_val.value()); + return 1; + } + } + } + else if (name_view == "height") { + if (auto comp = ltag->tag->find_component()) { + if (auto numeric_val = comp.value()->get_numeric_value()) { + lua_pushinteger(L, numeric_val.value()); + return 1; + } + } + } + else if (name_view == "size") { + if (auto comp = ltag->tag->find_component()) { + if (auto numeric_val = comp.value()->get_numeric_value()) { + lua_pushinteger(L, numeric_val.value()); + return 1; + } + } + } + else if (name_view == "font-size") { + if (auto comp = ltag->tag->find_component()) { + if (auto numeric_val = comp.value()->get_numeric_value()) { + lua_pushinteger(L, numeric_val.value()); + return 1; + } + } + } + else if (name_view == "line-height") { + if (auto comp = ltag->tag->find_component()) { + if (auto numeric_val = comp.value()->get_numeric_value()) { + lua_pushinteger(L, numeric_val.value()); + return 1; + } + } + } + else if (name_view == "border-width") { + if (auto comp = ltag->tag->find_component()) { + if (auto numeric_val = comp.value()->get_numeric_value()) { + lua_pushinteger(L, numeric_val.value()); + return 1; + } + } + } + else if (name_view == "opacity") { + if (auto comp = ltag->tag->find_component()) { + if (auto numeric_val = comp.value()->get_numeric_value()) { + lua_pushnumber(L, numeric_val.value()); + return 1; + } + } + } + else if (name_view == "min-width") { + if (auto comp = ltag->tag->find_component()) { + if (auto numeric_val = comp.value()->get_numeric_value()) { + lua_pushinteger(L, numeric_val.value()); + return 1; + } + } + } + else if (name_view == "max-width") { + if (auto comp = ltag->tag->find_component()) { + if (auto numeric_val = comp.value()->get_numeric_value()) { + lua_pushinteger(L, numeric_val.value()); + return 1; + } + } + } + else if (name_view == "min-height") { + if (auto comp = ltag->tag->find_component()) { + if (auto numeric_val = comp.value()->get_numeric_value()) { + lua_pushinteger(L, numeric_val.value()); + return 1; + } + } + } + else if (name_view == "max-height") { + if (auto comp = ltag->tag->find_component()) { + if (auto numeric_val = comp.value()->get_numeric_value()) { + lua_pushinteger(L, numeric_val.value()); + return 1; + } + } + } + else if (name_view == "cellpadding") { + if (auto comp = ltag->tag->find_component()) { + if (auto numeric_val = comp.value()->get_numeric_value()) { + lua_pushinteger(L, numeric_val.value()); + return 1; + } + } + } + else if (name_view == "cellspacing") { + if (auto comp = ltag->tag->find_component()) { + if (auto numeric_val = comp.value()->get_numeric_value()) { + lua_pushinteger(L, numeric_val.value()); + return 1; + } + } + } + else if (name_view == "tabindex") { + if (auto comp = ltag->tag->find_component()) { + if (auto numeric_val = comp.value()->get_numeric_value()) { + lua_pushinteger(L, numeric_val.value()); + return 1; + } + } + } + + lua_pushnil(L); + } + else { + return luaL_error(L, "invalid arguments"); + } + + return 1; +} + void luaopen_html(lua_State *L) { rspamd_lua_new_class(L, rspamd_html_classname, htmllib_m);