util/stanza.lua
changeset 7259 9fbb9fbf7e52
parent 6981 30c96a5db360
parent 7256 f4e71242556a
child 7761 2b305ec8c146
equal deleted inserted replaced
7258:3da7c334f034 7259:9fbb9fbf7e52
    38 local _ENV = nil;
    38 local _ENV = nil;
    39 
    39 
    40 local stanza_mt = { __type = "stanza" };
    40 local stanza_mt = { __type = "stanza" };
    41 stanza_mt.__index = stanza_mt;
    41 stanza_mt.__index = stanza_mt;
    42 
    42 
    43 local function stanza(name, attr, namespaces)
    43 local function new_stanza(name, attr, namespaces)
    44 	local stanza = { name = name, attr = attr or {}, namespaces = namespaces, tags = {} };
    44 	local stanza = { name = name, attr = attr or {}, namespaces = namespaces, tags = {} };
    45 	return setmetatable(stanza, stanza_mt);
    45 	return setmetatable(stanza, stanza_mt);
    46 end
    46 end
    47 local stanza = stanza;
       
    48 
    47 
    49 function stanza_mt:query(xmlns)
    48 function stanza_mt:query(xmlns)
    50 	return self:tag("query", { xmlns = xmlns });
    49 	return self:tag("query", { xmlns = xmlns });
    51 end
    50 end
    52 
    51 
    53 function stanza_mt:body(text, attr)
    52 function stanza_mt:body(text, attr)
    54 	return self:tag("body", attr):text(text);
    53 	return self:tag("body", attr):text(text);
    55 end
    54 end
    56 
    55 
    57 function stanza_mt:tag(name, attr, namespaces)
    56 function stanza_mt:tag(name, attr, namespaces)
    58 	local s = stanza(name, attr, namespaces);
    57 	local s = new_stanza(name, attr, namespaces);
    59 	local last_add = self.last_add;
    58 	local last_add = self.last_add;
    60 	if not last_add then last_add = {}; self.last_add = last_add; end
    59 	if not last_add then last_add = {}; self.last_add = last_add; end
    61 	(last_add[#last_add] or self):add_direct_child(s);
    60 	(last_add[#last_add] or self):add_direct_child(s);
    62 	t_insert(last_add, s);
    61 	t_insert(last_add, s);
    63 	return self;
    62 	return self;
   200 
   199 
   201 
   200 
   202 local escape_table = { ["'"] = "&apos;", ["\""] = "&quot;", ["<"] = "&lt;", [">"] = "&gt;", ["&"] = "&amp;" };
   201 local escape_table = { ["'"] = "&apos;", ["\""] = "&quot;", ["<"] = "&lt;", [">"] = "&gt;", ["&"] = "&amp;" };
   203 local function xml_escape(str) return (s_gsub(str, "['&<>\"]", escape_table)); end
   202 local function xml_escape(str) return (s_gsub(str, "['&<>\"]", escape_table)); end
   204 
   203 
   205 local function _dostring(t, buf, self, xml_escape, parentns)
   204 local function _dostring(t, buf, self, _xml_escape, parentns)
   206 	local nsid = 0;
   205 	local nsid = 0;
   207 	local name = t.name
   206 	local name = t.name
   208 	t_insert(buf, "<"..name);
   207 	t_insert(buf, "<"..name);
   209 	for k, v in pairs(t.attr) do
   208 	for k, v in pairs(t.attr) do
   210 		if s_find(k, "\1", 1, true) then
   209 		if s_find(k, "\1", 1, true) then
   211 			local ns, attrk = s_match(k, "^([^\1]*)\1?(.*)$");
   210 			local ns, attrk = s_match(k, "^([^\1]*)\1?(.*)$");
   212 			nsid = nsid + 1;
   211 			nsid = nsid + 1;
   213 			t_insert(buf, " xmlns:ns"..nsid.."='"..xml_escape(ns).."' ".."ns"..nsid..":"..attrk.."='"..xml_escape(v).."'");
   212 			t_insert(buf, " xmlns:ns"..nsid.."='".._xml_escape(ns).."' ".."ns"..nsid..":"..attrk.."='".._xml_escape(v).."'");
   214 		elseif not(k == "xmlns" and v == parentns) then
   213 		elseif not(k == "xmlns" and v == parentns) then
   215 			t_insert(buf, " "..k.."='"..xml_escape(v).."'");
   214 			t_insert(buf, " "..k.."='".._xml_escape(v).."'");
   216 		end
   215 		end
   217 	end
   216 	end
   218 	local len = #t;
   217 	local len = #t;
   219 	if len == 0 then
   218 	if len == 0 then
   220 		t_insert(buf, "/>");
   219 		t_insert(buf, "/>");
   221 	else
   220 	else
   222 		t_insert(buf, ">");
   221 		t_insert(buf, ">");
   223 		for n=1,len do
   222 		for n=1,len do
   224 			local child = t[n];
   223 			local child = t[n];
   225 			if child.name then
   224 			if child.name then
   226 				self(child, buf, self, xml_escape, t.attr.xmlns);
   225 				self(child, buf, self, _xml_escape, t.attr.xmlns);
   227 			else
   226 			else
   228 				t_insert(buf, xml_escape(child));
   227 				t_insert(buf, _xml_escape(child));
   229 			end
   228 			end
   230 		end
   229 		end
   231 		t_insert(buf, "</"..name..">");
   230 		t_insert(buf, "</"..name..">");
   232 	end
   231 	end
   233 end
   232 end
   250 		return t_concat(t);
   249 		return t_concat(t);
   251 	end
   250 	end
   252 end
   251 end
   253 
   252 
   254 function stanza_mt.get_error(stanza)
   253 function stanza_mt.get_error(stanza)
   255 	local type, condition, text;
   254 	local error_type, condition, text;
   256 
   255 
   257 	local error_tag = stanza:get_child("error");
   256 	local error_tag = stanza:get_child("error");
   258 	if not error_tag then
   257 	if not error_tag then
   259 		return nil, nil, nil;
   258 		return nil, nil, nil;
   260 	end
   259 	end
   261 	type = error_tag.attr.type;
   260 	error_type = error_tag.attr.type;
   262 
   261 
   263 	for _, child in ipairs(error_tag.tags) do
   262 	for _, child in ipairs(error_tag.tags) do
   264 		if child.attr.xmlns == xmlns_stanzas then
   263 		if child.attr.xmlns == xmlns_stanzas then
   265 			if not text and child.name == "text" then
   264 			if not text and child.name == "text" then
   266 				text = child:get_text();
   265 				text = child:get_text();
   270 			if condition and text then
   269 			if condition and text then
   271 				break;
   270 				break;
   272 			end
   271 			end
   273 		end
   272 		end
   274 	end
   273 	end
   275 	return type, condition or "undefined-condition", text;
   274 	return error_type, condition or "undefined-condition", text;
   276 end
   275 end
   277 
   276 
   278 local id = 0;
   277 local id = 0;
   279 local function new_id()
   278 local function new_id()
   280 	id = id + 1;
   279 	id = id + 1;
   350 	return setmetatable(new, stanza_mt);
   349 	return setmetatable(new, stanza_mt);
   351 end
   350 end
   352 
   351 
   353 local function message(attr, body)
   352 local function message(attr, body)
   354 	if not body then
   353 	if not body then
   355 		return stanza("message", attr);
   354 		return new_stanza("message", attr);
   356 	else
   355 	else
   357 		return stanza("message", attr):tag("body"):text(body):up();
   356 		return new_stanza("message", attr):tag("body"):text(body):up();
   358 	end
   357 	end
   359 end
   358 end
   360 local function iq(attr)
   359 local function iq(attr)
   361 	if attr and not attr.id then attr.id = new_id(); end
   360 	if attr and not attr.id then attr.id = new_id(); end
   362 	return stanza("iq", attr or { id = new_id() });
   361 	return new_stanza("iq", attr or { id = new_id() });
   363 end
   362 end
   364 
   363 
   365 local function reply(orig)
   364 local function reply(orig)
   366 	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) });
   365 	return new_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) });
   367 end
   366 end
   368 
   367 
   369 local xmpp_stanzas_attr = { xmlns = xmlns_stanzas };
   368 local xmpp_stanzas_attr = { xmlns = xmlns_stanzas };
   370 local function error_reply(orig, type, condition, message)
   369 local function error_reply(orig, error_type, condition, error_message)
   371 	local t = reply(orig);
   370 	local t = reply(orig);
   372 	t.attr.type = "error";
   371 	t.attr.type = "error";
   373 	t:tag("error", {type = type}) --COMPAT: Some day xmlns:stanzas goes here
   372 	t:tag("error", {type = error_type}) --COMPAT: Some day xmlns:stanzas goes here
   374 	:tag(condition, xmpp_stanzas_attr):up();
   373 	:tag(condition, xmpp_stanzas_attr):up();
   375 	if (message) then t:tag("text", xmpp_stanzas_attr):text(message):up(); end
   374 	if error_message then t:tag("text", xmpp_stanzas_attr):text(error_message):up(); end
   376 	return t; -- stanza ready for adding app-specific errors
   375 	return t; -- stanza ready for adding app-specific errors
   377 end
   376 end
   378 
   377 
   379 local function presence(attr)
   378 local function presence(attr)
   380 	return stanza("presence", attr);
   379 	return new_stanza("presence", attr);
   381 end
   380 end
   382 
   381 
   383 if do_pretty_printing then
   382 if do_pretty_printing then
   384 	local style_attrk = getstyle("yellow");
   383 	local style_attrk = getstyle("yellow");
   385 	local style_attrv = getstyle("red");
   384 	local style_attrv = getstyle("red");
   390 	local top_tag_format = getstring(style_punc, "<")..getstring(style_tagname, "%s").."%s"..getstring(style_punc, ">");
   389 	local top_tag_format = getstring(style_punc, "<")..getstring(style_tagname, "%s").."%s"..getstring(style_punc, ">");
   391 	--local tag_format = getstring(style_punc, "<")..getstring(style_tagname, "%s").."%s"..getstring(style_punc, ">").."%s"..getstring(style_punc, "</")..getstring(style_tagname, "%s")..getstring(style_punc, ">");
   390 	--local tag_format = getstring(style_punc, "<")..getstring(style_tagname, "%s").."%s"..getstring(style_punc, ">").."%s"..getstring(style_punc, "</")..getstring(style_tagname, "%s")..getstring(style_punc, ">");
   392 	local tag_format = top_tag_format.."%s"..getstring(style_punc, "</")..getstring(style_tagname, "%s")..getstring(style_punc, ">");
   391 	local tag_format = top_tag_format.."%s"..getstring(style_punc, "</")..getstring(style_tagname, "%s")..getstring(style_punc, ">");
   393 	function stanza_mt.pretty_print(t)
   392 	function stanza_mt.pretty_print(t)
   394 		local children_text = "";
   393 		local children_text = "";
   395 		for n, child in ipairs(t) do
   394 		for _, child in ipairs(t) do
   396 			if type(child) == "string" then
   395 			if type(child) == "string" then
   397 				children_text = children_text .. xml_escape(child);
   396 				children_text = children_text .. xml_escape(child);
   398 			else
   397 			else
   399 				children_text = children_text .. child:pretty_print();
   398 				children_text = children_text .. child:pretty_print();
   400 			end
   399 			end
   420 	stanza_mt.pretty_top_tag = stanza_mt.top_tag;
   419 	stanza_mt.pretty_top_tag = stanza_mt.top_tag;
   421 end
   420 end
   422 
   421 
   423 return {
   422 return {
   424 	stanza_mt = stanza_mt;
   423 	stanza_mt = stanza_mt;
   425 	stanza = stanza;
   424 	stanza = new_stanza;
   426 	new_id = new_id;
   425 	new_id = new_id;
   427 	preserialize = preserialize;
   426 	preserialize = preserialize;
   428 	deserialize = deserialize;
   427 	deserialize = deserialize;
   429 	clone = clone;
   428 	clone = clone;
   430 	message = message;
   429 	message = message;