/**
* Call Lua url_rewriter function to get replacement URL
* @param task Rspamd task
- * @param func_name Lua function name (e.g., "url_rewriter")
+ * @param L Lua state
+ * @param func_ref Lua function reference from luaL_ref
* @param url Original URL string
* @return Replacement URL or empty optional if no replacement
*/
-static auto call_lua_url_rewriter(struct rspamd_task *task, const char *func_name, const std::string &url)
+static auto call_lua_url_rewriter(struct rspamd_task *task, ::lua_State *L, int func_ref, const std::string &url)
-> std::optional<std::string>
{
- if (!func_name || !task || !task->cfg) {
- return std::nullopt;
- }
-
- auto *L = RSPAMD_LUA_CFG_STATE(task->cfg);
- if (!L) {
+ if (!L || func_ref == LUA_NOREF || func_ref == LUA_REFNIL || !task) {
return std::nullopt;
}
lua_pushcfunction(L, &rspamd_lua_traceback);
auto err_idx = lua_gettop(L);
- // Get the function
- if (!rspamd_lua_require_function(L, func_name, nullptr)) {
- msg_debug_html_rewrite("cannot require function %s", func_name);
+ // Get the function from registry
+ lua_rawgeti(L, LUA_REGISTRYINDEX, func_ref);
+
+ if (!lua_isfunction(L, -1)) {
+ msg_debug_html_rewrite("func_ref is not a function");
lua_settop(L, err_idx - 1);
return std::nullopt;
}
// Call function with 2 args, 1 result
if (lua_pcall(L, 2, 1, err_idx) != 0) {
- msg_warn_task("call to %s failed: %s", func_name, lua_tostring(L, -1));
+ msg_warn_task("call to url rewriter failed: %s", lua_tostring(L, -1));
lua_settop(L, err_idx - 1);
return std::nullopt;
}
}
}
else if (!lua_isnil(L, -1)) {
- msg_warn_task("%s returned non-string value", func_name);
+ msg_warn_task("url rewriter returned non-string value");
}
lua_settop(L, err_idx - 1);
}
auto process_html_url_rewrite(struct rspamd_task *task,
+ ::lua_State *L,
const html_content *hc,
- const char *func_name,
+ int func_ref,
int part_id,
std::string_view original_html)
-> std::optional<std::string>
{
- if (!task || !hc || !func_name) {
+ if (!task || !hc || !L || func_ref == LUA_NOREF || func_ref == LUA_REFNIL) {
return std::nullopt;
}
for (const auto &candidate: candidates) {
// Call Lua callback
- auto replacement = call_lua_url_rewriter(task, func_name, candidate.absolute_url);
+ auto replacement = call_lua_url_rewriter(task, L, func_ref, candidate.absolute_url);
if (!replacement) {
continue;// Skip if Lua returned nil
}
#include <optional>
struct rspamd_task;
+struct lua_State;
namespace rspamd::html {
* Process HTML URL rewriting for a task
* Enumerates candidates, calls Lua callback, applies patches, and returns rewritten HTML
* @param task Rspamd task
+ * @param L Lua state
* @param hc HTML content
- * @param func_name Lua function name for URL rewriting
+ * @param func_ref Lua function reference from luaL_ref
* @param part_id MIME part ID
* @param original_html Original HTML content (decoded)
* @return Rewritten HTML or nullopt if no changes
*/
auto process_html_url_rewrite(struct rspamd_task *task,
+ ::lua_State *L,
const html_content *hc,
- const char *func_name,
+ int func_ref,
int part_id,
std::string_view original_html)
-> std::optional<std::string>;
extern "C" {
int rspamd_html_url_rewrite(struct rspamd_task *task,
+ struct lua_State *L,
void *html_content,
- const char *func_name,
+ int func_ref,
int part_id,
const char *original_html,
gsize html_len,
char **output_html,
gsize *output_len)
{
- if (!task || !html_content || !func_name || !original_html) {
+ if (!task || !L || !html_content || !original_html) {
return -1;
}
std::string_view original{original_html, html_len};
auto result = rspamd::html::process_html_url_rewrite(
- task, hc, func_name, part_id, original);
+ task, L, hc, func_ref, part_id, original);
if (!result) {
return -1;
struct rspamd_task;
+struct lua_State;
+
/**
* C wrapper for HTML URL rewriting
* @param task Rspamd task
+ * @param L Lua state
* @param html_content HTML content pointer (void* cast of html_content*)
- * @param func_name Lua function name for rewriting
+ * @param func_ref Lua function reference (from luaL_ref)
* @param part_id MIME part ID
* @param original_html Original HTML content
* @param html_len Length of original HTML
* @return 0 on success, -1 on error/no rewrite
*/
int rspamd_html_url_rewrite(struct rspamd_task *task,
+ struct lua_State *L,
void *html_content,
- const char *func_name,
+ int func_ref,
int part_id,
const char *original_html,
gsize html_len,
LUA_FUNCTION_DEF(task, add_timer);
/***
- * @method task:rewrite_html_urls(func_name)
+ * @method task:rewrite_html_urls(callback)
* Rewrites URLs in HTML parts using the specified Lua callback function.
* The callback receives (task, url) and should return the replacement URL or nil.
- * @param {string} func_name name of Lua function to call for each URL
+ * @param {function} callback Lua function to call for each URL
* @return {table|nil} table of rewritten HTML parts indexed by part number, or nil on error
*/
LUA_FUNCTION_DEF(task, rewrite_html_urls);
lua_task_rewrite_html_urls(lua_State *L)
{
struct rspamd_task *task = lua_check_task(L, 1);
- const char *func_name = luaL_checkstring(L, 2);
- if (!func_name) {
- return luaL_error(L, "invalid arguments: function name expected");
+ if (!lua_isfunction(L, 2)) {
+ return luaL_error(L, "invalid arguments: function expected");
}
if (!task || !MESSAGE_FIELD_CHECK(task, text_parts)) {
return 1;
}
+ /* Create function reference */
+ lua_pushvalue(L, 2);
+ int func_ref = luaL_ref(L, LUA_REGISTRYINDEX);
+
/* Create result table */
lua_newtable(L);
int results = 0;
/* Process URL rewriting using C wrapper */
int ret = rspamd_html_url_rewrite(
task,
+ L,
text_part->html,
- func_name,
+ func_ref,
text_part->mime_part->part_number,
(const char *) text_part->parsed.begin,
text_part->parsed.len,
}
}
+ /* Unreference the function */
+ luaL_unref(L, LUA_REGISTRYINDEX, func_ref);
+
if (results == 0) {
lua_pop(L, 1);
lua_pushnil(L);