]> git.ipfire.org Git - thirdparty/rspamd.git/commitdiff
[Feature] Allow to hash any Lua types
authorVsevolod Stakhov <vsevolod@rspamd.com>
Fri, 20 Dec 2024 09:11:10 +0000 (09:11 +0000)
committerVsevolod Stakhov <vsevolod@rspamd.com>
Fri, 20 Dec 2024 09:11:10 +0000 (09:11 +0000)
src/lua/lua_cryptobox.c

index 9600a47327cdbc499dd23b57193f179f51b189e9..b562c47783a9b46931a4d3d49bb58196e009e2ce 100644 (file)
@@ -1362,57 +1362,110 @@ lua_cryptobox_hash_create_specific_keyed(lua_State *L)
        return 1;
 }
 
-/***
- * @method cryptobox_hash:update(data)
- * Updates hash with the specified data (hash should not be finalized using `hex` or `bin` methods)
- * @param {string} data data to hash
- */
-static int
-lua_cryptobox_hash_update(lua_State *L)
+static void
+lua_cryptobox_update_pos(lua_State *L, struct rspamd_lua_cryptobox_hash *h, int pos)
 {
-       LUA_TRACE_POINT;
-       struct rspamd_lua_cryptobox_hash *h = lua_check_cryptobox_hash(L, 1), **ph;
        const char *data;
        struct rspamd_lua_text *t;
        gsize len;
 
-       if (lua_isuserdata(L, 2)) {
-               t = lua_check_text(L, 2);
+       /* Inverse pos if it is relative to the top of the stack */
+       if (pos < 0) {
+               pos = lua_gettop(L) + pos + 1;
+       }
 
-               if (!t) {
-                       return luaL_error(L, "invalid arguments");
+       switch (lua_type(L, pos)) {
+       case LUA_TSTRING:
+               data = lua_tolstring(L, pos, &len);
+               rspamd_lua_hash_update(h, data, len);
+               break;
+
+       case LUA_TNUMBER: {
+               lua_Number n = lua_tonumber(L, pos);
+               if (n == (lua_Number) (lua_Integer) n) {
+                       lua_Integer i = lua_tointeger(L, pos);
+                       rspamd_lua_hash_update(h, (void *) &i, sizeof(i));
                }
+               else {
 
-               data = t->start;
-               len = t->len;
+                       rspamd_lua_hash_update(h, (void *) &n, sizeof(n));
+               }
+
+               break;
        }
-       else {
-               data = luaL_checklstring(L, 2, &len);
+
+       case LUA_TBOOLEAN: {
+               char b = lua_toboolean(L, pos);
+               rspamd_lua_hash_update(h, &b, sizeof(b));
+               break;
        }
 
-       if (lua_isnumber(L, 3)) {
-               gsize nlen = lua_tonumber(L, 3);
+       case LUA_TTABLE: {
 
-               if (nlen > len) {
-                       return luaL_error(L, "invalid length: %d while %d is available",
-                                                         (int) nlen, (int) len);
+               /* Hash array part */
+               gsize alen;
+#if LUA_VERSION_NUM >= 502
+               alen = lua_rawlen(L, 2);
+#else
+               alen = lua_objlen(L, pos);
+#endif
+
+               for (gsize i = 1; i <= alen; i++) {
+                       lua_rawgeti(L, pos, i);
+                       lua_cryptobox_update_pos(L, h, -1); /* Recurse */
+                       lua_pop(L, 1);
                }
 
-               len = nlen;
-       }
+               /* Hash key-value pairs */
+               lua_pushnil(L);
+               while (lua_next(L, pos) != 0) {
+                       /* Hash key */
+                       lua_pushvalue(L, -2);
+                       lua_cryptobox_update_pos(L, h, -1);
+                       lua_pop(L, 1);
 
-       if (h && data) {
-               if (!h->is_finished) {
-                       rspamd_lua_hash_update(h, data, len);
+                       /* Hash value */
+                       lua_cryptobox_update_pos(L, h, -1);
+                       lua_pop(L, 1);
                }
-               else {
-                       return luaL_error(L, "hash is already finalized");
+
+               break;
+       }
+
+       case LUA_TUSERDATA:
+               t = lua_check_text(L, 2);
+               if (t) {
+                       rspamd_lua_hash_update(h, t->start, t->len);
                }
+               break;
+
+       case LUA_TFUNCTION:
+       case LUA_TTHREAD:
+       case LUA_TNIL:
+       default:
+               /* Skip these types */
+               break;
        }
-       else {
-               return luaL_error(L, "invalid arguments");
+}
+
+/***
+ * @method cryptobox_hash:update(data)
+ * Updates hash with the specified data (hash should not be finalized using `hex` or `bin` methods)
+ * @param {string} data data to hash
+ */
+static int
+lua_cryptobox_hash_update(lua_State *L)
+{
+       LUA_TRACE_POINT;
+       struct rspamd_lua_cryptobox_hash *h = lua_check_cryptobox_hash(L, 1), **ph;
+
+
+       if (h == NULL || h->is_finished) {
+               return luaL_error(L, "invalid arguments or hash is already finalized");
        }
 
+       lua_cryptobox_update_pos(L, h, 2);
+
        ph = lua_newuserdata(L, sizeof(void *));
        *ph = h;
        REF_RETAIN(h);
@@ -1420,7 +1473,6 @@ lua_cryptobox_hash_update(lua_State *L)
 
        return 1;
 }
-
 /***
  * @method cryptobox_hash:reset()
  * Resets hash to the initial state