return lua_archive_pack(L);
}
-/**
- * zip_encrypt(files, password) -> text
+/***
+ * @function archive.zip_encrypt(files[, password])
+ * Create a ZIP archive in-memory using Rspamd ZIP writer.
+ * If password is provided and non-empty, entries are encrypted via WinZip AES (AE-2).
+ * - AES-256-CTR with HMAC-SHA1 (10-byte tag), interoperable with 7-Zip/WinZip/libarchive
+ * @param {table} files array: { name = string, content = string|rspamd_text, [mode|perms] = int, [mtime] = int }
+ * @param {string} password optional password string
+ * @return {text} archive bytes
*/
static int
lua_archive_zip_encrypt(lua_State *L)
wctx.buf = g_byte_array_new();
if (archive_write_open(a, &wctx, lua_archive_write_open, lua_archive_write_cb, lua_archive_write_close) != ARCHIVE_OK) {
+ const char *aerr = archive_error_string(a);
+ lua_pushfstring(L, "cannot open archive writer: %s", aerr ? aerr : "unknown error");
g_byte_array_free(wctx.buf, TRUE);
archive_write_free(a);
- return luaL_error(L, "cannot open archive writer: %s", archive_error_string(a));
+ return lua_error(L);
}
/* Iterate files table */
int r = archive_write_header(a, ae);
if (r != ARCHIVE_OK) {
- const char *err = archive_error_string(a);
+ const char *aerr = archive_error_string(a);
+ lua_pushfstring(L, "cannot write header: %s", aerr ? aerr : "unknown error");
archive_entry_free(ae);
archive_write_free(a);
g_byte_array_free(wctx.buf, TRUE);
- return luaL_error(L, "cannot write header: %s", err ? err : "unknown error");
+ return lua_error(L);
}
if (dlen > 0) {
la_ssize_t wr = archive_write_data(a, data, dlen);
if (wr < 0 || (size_t) wr != dlen) {
- const char *err = archive_error_string(a);
+ const char *aerr = archive_error_string(a);
+ lua_pushfstring(L, "cannot write data: %s", aerr ? aerr : "unknown error");
archive_entry_free(ae);
archive_write_free(a);
g_byte_array_free(wctx.buf, TRUE);
- return luaL_error(L, "cannot write data: %s", err ? err : "unknown error");
+ return lua_error(L);
}
}
int r = archive_read_open_memory(a, t->start, t->len);
if (r != ARCHIVE_OK) {
- const char *err = archive_error_string(a);
+ const char *aerr = archive_error_string(a);
+ lua_pushfstring(L, "cannot open archive: %s", aerr ? aerr : "unknown error");
archive_read_free(a);
- return luaL_error(L, "cannot open archive: %s", err ? err : "unknown error");
+ return lua_error(L);
}
lua_newtable(L);
break;
}
else if (rr < 0) {
- const char *err = archive_error_string(a);
+ const char *aerr = archive_error_string(a);
+ lua_pushfstring(L, "cannot read data: %s", aerr ? aerr : "unknown error");
g_byte_array_free(ba, TRUE);
archive_read_free(a);
- return luaL_error(L, "cannot read data: %s", err ? err : "unknown error");
+ return lua_error(L);
}
g_byte_array_append(ba, (const guint8 *) buf, (guint) rr);
}
assert_rspamd_eq({ actual = out[1].content, expect = rnd })
end)
+ test("zip_encrypt without password == plain zip", function()
+ local files = {
+ { name = "a.txt", content = "Hello" },
+ }
+ local blob = archive.zip_encrypt(files) -- no password
+ assert_equal(type(blob), "userdata")
+ local out = archive.unzip(blob)
+ assert_equal(#out, 1)
+ assert_equal(out[1].name, "a.txt")
+ assert_rspamd_eq({ actual = out[1].content, expect = rspamd_text.fromstring("Hello") })
+ end)
+
+ test("zip_encrypt with password (AE-2) roundtrip via libarchive", function()
+ local files = {
+ { name = "dir/x.txt", content = "secret" },
+ { name = "y.bin", content = rspamd_text.fromstring("\001\002\003") },
+ }
+ local pwd = "testpass123"
+ local blob = archive.zip_encrypt(files, pwd)
+ assert_equal(type(blob), "userdata")
+ -- libarchive can read AE-2, so unpack should succeed and yield the same files
+ local out = archive.unpack(blob, "zip")
+ assert_equal(#out, 2)
+ local names = {}
+ for _, f in ipairs(out) do names[f.name] = f.content end
+ assert_rspamd_eq({ actual = names["dir/x.txt"], expect = rspamd_text.fromstring("secret") })
+ assert_rspamd_eq({ actual = names["y.bin"], expect = rspamd_text.fromstring("\001\002\003") })
+ end)
+
test("tar/untar helpers roundtrip (no compression)", function()
local files = {
{ name = "x.txt", content = "X" },