1 local t_insert = table.insert;
2 local t_remove = table.remove;
3 local s_format = string.format;
4 local tostring = tostring;
5 local setmetatable = setmetatable;
11 local unpack = unpack;
12 local s_gsub = string.gsub;
15 local log = require "util.logger".init("stanza");
20 stanza_mt.__index = stanza_mt;
22 function stanza(name, attr)
23 local stanza = { name = name, attr = attr or {}, tags = {}, last_add = {}};
24 return setmetatable(stanza, stanza_mt);
27 function stanza_mt:query(xmlns)
28 return self:tag("query", { xmlns = xmlns });
30 function stanza_mt:tag(name, attrs)
31 local s = stanza(name, attrs);
32 (self.last_add[#self.last_add] or self):add_direct_child(s);
33 t_insert(self.last_add, s);
37 function stanza_mt:text(text)
38 (self.last_add[#self.last_add] or self):add_direct_child(text);
42 function stanza_mt:up()
43 t_remove(self.last_add);
47 function stanza_mt:add_direct_child(child)
48 if type(child) == "table" then
49 t_insert(self.tags, child);
51 t_insert(self, child);
54 function stanza_mt:add_child(child)
55 (self.last_add[#self.last_add] or self):add_direct_child(child);
59 function stanza_mt:child_with_name(name)
60 for _, child in ipairs(self) do
61 if child.name == name then return child; end
65 function stanza_mt:children()
70 if v then return v; end
74 function stanza_mt:childtags()
78 local v = self.tags[i]
79 if v then return v; end
85 local xml_entities = { ["'"] = "'", ["\""] = """, ["<"] = "<", [">"] = ">", ["&"] = "&" };
86 function xml_escape(s) return s_gsub(s, "['&<>\"]", xml_entities); end
89 local xml_escape = xml_escape;
91 function stanza_mt.__tostring(t)
92 local children_text = "";
93 for n, child in ipairs(t) do
94 if type(child) == "string" then
95 children_text = children_text .. xml_escape(child);
97 children_text = children_text .. tostring(child);
101 local attr_string = "";
103 for k, v in pairs(t.attr) do if type(k) == "string" then attr_string = attr_string .. s_format(" %s='%s'", k, tostring(v)); end end
105 return s_format("<%s%s>%s</%s>", t.name, attr_string, children_text, t.name);
108 function stanza_mt.__add(s1, s2)
109 return s1:add_direct_child(s2);
121 function preserialize(stanza)
122 local s = { name = stanza.name, attr = stanza.attr };
123 for _, child in ipairs(stanza) do
124 if type(child) == "table" then
125 t_insert(s, preserialize(child));
133 function deserialize(stanza)
136 setmetatable(stanza, stanza_mt);
137 for _, child in ipairs(stanza) do
138 if type(child) == "table" then
142 if not stanza.tags then
145 for _, child in ipairs(stanza) do
146 if type(child) == "table" then
147 t_insert(tags, child);
157 function message(attr, body)
159 return stanza("message", attr);
161 return stanza("message", attr):tag("body"):text(body);
165 if attr and not attr.id then attr.id = new_id(); end
166 return stanza("iq", attr or { id = new_id() });
170 return stanza(orig.name, orig.attr and { to = orig.attr.from, from = orig.attr.to, id = orig.attr.id, type = ((orig.name == "iq" and "result") or orig.attr.type) });
173 function error_reply(orig, type, condition, message, clone)
174 local t = reply(orig);
175 t.attr.type = "error";
177 t:tag("error", {type = type})
178 :tag(condition, {xmlns = "urn:ietf:params:xml:ns:xmpp-stanzas"}):up();
179 if (message) then t:tag("text"):text(message):up(); end
180 return t; -- stanza ready for adding app-specific errors
183 function presence(attr)
184 return stanza("presence", attr);