util.x509: Only collect commonNames that pass idna
[prosody.git] / util / serialization.lua
blobd70e92ba4e41472d592ce989930113f28e4b7b79
1 -- Prosody IM
2 -- Copyright (C) 2008-2010 Matthew Wild
3 -- Copyright (C) 2008-2010 Waqas Hussain
4 -- Copyright (C) 2018 Kim Alvefur
5 --
6 -- This project is MIT/X11 licensed. Please see the
7 -- COPYING file in the source package for more information.
8 --
10 local getmetatable = getmetatable;
11 local next, type = next, type;
12 local s_format = string.format;
13 local s_gsub = string.gsub;
14 local s_rep = string.rep;
15 local s_char = string.char;
16 local s_match = string.match;
17 local t_concat = table.concat;
19 local to_hex = require "util.hex".to;
21 local pcall = pcall;
22 local envload = require"util.envload".envload;
24 local pos_inf, neg_inf = math.huge, -math.huge;
25 local m_type = math.type or function (n)
26 return n % 1 == 0 and n <= 9007199254740992 and n >= -9007199254740992 and "integer" or "float";
27 end;
29 local function rawpairs(t)
30 return next, t, nil;
31 end
33 local function fatal_error(obj, why)
34 error("Can't serialize "..type(obj) .. (why and ": ".. why or ""));
35 end
37 local function nonfatal_fallback(x, why)
38 return s_format("{__type=%q,__error=%q}", type(x), why or "fail");
39 end
41 local string_escapes = {
42 ['\a'] = [[\a]]; ['\b'] = [[\b]];
43 ['\f'] = [[\f]]; ['\n'] = [[\n]];
44 ['\r'] = [[\r]]; ['\t'] = [[\t]];
45 ['\v'] = [[\v]]; ['\\'] = [[\\]];
46 ['\"'] = [[\"]]; ['\''] = [[\']];
49 for i = 0, 255 do
50 local c = s_char(i);
51 if not string_escapes[c] then
52 string_escapes[c] = s_format("\\%03d", i);
53 end
54 end
56 local default_keywords = {
57 ["do"] = true; ["and"] = true; ["else"] = true; ["break"] = true;
58 ["if"] = true; ["end"] = true; ["goto"] = true; ["false"] = true;
59 ["in"] = true; ["for"] = true; ["then"] = true; ["local"] = true;
60 ["or"] = true; ["nil"] = true; ["true"] = true; ["until"] = true;
61 ["elseif"] = true; ["function"] = true; ["not"] = true;
62 ["repeat"] = true; ["return"] = true; ["while"] = true;
65 local function new(opt)
66 if type(opt) ~= "table" then
67 opt = { preset = opt };
68 end
70 local types = {
71 table = true;
72 string = true;
73 number = true;
74 boolean = true;
75 ["nil"] = true;
78 -- presets
79 if opt.preset == "debug" then
80 opt.preset = "oneline";
81 opt.freeze = true;
82 opt.fatal = false;
83 opt.fallback = nonfatal_fallback;
84 opt.unquoted = true;
85 end
86 if opt.preset == "oneline" then
87 opt.indentwith = opt.indentwith or "";
88 opt.itemstart = opt.itemstart or " ";
89 opt.itemlast = opt.itemlast or "";
90 opt.tend = opt.tend or " }";
91 elseif opt.preset == "compact" then
92 opt.indentwith = opt.indentwith or "";
93 opt.itemstart = opt.itemstart or "";
94 opt.itemlast = opt.itemlast or "";
95 opt.equals = opt.equals or "=";
96 opt.unquoted = true;
97 end
99 local fallback = opt.fallback or opt.fatal == false and nonfatal_fallback or fatal_error;
101 local function ser(v)
102 return (types[type(v)] or fallback)(v);
105 local keywords = opt.keywords or default_keywords;
107 -- indented
108 local indentwith = opt.indentwith or "\t";
109 local itemstart = opt.itemstart or "\n";
110 local itemsep = opt.itemsep or ";";
111 local itemlast = opt.itemlast or ";\n";
112 local tstart = opt.tstart or "{";
113 local tend = opt.tend or "}";
114 local kstart = opt.kstart or "[";
115 local kend = opt.kend or "]";
116 local equals = opt.equals or " = ";
117 local unquoted = opt.unquoted == true and "^[%a_][%w_]*$" or opt.unquoted;
118 local hex = opt.hex;
119 local freeze = opt.freeze;
120 local maxdepth = opt.maxdepth or 127;
121 local multirefs = opt.multiref;
122 local table_pairs = opt.table_iterator or rawpairs;
124 -- serialize one table, recursively
125 -- t - table being serialized
126 -- o - array where tokens are added, concatenate to get final result
127 -- - also used to detect cycles
128 -- l - position in o of where to insert next token
129 -- d - depth, used for indentation
130 local function serialize_table(t, o, l, d)
131 if o[t] then
132 o[l], l = fallback(t, "table has multiple references"), l + 1;
133 return l;
134 elseif d > maxdepth then
135 o[l], l = fallback(t, "max table depth reached"), l + 1;
136 return l;
139 -- Keep track of table loops
140 local ot = t; -- reference pre-freeze
141 o[t] = true;
142 o[ot] = true;
144 if freeze == true then
145 -- opportunity to do pre-serialization
146 local mt = getmetatable(t);
147 if type(mt) == "table" then
148 local tag = mt.__name;
149 local fr = mt.__freeze;
151 if type(fr) == "function" then
152 t = fr(t);
153 if type(tag) == "string" then
154 o[l], l = tag, l + 1;
160 o[l], l = tstart, l + 1;
161 local indent = s_rep(indentwith, d);
162 local numkey = 1;
163 local ktyp, vtyp;
164 local had_items = false;
165 for k,v in table_pairs(t) do
166 had_items = true;
167 o[l], l = itemstart, l + 1;
168 o[l], l = indent, l + 1;
169 ktyp, vtyp = type(k), type(v);
170 if k == numkey then
171 -- next index in array part
172 -- assuming that these are found in order
173 numkey = numkey + 1;
174 elseif unquoted and ktyp == "string" and
175 not keywords[k] and s_match(k, unquoted) then
176 -- unquoted keys
177 o[l], l = k, l + 1;
178 o[l], l = equals, l + 1;
179 else
180 -- quoted keys
181 o[l], l = kstart, l + 1;
182 if ktyp == "table" then
183 l = serialize_table(k, o, l, d+1);
184 else
185 o[l], l = ser(k), l + 1;
187 -- =
188 o[l], o[l+1], l = kend, equals, l + 2;
191 -- the value
192 if vtyp == "table" then
193 l = serialize_table(v, o, l, d+1);
194 else
195 o[l], l = ser(v), l + 1;
197 o[l], l = itemsep, l + 1;
199 if had_items then
200 o[l - 1] = itemlast;
201 o[l], l = s_rep(indentwith, d-1), l + 1;
203 o[l], l = tend, l +1;
205 if multirefs then
206 o[t] = nil;
207 o[ot] = nil;
210 return l;
213 function types.table(t)
214 local o = {};
215 serialize_table(t, o, 1, 1);
216 return t_concat(o);
219 local function serialize_string(s)
220 return '"' .. s_gsub(s, "[%z\1-\31\"\'\\\127-\255]", string_escapes) .. '"';
223 if type(hex) == "string" then
224 function types.string(s)
225 local esc = serialize_string(s);
226 if #esc > (#s*2+2+#hex) then
227 return hex .. '"' .. to_hex(s) .. '"';
229 return esc;
231 else
232 types.string = serialize_string;
235 function types.number(t)
236 if m_type(t) == "integer" then
237 return s_format("%d", t);
238 elseif t == pos_inf then
239 return "(1/0)";
240 elseif t == neg_inf then
241 return "(-1/0)";
242 elseif t ~= t then
243 return "(0/0)";
245 return s_format("%.18g", t);
248 -- Are these faster than tostring?
249 types["nil"] = function()
250 return "nil";
253 function types.boolean(t)
254 return t and "true" or "false";
257 return ser;
260 local function deserialize(str)
261 if type(str) ~= "string" then return nil; end
262 str = "return "..str;
263 local f, err = envload(str, "=serialized data", {});
264 if not f then return nil, err; end
265 local success, ret = pcall(f);
266 if not success then return nil, ret; end
267 return ret;
270 local default = new();
271 return {
272 new = new;
273 serialize = function (x, opt)
274 if opt == nil then
275 return default(x);
276 else
277 return new(opt)(x);
279 end;
280 deserialize = deserialize;