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;
10 local s_gsub = string.gsub;
14 stanza_mt.__index = stanza_mt;
16 function stanza(name, attr)
17 local stanza = { name = name, attr = attr or {}, tags = {}, last_add = {}};
18 return setmetatable(stanza, stanza_mt);
21 function stanza_mt:query(xmlns)
22 return self:tag("query", { xmlns = xmlns });
24 function stanza_mt:tag(name, attrs)
25 local s = stanza(name, attrs);
26 (self.last_add[#self.last_add] or self):add_child(s);
27 t_insert(self.last_add, s);
31 function stanza_mt:text(text)
32 (self.last_add[#self.last_add] or self):add_child(text);
36 function stanza_mt:up()
37 t_remove(self.last_add);
41 function stanza_mt:add_child(child)
42 if type(child) == "table" then
43 t_insert(self.tags, child);
45 t_insert(self, child);
48 function stanza_mt:child_with_name(name)
49 for _, child in ipairs(self) do
50 if child.name == name then return child; end
54 function stanza_mt:children()
59 if v then return v; end
63 function stanza_mt:childtags()
67 local v = self.tags[i]
68 if v then return v; end
74 local xml_entities = { ["'"] = "'", ["\""] = """, ["<"] = "<", [">"] = ">", ["&"] = "&" };
75 function xml_escape(s) return s_gsub(s, "['&<>\"]", xml_entities); end
78 local xml_escape = xml_escape;
80 function stanza_mt.__tostring(t)
81 local children_text = "";
82 for n, child in ipairs(t) do
83 if type(child) == "string" then
84 children_text = children_text .. xml_escape(child);
86 children_text = children_text .. tostring(child);
90 local attr_string = "";
92 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
95 return s_format("<%s%s>%s</%s>", t.name, attr_string, children_text, t.name);
98 function stanza_mt.__add(s1, s2)
99 return s1:add_child(s2);
111 function preserialize(stanza)
112 local s = { name = stanza.name, attr = stanza.attr };
113 for _, child in ipairs(stanza) do
114 if type(child) == "table" then
115 t_insert(s, preserialize(child));
123 function deserialize(stanza)
125 setmetatable(stanza, stanza_mt);
126 for _, child in ipairs(stanza) do
127 if type(child) == "table" then
131 if not stanza.tags then
134 for _, child in ipairs(stanza) do
135 if type(child) == "table" then
136 t_insert(tags, child);
145 function message(attr, body)
147 return stanza("message", attr);
149 return stanza("message", attr):tag("body"):text(body);
153 if attr and not attr.id then attr.id = new_id(); end
154 return stanza("iq", attr or { id = new_id() });
158 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 nil) });
161 function error_reply(orig, type, condition, message, clone)
162 local t = reply(orig);
163 t.attr.type = "error";
165 t:tag("error", {type = type})
166 :tag(condition, {xmlns = "urn:ietf:params:xml:ns:xmpp-stanzas"}):up();
167 if (message) then t:tag("text"):text(message):up(); end
168 return t; -- stanza ready for adding app-specific errors
171 function presence(attr)
172 return stanza("presence", attr);