]> git.ipfire.org Git - thirdparty/rspamd.git/commitdiff
[Project] Fix Lua API and some constexpr compatibility
authorVsevolod Stakhov <vsevolod@rspamd.com>
Thu, 17 Jul 2025 08:39:54 +0000 (09:39 +0100)
committerVsevolod Stakhov <vsevolod@rspamd.com>
Thu, 17 Jul 2025 08:39:54 +0000 (09:39 +0100)
src/libserver/html/html.cxx
src/libserver/html/html_tag.hxx
src/lua/lua_html.cxx

index 374fb349c9141b9beb193d53b3581612aeba66e7..fff2692da6484e5e097a5ad709d695e566582f8d 100644 (file)
@@ -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::pair<std::string_view, std::string_view>>
+{
+       std::vector<std::pair<std::string_view, std::string_view>> 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,
index 5948b91bf0ea134ddb81448a25a8463c1fe83ef3..6d41f13376822fe5e19c1fd6160f989a07373e10 100644 (file)
@@ -268,7 +268,7 @@ struct html_component_width : html_component_base {
        {
                return raw_value;
        }
-       constexpr std::optional<std::uint32_t> get_numeric_value() const
+       std::optional<std::uint32_t> get_numeric_value() const
        {
                return numeric_value;
        }
@@ -291,7 +291,7 @@ struct html_component_height : html_component_base {
        {
                return raw_value;
        }
-       constexpr std::optional<std::uint32_t> get_numeric_value() const
+       std::optional<std::uint32_t> get_numeric_value() const
        {
                return numeric_value;
        }
@@ -314,7 +314,7 @@ struct html_component_size : html_component_base {
        {
                return raw_value;
        }
-       constexpr std::optional<std::uint32_t> get_numeric_value() const
+       std::optional<std::uint32_t> 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<std::uint32_t> get_numeric_value() const
+       std::optional<std::uint32_t> get_numeric_value() const
        {
                return numeric_value;
        }
@@ -1299,7 +1299,7 @@ struct html_tag {
 
        // Template method to find component by type
        template<typename T>
-       constexpr auto find_component() const -> std::optional<const T *>
+       auto find_component() const -> std::optional<const T *>
        {
                for (const auto &comp: components) {
                        if (std::holds_alternative<T>(comp)) {
@@ -1310,7 +1310,7 @@ struct html_tag {
        }
 
        // Helper methods for common component access
-       constexpr auto find_href() const -> std::optional<std::string_view>
+       auto find_href() const -> std::optional<std::string_view>
        {
                if (auto comp = find_component<html_component_href>()) {
                        return comp.value()->value;
@@ -1318,7 +1318,7 @@ struct html_tag {
                return std::nullopt;
        }
 
-       constexpr auto find_class() const -> std::optional<std::string_view>
+       auto find_class() const -> std::optional<std::string_view>
        {
                if (auto comp = find_component<html_component_class>()) {
                        return comp.value()->value;
@@ -1326,7 +1326,7 @@ struct html_tag {
                return std::nullopt;
        }
 
-       constexpr auto find_id() const -> std::optional<std::string_view>
+       auto find_id() const -> std::optional<std::string_view>
        {
                if (auto comp = find_component<html_component_id>()) {
                        return comp.value()->value;
@@ -1334,7 +1334,7 @@ struct html_tag {
                return std::nullopt;
        }
 
-       constexpr auto find_width() const -> std::optional<std::uint32_t>
+       auto find_width() const -> std::optional<std::uint32_t>
        {
                if (auto comp = find_component<html_component_width>()) {
                        return comp.value()->get_numeric_value();
@@ -1342,7 +1342,7 @@ struct html_tag {
                return std::nullopt;
        }
 
-       constexpr auto find_height() const -> std::optional<std::uint32_t>
+       auto find_height() const -> std::optional<std::uint32_t>
        {
                if (auto comp = find_component<html_component_height>()) {
                        return comp.value()->get_numeric_value();
@@ -1350,7 +1350,7 @@ struct html_tag {
                return std::nullopt;
        }
 
-       constexpr auto find_style() const -> std::optional<std::string_view>
+       auto find_style() const -> std::optional<std::string_view>
        {
                if (auto comp = find_component<html_component_style>()) {
                        return comp.value()->value;
@@ -1358,7 +1358,7 @@ struct html_tag {
                return std::nullopt;
        }
 
-       constexpr auto find_alt() const -> std::optional<std::string_view>
+       auto find_alt() const -> std::optional<std::string_view>
        {
                if (auto comp = find_component<html_component_alt>()) {
                        return comp.value()->value;
@@ -1366,7 +1366,7 @@ struct html_tag {
                return std::nullopt;
        }
 
-       constexpr auto find_rel() const -> std::optional<std::string_view>
+       auto find_rel() const -> std::optional<std::string_view>
        {
                if (auto comp = find_component<html_component_rel>()) {
                        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<html_component_hidden>().has_value();
        }
 
-       constexpr auto find_unknown_component(std::string_view attr_name) const -> std::optional<std::string_view>
+       auto find_unknown_component(std::string_view attr_name) const -> std::optional<std::string_view>
        {
                for (const auto &comp: components) {
                        if (std::holds_alternative<html_component_unknown>(comp)) {
@@ -1392,7 +1392,7 @@ struct html_tag {
                return std::nullopt;
        }
 
-       constexpr auto get_unknown_components() const -> std::vector<std::pair<std::string_view, std::string_view>>
+       auto get_unknown_components() const -> std::vector<std::pair<std::string_view, std::string_view>>
        {
                std::vector<std::pair<std::string_view, std::string_view>> 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<std::string_view>;
 
+       // Get all attributes as name-value pairs
+       auto get_all_attributes() const -> std::vector<std::pair<std::string_view, std::string_view>>;
+
        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;
index a03247c8ade121434e7523342606ef332f7ef428..9b0deed45c1b85aba115da4fd0e5ebdfe79122c8 100644 (file)
@@ -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_html_tag *>(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<rspamd::html::html_component_width>()) {
+                               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<rspamd::html::html_component_height>()) {
+                               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<rspamd::html::html_component_size>()) {
+                               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<rspamd::html::html_component_font_size>()) {
+                               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<rspamd::html::html_component_line_height>()) {
+                               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<rspamd::html::html_component_border_width>()) {
+                               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<rspamd::html::html_component_opacity>()) {
+                               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<rspamd::html::html_component_min_width>()) {
+                               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<rspamd::html::html_component_max_width>()) {
+                               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<rspamd::html::html_component_min_height>()) {
+                               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<rspamd::html::html_component_max_height>()) {
+                               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<rspamd::html::html_component_cellpadding>()) {
+                               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<rspamd::html::html_component_cellspacing>()) {
+                               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<rspamd::html::html_component_tabindex>()) {
+                               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);