X-Git-Url: https://git.enpas.org/?a=blobdiff_plain;f=util%2Fstanza.lua;h=1c1cab0e40425281b0316ebf40b7e8413a774edd;hb=cb2d79f8322fc2fd64655a9b8bb46aa9ac056ad8;hp=553bba4d6f783130bcb8f8991aadc33f05d79011;hpb=6e71a4c6b4dad19c8ad6636f7503a1e5cb7079f4;p=prosody.git diff --git a/util/stanza.lua b/util/stanza.lua index 553bba4d..1c1cab0e 100644 --- a/util/stanza.lua +++ b/util/stanza.lua @@ -1,12 +1,34 @@ +-- Prosody IM v0.3 +-- Copyright (C) 2008-2009 Matthew Wild +-- Copyright (C) 2008-2009 Waqas Hussain +-- +-- This project is MIT/X11 licensed. Please see the +-- COPYING file in the source package for more information. +-- + + local t_insert = table.insert; +local t_concat = table.concat; local t_remove = table.remove; +local t_concat = table.concat; local s_format = string.format; +local s_match = string.match; local tostring = tostring; local setmetatable = setmetatable; local pairs = pairs; local ipairs = ipairs; local type = type; +local next = next; +local print = print; +local unpack = unpack; local s_gsub = string.gsub; +local os = os; + +local do_pretty_printing = not os.getenv("WINDIR"); +local getstyle, getstring = require "util.termcolours".getstyle, require "util.termcolours".getstring; + +local log = require "util.logger".init("stanza"); + module "stanza" stanza_mt = {}; @@ -17,27 +39,23 @@ function stanza(name, attr) return setmetatable(stanza, stanza_mt); end -function stanza_mt:iq(attrs) - return self + stanza("iq", attrs) -end -function stanza_mt:message(attrs) - return self + stanza("message", attrs) -end -function stanza_mt:presence(attrs) - return self + stanza("presence", attrs) -end function stanza_mt:query(xmlns) return self:tag("query", { xmlns = xmlns }); end + +function stanza_mt:body(text, attr) + return self:tag("body", attr):text(text); +end + function stanza_mt:tag(name, attrs) local s = stanza(name, attrs); - (self.last_add[#self.last_add] or self):add_child(s); + (self.last_add[#self.last_add] or self):add_direct_child(s); t_insert(self.last_add, s); return self; end function stanza_mt:text(text) - (self.last_add[#self.last_add] or self):add_child(text); + (self.last_add[#self.last_add] or self):add_direct_child(text); return self; end @@ -46,19 +64,30 @@ function stanza_mt:up() return self; end -function stanza_mt:add_child(child) +function stanza_mt:add_direct_child(child) if type(child) == "table" then t_insert(self.tags, child); end t_insert(self, child); end +function stanza_mt:add_child(child) + (self.last_add[#self.last_add] or self):add_direct_child(child); + return self; +end + function stanza_mt:child_with_name(name) - for _, child in ipairs(self) do + for _, child in ipairs(self.tags) do if child.name == name then return child; end end end +function stanza_mt:child_with_ns(ns) + for _, child in ipairs(self.tags) do + if child.attr.xmlns == ns then return child; end + end +end + function stanza_mt:children() local i = 0; return function (a) @@ -85,26 +114,57 @@ end local xml_escape = xml_escape; -function stanza_mt.__tostring(t) - local children_text = ""; +local function dostring(t, buf, self, xml_escape) + local nsid, ns, attrk = 0; + t_insert(buf, "<"); + t_insert(buf, t.name); + for k, v in pairs(t.attr) do if type(k) == "string" then + t_insert(buf, " "); + ns, attrk = s_match(k, "^([^|]+)|(.+)$"); + if ns then + nsid = (nsid or -1) + 1; + t_insert(buf, "xmlns:ns"..nsid); + t_insert(buf, "='"); + t_insert(buf, (xml_escape(tostring(ns)))); + t_insert(buf, "' "); + t_insert(buf, "ns"..nsid..":"..attrk); + else + t_insert(buf, k); + end + t_insert(buf, "='"); + t_insert(buf, (xml_escape(tostring(v)))); + t_insert(buf, "'"); + end end + t_insert(buf, ">"); for n, child in ipairs(t) do - if type(child) == "string" then - children_text = children_text .. xml_escape(child); + if child.name then + self(child, buf, self, xml_escape); else - children_text = children_text .. tostring(child); + t_insert(buf, (xml_escape(child))); end end + t_insert(buf, ""); +end + +function stanza_mt.__tostring(t) + local buf = {}; + dostring(t, buf, dostring, xml_escape); + return t_concat(buf); +end + +function stanza_mt.top_tag(t) local attr_string = ""; if t.attr then - 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 + for k, v in pairs(t.attr) do if type(k) == "string" then attr_string = attr_string .. s_format(" %s='%s'", k, xml_escape(tostring(v))); end end end - - return s_format("<%s%s>%s", t.name, attr_string, children_text, t.name); + return s_format("<%s%s>", t.name, attr_string); end function stanza_mt.__add(s1, s2) - return s1:add_child(s2); + return s1:add_direct_child(s2); end @@ -116,6 +176,45 @@ do end end +function preserialize(stanza) + local s = { name = stanza.name, attr = stanza.attr }; + for _, child in ipairs(stanza) do + if type(child) == "table" then + t_insert(s, preserialize(child)); + else + t_insert(s, child); + end + end + return s; +end + +function deserialize(stanza) + -- Set metatable + if stanza then + setmetatable(stanza, stanza_mt); + for _, child in ipairs(stanza) do + if type(child) == "table" then + deserialize(child); + end + end + if not stanza.tags then + -- Rebuild tags + local tags = {}; + for _, child in ipairs(stanza) do + if type(child) == "table" then + t_insert(tags, child); + end + end + stanza.tags = tags; + if not stanza.last_add then + stanza.last_add = {}; + end + end + end + + return stanza; +end + function message(attr, body) if not body then return stanza("message", attr); @@ -129,11 +228,61 @@ function iq(attr) end function reply(orig) - 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) }); + 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) }); +end + +function error_reply(orig, type, condition, message, clone) + local t = reply(orig); + t.attr.type = "error"; + -- TODO use clone + t:tag("error", {type = type}) + :tag(condition, {xmlns = "urn:ietf:params:xml:ns:xmpp-stanzas"}):up(); + if (message) then t:tag("text"):text(message):up(); end + return t; -- stanza ready for adding app-specific errors end function presence(attr) return stanza("presence", attr); end -return _M; \ No newline at end of file +if do_pretty_printing then + local style_attrk = getstyle("yellow"); + local style_attrv = getstyle("red"); + local style_tagname = getstyle("red"); + local style_punc = getstyle("magenta"); + + local attr_format = " "..getstring(style_attrk, "%s")..getstring(style_punc, "=")..getstring(style_attrv, "'%s'"); + local top_tag_format = getstring(style_punc, "<")..getstring(style_tagname, "%s").."%s"..getstring(style_punc, ">"); + --local tag_format = getstring(style_punc, "<")..getstring(style_tagname, "%s").."%s"..getstring(style_punc, ">").."%s"..getstring(style_punc, ""); + local tag_format = top_tag_format.."%s"..getstring(style_punc, ""); + function stanza_mt.pretty_print(t) + local children_text = ""; + for n, child in ipairs(t) do + if type(child) == "string" then + children_text = children_text .. xml_escape(child); + else + children_text = children_text .. child:pretty_print(); + end + end + + local attr_string = ""; + if t.attr then + for k, v in pairs(t.attr) do if type(k) == "string" then attr_string = attr_string .. s_format(attr_format, k, tostring(v)); end end + end + return s_format(tag_format, t.name, attr_string, children_text, t.name); + end + + function stanza_mt.pretty_top_tag(t) + local attr_string = ""; + if t.attr then + for k, v in pairs(t.attr) do if type(k) == "string" then attr_string = attr_string .. s_format(attr_format, k, tostring(v)); end end + end + return s_format(top_tag_format, t.name, attr_string); + end +else + -- Sorry, fresh out of colours for you guys ;) + stanza_mt.pretty_print = stanza_mt.__tostring; + stanza_mt.pretty_top_tag = stanza_mt.top_tag; +end + +return _M;