mod_log_ringbuffer: Detach event handlers on logging reload (thanks Menel)
Otherwise the global event handlers accumulate, one added each time
logging is reoladed, and each invocation of the signal or event triggers
one dump of each created ringbuffer.
-- Copyright (C) 2011-2012 Kim Alvefur
--
-- This project is MIT/X11 licensed. Please see the
-- COPYING file in the source package for more information.
--
-- TODO
-- Fix folding.
local st = require "util.stanza";
local t_insert, t_concat = table.insert, table.concat;
local type = type;
local next, pairs, ipairs = next, pairs, ipairs;
local from_text, to_text, from_xep54, to_xep54;
local line_sep = "\n";
local vCard_dtd; -- See end of file
local function fold_line()
error "Not implemented" --TODO
end
local function unfold_line()
error "Not implemented"
-- gsub("\r?\n[ \t]([^\r\n])", "%1");
end
local function vCard_esc(s)
return s:gsub("[,:;\\]", "\\%1"):gsub("\n","\\n");
end
local function vCard_unesc(s)
return s:gsub("\\?[\\nt:;,]", {
["\\\\"] = "\\",
["\\n"] = "\n",
["\\r"] = "\r",
["\\t"] = "\t",
["\\:"] = ":", -- FIXME Shouldn't need to espace : in values, just params
["\\;"] = ";",
["\\,"] = ",",
[":"] = "\29",
[";"] = "\30",
[","] = "\31",
});
end
local function item_to_xep54(item)
local t = st.stanza(item.name, { xmlns = "vcard-temp" });
local prop_def = vCard_dtd[item.name];
if prop_def == "text" then
t:text(item[1]);
elseif type(prop_def) == "table" then
if prop_def.types and item.TYPE then
if type(item.TYPE) == "table" then
for _,v in pairs(prop_def.types) do
for _,typ in pairs(item.TYPE) do
if typ:upper() == v then
t:tag(v):up();
break;
end
end
end
else
t:tag(item.TYPE:upper()):up();
end
end
if prop_def.props then
for _,v in pairs(prop_def.props) do
if item[v] then
t:tag(v):up();
end
end
end
if prop_def.value then
t:tag(prop_def.value):text(item[1]):up();
elseif prop_def.values then
local prop_def_values = prop_def.values;
local repeat_last = prop_def_values.behaviour == "repeat-last" and prop_def_values[#prop_def_values];
for i=1,#item do
t:tag(prop_def.values[i] or repeat_last):text(item[i]):up();
end
end
end
return t;
end
local function vcard_to_xep54(vCard)
local t = st.stanza("vCard", { xmlns = "vcard-temp" });
for i=1,#vCard do
t:add_child(item_to_xep54(vCard[i]));
end
return t;
end
function to_xep54(vCards)
if not vCards[1] or vCards[1].name then
return vcard_to_xep54(vCards)
else
local t = st.stanza("xCard", { xmlns = "vcard-temp" });
for i=1,#vCards do
t:add_child(vcard_to_xep54(vCards[i]));
end
return t;
end
end
function from_text(data)
data = data -- unfold and remove empty lines
:gsub("\r\n","\n")
:gsub("\n ", "")
:gsub("\n\n+","\n");
local vCards = {};
local c; -- current item
for line in data:gmatch("[^\n]+") do
local line = vCard_unesc(line);
local name, params, value = line:match("^([-%a]+)(\30?[^\29]*)\29(.*)$");
value = value:gsub("\29",":");
if #params > 0 then
local _params = {};
for k,isval,v in params:gmatch("\30([^=]+)(=?)([^\30]*)") do
k = k:upper();
local _vt = {};
for _p in v:gmatch("[^\31]+") do
_vt[#_vt+1]=_p
_vt[_p]=true;
end
if isval == "=" then
_params[k]=_vt;
else
_params[k]=true;
end
end
params = _params;
end
if name == "BEGIN" and value == "VCARD" then
c = {};
vCards[#vCards+1] = c;
elseif name == "END" and value == "VCARD" then
c = nil;
elseif vCard_dtd[name] then
local dtd = vCard_dtd[name];
local p = { name = name };
c[#c+1]=p;
--c[name]=p;
local up = c;
c = p;
if dtd.types then
for _, t in ipairs(dtd.types) do
local t = t:lower();
if ( params.TYPE and params.TYPE[t] == true)
or params[t] == true then
c.TYPE=t;
end
end
end
if dtd.props then
for _, p in ipairs(dtd.props) do
if params[p] then
if params[p] == true then
c[p]=true;
else
for _, prop in ipairs(params[p]) do
c[p]=prop;
end
end
end
end
end
if dtd == "text" or dtd.value then
t_insert(c, value);
elseif dtd.values then
local value = "\30"..value;
for p in value:gmatch("\30([^\30]*)") do
t_insert(c, p);
end
end
c = up;
end
end
return vCards;
end
local function item_to_text(item)
local value = {};
for i=1,#item do
value[i] = vCard_esc(item[i]);
end
value = t_concat(value, ";");
local params = "";
for k,v in pairs(item) do
if type(k) == "string" and k ~= "name" then
params = params .. (";%s=%s"):format(k, type(v) == "table" and t_concat(v,",") or v);
end
end
return ("%s%s:%s"):format(item.name, params, value)
end
local function vcard_to_text(vcard)
local t={};
t_insert(t, "BEGIN:VCARD")
for i=1,#vcard do
t_insert(t, item_to_text(vcard[i]));
end
t_insert(t, "END:VCARD")
return t_concat(t, line_sep);
end
function to_text(vCards)
if vCards[1] and vCards[1].name then
return vcard_to_text(vCards)
else
local t = {};
for i=1,#vCards do
t[i]=vcard_to_text(vCards[i]);
end
return t_concat(t, line_sep);
end
end
local function from_xep54_item(item)
local prop_name = item.name;
local prop_def = vCard_dtd[prop_name];
local prop = { name = prop_name };
if prop_def == "text" then
prop[1] = item:get_text();
elseif type(prop_def) == "table" then
if prop_def.value then --single item
prop[1] = item:get_child_text(prop_def.value) or "";
elseif prop_def.values then --array
local value_names = prop_def.values;
if value_names.behaviour == "repeat-last" then
for i=1,#item.tags do
t_insert(prop, item.tags[i]:get_text() or "");
end
else
for i=1,#value_names do
t_insert(prop, item:get_child_text(value_names[i]) or "");
end
end
elseif prop_def.names then
local names = prop_def.names;
for i=1,#names do
if item:get_child(names[i]) then
prop[1] = names[i];
break;
end
end
end
if prop_def.props_verbatim then
for k,v in pairs(prop_def.props_verbatim) do
prop[k] = v;
end
end
if prop_def.types then
local types = prop_def.types;
prop.TYPE = {};
for i=1,#types do
if item:get_child(types[i]) then
t_insert(prop.TYPE, types[i]:lower());
end
end
if #prop.TYPE == 0 then
prop.TYPE = nil;
end
end
-- A key-value pair, within a key-value pair?
if prop_def.props then
local params = prop_def.props;
for i=1,#params do
local name = params[i]
local data = item:get_child_text(name);
if data then
prop[name] = prop[name] or {};
t_insert(prop[name], data);
end
end
end
else
return nil
end
return prop;
end
local function from_xep54_vCard(vCard)
local tags = vCard.tags;
local t = {};
for i=1,#tags do
t_insert(t, from_xep54_item(tags[i]));
end
return t
end
function from_xep54(vCard)
if vCard.attr.xmlns ~= "vcard-temp" then
return nil, "wrong-xmlns";
end
if vCard.name == "xCard" then -- A collection of vCards
local t = {};
local vCards = vCard.tags;
for i=1,#vCards do
t[i] = from_xep54_vCard(vCards[i]);
end
return t
elseif vCard.name == "vCard" then -- A single vCard
return from_xep54_vCard(vCard)
end
end
-- This was adapted from http://xmpp.org/extensions/xep-0054.html#dtd
vCard_dtd = {
VERSION = "text", --MUST be 3.0, so parsing is redundant
FN = "text",
N = {
values = {
"FAMILY",
"GIVEN",
"MIDDLE",
"PREFIX",
"SUFFIX",
},
},
NICKNAME = "text",
PHOTO = {
props_verbatim = { ENCODING = { "b" } },
props = { "TYPE" },
value = "BINVAL", --{ "EXTVAL", },
},
BDAY = "text",
ADR = {
types = {
"HOME",
"WORK",
"POSTAL",
"PARCEL",
"DOM",
"INTL",
"PREF",
},
values = {
"POBOX",
"EXTADD",
"STREET",
"LOCALITY",
"REGION",
"PCODE",
"CTRY",
}
},
LABEL = {
types = {
"HOME",
"WORK",
"POSTAL",
"PARCEL",
"DOM",
"INTL",
"PREF",
},
value = "LINE",
},
TEL = {
types = {
"HOME",
"WORK",
"VOICE",
"FAX",
"PAGER",
"MSG",
"CELL",
"VIDEO",
"BBS",
"MODEM",
"ISDN",
"PCS",
"PREF",
},
value = "NUMBER",
},
EMAIL = {
types = {
"HOME",
"WORK",
"INTERNET",
"PREF",
"X400",
},
value = "USERID",
},
JABBERID = "text",
MAILER = "text",
TZ = "text",
GEO = {
values = {
"LAT",
"LON",
},
},
TITLE = "text",
ROLE = "text",
LOGO = "copy of PHOTO",
AGENT = "text",
ORG = {
values = {
behaviour = "repeat-last",
"ORGNAME",
"ORGUNIT",
}
},
CATEGORIES = {
values = "KEYWORD",
},
NOTE = "text",
PRODID = "text",
REV = "text",
SORTSTRING = "text",
SOUND = "copy of PHOTO",
UID = "text",
URL = "text",
CLASS = {
names = { -- The item.name is the value if it's one of these.
"PUBLIC",
"PRIVATE",
"CONFIDENTIAL",
},
},
KEY = {
props = { "TYPE" },
value = "CRED",
},
DESC = "text",
};
vCard_dtd.LOGO = vCard_dtd.PHOTO;
vCard_dtd.SOUND = vCard_dtd.PHOTO;
return {
from_text = from_text;
to_text = to_text;
from_xep54 = from_xep54;
to_xep54 = to_xep54;
-- COMPAT:
lua_to_text = to_text;
lua_to_xep54 = to_xep54;
text_to_lua = from_text;
text_to_xep54 = function (...) return to_xep54(from_text(...)); end;
xep54_to_lua = from_xep54;
xep54_to_text = function (...) return to_text(from_xep54(...)) end;
};