Mercurial > prosody-modules
view mod_storage_appendmap/mod_storage_appendmap.lua @ 4651:8231774f5bfd
mod_cloud_notify_encrypted: Ensure body substring remains valid UTF-8
The `body:sub()` call risks splitting the string in the middle of a
multi-byte UTF-8 sequence. This should have been caught by util.stanza
validation, but that would have caused some havoc, at the very least causing
the notification to not be sent.
There have been no reports of this happening. Likely because this module
isn't widely deployed among users with languages that use many longer UTF-8
sequences.
The util.encodings.utf8.valid() function is O(n) where only the last
sequence really needs to be checked, but it's in C and expected to be fast.
author | Kim Alvefur <zash@zash.se> |
---|---|
date | Sun, 22 Aug 2021 13:22:59 +0200 |
parents | 6a7b7cb7148e |
children | e44b868cc575 |
line wrap: on
line source
local dump = require "util.serialization".serialize; local load = require "util.envload".envloadfile; local dm = require "core.storagemanager".olddm; local REMOVE = {}; -- Special value for removing keys local driver = {}; local keywords = { ["do"] = true; ["and"] = true; ["else"] = true; ["break"] = true; ["if"] = true; ["end"] = true; ["goto"] = true; ["false"] = true; ["in"] = true; ["for"] = true; ["then"] = true; ["local"] = true; ["or"] = true; ["nil"] = true; ["true"] = true; ["until"] = true; ["elseif"] = true; ["function"] = true; ["not"] = true; ["repeat"] = true; ["return"] = true; ["while"] = true; -- _ENV is not technically a keyword but we need to treat it as such ["_ENV"] = true; }; local function is_usable_identifier(s) return type(s) == "string" and not keywords[s] and s:find("^[%a_][%w_]*$"); end local function serialize_key(key) if is_usable_identifier(key) then return key; else return "_ENV[" .. dump(key) .. "]"; end end local function serialize_value(value) if value == REMOVE then return "nil"; else return dump(value); end end local function serialize_pair(key, value) key = serialize_key(key); value = serialize_value(value); return key .. " = " .. value .. ";\n"; end local function serialize_map(keyvalues) local keys, values = {}, {}; for key, value in pairs(keyvalues) do key = serialize_key(key); value = serialize_value(value); table.insert(keys, key); table.insert(values, value); end return table.concat(keys, ", ") .. " = " .. table.concat(values, ", ") .. ";\n"; end local map = { remove = REMOVE }; local map_mt = { __index = map }; function map:get(user, key) module:log("debug", "map:get(%s, %s)", tostring(user), tostring(key)) local filename = dm.getpath(user, module.host, self.store, "map"); module:log("debug", "File is %s", filename); local env = {}; if _VERSION == "Lua 5.1" then -- HACK env._ENV = env; -- HACK end -- SO MANY HACKS local chunk, err, errno = load(filename, env); if not chunk then if errno == 2 then return end return chunk, err; end local ok, err = pcall(chunk); if not ok then return ok, err; end if _VERSION == "Lua 5.1" then -- HACK env._ENV = nil; -- HACK end -- HACKS EVERYWHERE if key == nil then return env; end return env[key]; end function map:set_keys(user, keyvalues) local data = serialize_map(keyvalues); return dm.append_raw(user, module.host, self.store, "map", data); end function map:set(user, key, value) if _VERSION == "Lua 5.1" then assert(key ~= "_ENV", "'_ENV' is a restricted key"); end if key == nil then local filename = dm.getpath(user, module.host, self.store, "map"); return os.remove(filename); end local data = serialize_pair(key, value); return dm.append_raw(user, module.host, self.store, "map", data); end local keyval = { remove = REMOVE }; local keyval_mt = { __index = keyval }; function keyval:get(user) return map.get(self, user, nil); end function keyval:set(user, keyvalues) local data = serialize_map(keyvalues); return dm.store_raw(user, module.host, self.store, "map", data); end -- TODO some kind of periodic compaction thing? function map:_compact(user) local data = self:get(user); return keyval.set(self, user, data); end function driver:open(store, typ) if typ == "map" then return setmetatable({ store = store, }, map_mt); elseif typ == nil or typ == "keyval" then return setmetatable({ store = store, }, keyval_mt); end return nil, "unsupported-store"; end module:provides("storage", driver);