comparison util/stanza.lua @ 12802:4a8740e01813

Merge 0.12->trunk
author Kim Alvefur <zash@zash.se>
date Mon, 12 Dec 2022 07:10:54 +0100
parents 12ced5db29b2
children d10957394a3c
comparison
equal deleted inserted replaced
12801:ebd6b4d8bf04 12802:4a8740e01813
19 local ipairs = ipairs; 19 local ipairs = ipairs;
20 local type = type; 20 local type = type;
21 local s_gsub = string.gsub; 21 local s_gsub = string.gsub;
22 local s_sub = string.sub; 22 local s_sub = string.sub;
23 local s_find = string.find; 23 local s_find = string.find;
24 local t_move = table.move or require "util.table".move;
25 local t_create = require"util.table".create;
24 26
25 local valid_utf8 = require "util.encodings".utf8.valid; 27 local valid_utf8 = require "util.encodings".utf8.valid;
26 28
27 local do_pretty_printing, termcolours = pcall(require, "util.termcolours"); 29 local do_pretty_printing, termcolours = pcall(require, "util.termcolours");
28 30
29 local xmlns_stanzas = "urn:ietf:params:xml:ns:xmpp-stanzas"; 31 local xmlns_stanzas = "urn:ietf:params:xml:ns:xmpp-stanzas";
32 local xmpp_stanzas_attr = { xmlns = xmlns_stanzas };
30 33
31 local _ENV = nil; 34 local _ENV = nil;
32 -- luacheck: std none 35 -- luacheck: std none
33 36
34 local stanza_mt = { __name = "stanza" }; 37 local stanza_mt = { __name = "stanza" };
173 176
174 function stanza_mt:get_child_text(name, xmlns) 177 function stanza_mt:get_child_text(name, xmlns)
175 local tag = self:get_child(name, xmlns); 178 local tag = self:get_child(name, xmlns);
176 if tag then 179 if tag then
177 return tag:get_text(); 180 return tag:get_text();
181 end
182 return nil;
183 end
184
185 function stanza_mt:get_child_attr(name, xmlns, attr)
186 local tag = self:get_child(name, xmlns);
187 if tag then
188 return tag.attr[attr];
178 end 189 end
179 return nil; 190 return nil;
180 end 191 end
181 192
182 function stanza_mt:child_with_name(name) 193 function stanza_mt:child_with_name(name)
281 self = self:get_child(name, xmlns); 292 self = self:get_child(name, xmlns);
282 until not self 293 until not self
283 end 294 end
284 295
285 local function _clone(stanza, only_top) 296 local function _clone(stanza, only_top)
286 local attr, tags = {}, {}; 297 local attr = {};
287 for k,v in pairs(stanza.attr) do attr[k] = v; end 298 for k,v in pairs(stanza.attr) do attr[k] = v; end
288 local old_namespaces, namespaces = stanza.namespaces; 299 local old_namespaces, namespaces = stanza.namespaces;
289 if old_namespaces then 300 if old_namespaces then
290 namespaces = {}; 301 namespaces = {};
291 for k,v in pairs(old_namespaces) do namespaces[k] = v; end 302 for k,v in pairs(old_namespaces) do namespaces[k] = v; end
292 end 303 end
293 local new = { name = stanza.name, attr = attr, namespaces = namespaces, tags = tags }; 304 local tags, new;
305 if only_top then
306 tags = {};
307 new = { name = stanza.name, attr = attr, namespaces = namespaces, tags = tags };
308 else
309 tags = t_create(#stanza.tags, 0);
310 new = t_create(#stanza, 4);
311 new.name = stanza.name;
312 new.attr = attr;
313 new.namespaces = namespaces;
314 new.tags = tags;
315 end
316
317 setmetatable(new, stanza_mt);
294 if not only_top then 318 if not only_top then
295 for i=1,#stanza do 319 t_move(stanza, 1, #stanza, 1, new);
296 local child = stanza[i]; 320 t_move(stanza.tags, 1, #stanza.tags, 1, tags);
297 if child.name then 321 new:maptags(_clone);
298 child = _clone(child); 322 end
299 t_insert(tags, child); 323 return new;
300 end
301 t_insert(new, child);
302 end
303 end
304 return setmetatable(new, stanza_mt);
305 end 324 end
306 325
307 local function clone(stanza, only_top) 326 local function clone(stanza, only_top)
308 if not is_stanza(stanza) then 327 if not is_stanza(stanza) then
309 error("bad argument to clone: expected stanza, got "..type(stanza)); 328 error("bad argument to clone: expected stanza, got "..type(stanza));
383 if condition and text and extra_tag then 402 if condition and text and extra_tag then
384 break; 403 break;
385 end 404 end
386 end 405 end
387 return error_type, condition or "undefined-condition", text, extra_tag; 406 return error_type, condition or "undefined-condition", text, extra_tag;
407 end
408
409 function stanza_mt.add_error(stanza, error_type, condition, error_message, error_by)
410 local extra;
411 if type(error_type) == "table" then -- an util.error or similar object
412 if type(error_type.extra) == "table" then
413 extra = error_type.extra;
414 end
415 if type(error_type.context) == "table" and type(error_type.context.by) == "string" then error_by = error_type.context.by; end
416 error_type, condition, error_message = error_type.type, error_type.condition, error_type.text;
417 end
418 if stanza.attr.from == error_by then
419 error_by = nil;
420 end
421 stanza:tag("error", {type = error_type, by = error_by}) --COMPAT: Some day xmlns:stanzas goes here
422 :tag(condition, xmpp_stanzas_attr);
423 if extra and condition == "gone" and type(extra.uri) == "string" then
424 stanza:text(extra.uri);
425 end
426 stanza:up();
427 if error_message then stanza:text_tag("text", error_message, xmpp_stanzas_attr); end
428 if extra and is_stanza(extra.tag) then
429 stanza:add_child(extra.tag);
430 elseif extra and extra.namespace and extra.condition then
431 stanza:tag(extra.condition, { xmlns = extra.namespace }):up();
432 end
433 return stanza:up();
388 end 434 end
389 435
390 local function preserialize(stanza) 436 local function preserialize(stanza)
391 local s = { name = stanza.name, attr = stanza.attr }; 437 local s = { name = stanza.name, attr = stanza.attr };
392 for _, child in ipairs(stanza) do 438 for _, child in ipairs(stanza) do
459 id = orig.attr.id, 505 id = orig.attr.id,
460 type = ((orig.name == "iq" and "result") or orig.attr.type) 506 type = ((orig.name == "iq" and "result") or orig.attr.type)
461 }); 507 });
462 end 508 end
463 509
464 local xmpp_stanzas_attr = { xmlns = xmlns_stanzas };
465 local function error_reply(orig, error_type, condition, error_message, error_by) 510 local function error_reply(orig, error_type, condition, error_message, error_by)
466 if not is_stanza(orig) then 511 if not is_stanza(orig) then
467 error("bad argument to error_reply: expected stanza, got "..type(orig)); 512 error("bad argument to error_reply: expected stanza, got "..type(orig));
468 elseif orig.attr.type == "error" then 513 elseif orig.attr.type == "error" then
469 error("bad argument to error_reply: got stanza of type error which must not be replied to"); 514 error("bad argument to error_reply: got stanza of type error which must not be replied to");
470 end 515 end
471 local t = reply(orig); 516 local t = reply(orig);
472 t.attr.type = "error"; 517 t.attr.type = "error";
473 local extra; 518 t:add_error(error_type, condition, error_message, error_by);
474 if type(error_type) == "table" then -- an util.error or similar object 519 t.last_add = { t[1] }; -- ready to add application-specific errors
475 if type(error_type.extra) == "table" then 520 return t;
476 extra = error_type.extra;
477 end
478 if type(error_type.context) == "table" and type(error_type.context.by) == "string" then error_by = error_type.context.by; end
479 error_type, condition, error_message = error_type.type, error_type.condition, error_type.text;
480 end
481 if t.attr.from == error_by then
482 error_by = nil;
483 end
484 t:tag("error", {type = error_type, by = error_by}) --COMPAT: Some day xmlns:stanzas goes here
485 :tag(condition, xmpp_stanzas_attr);
486 if extra and condition == "gone" and type(extra.uri) == "string" then
487 t:text(extra.uri);
488 end
489 t:up();
490 if error_message then t:text_tag("text", error_message, xmpp_stanzas_attr); end
491 if extra and is_stanza(extra.tag) then
492 t:add_child(extra.tag);
493 elseif extra and extra.namespace and extra.condition then
494 t:tag(extra.condition, { xmlns = extra.namespace }):up();
495 end
496 return t; -- stanza ready for adding app-specific errors
497 end 521 end
498 522
499 local function presence(attr) 523 local function presence(attr)
500 return new_stanza("presence", attr); 524 return new_stanza("presence", attr);
501 end 525 end