Merge 0.10->trunk
[prosody.git] / util / xml.lua
1
2 local st = require "util.stanza";
3 local lxp = require "lxp";
4 local t_insert = table.insert;
5 local t_remove = table.remove;
6
7 local _ENV = nil;
8
9 local parse_xml = (function()
10         local ns_prefixes = {
11                 ["http://www.w3.org/XML/1998/namespace"] = "xml";
12         };
13         local ns_separator = "\1";
14         local ns_pattern = "^([^"..ns_separator.."]*)"..ns_separator.."?(.*)$";
15         return function(xml)
16                 --luacheck: ignore 212/self
17                 local handler = {};
18                 local stanza = st.stanza("root");
19                 local namespaces = {};
20                 local prefixes = {};
21                 function handler:StartNamespaceDecl(prefix, url)
22                         if prefix ~= nil then
23                                 t_insert(namespaces, url);
24                                 t_insert(prefixes, prefix);
25                         end
26                 end
27                 function handler:EndNamespaceDecl(prefix)
28                         if prefix ~= nil then
29                                 -- we depend on each StartNamespaceDecl having a paired EndNamespaceDecl
30                                 t_remove(namespaces);
31                                 t_remove(prefixes);
32                         end
33                 end
34                 function handler:StartElement(tagname, attr)
35                         local curr_ns,name = tagname:match(ns_pattern);
36                         if name == "" then
37                                 curr_ns, name = "", curr_ns;
38                         end
39                         if curr_ns ~= "" then
40                                 attr.xmlns = curr_ns;
41                         end
42                         for i=1,#attr do
43                                 local k = attr[i];
44                                 attr[i] = nil;
45                                 local ns, nm = k:match(ns_pattern);
46                                 if nm ~= "" then
47                                         ns = ns_prefixes[ns];
48                                         if ns then
49                                                 attr[ns..":"..nm] = attr[k];
50                                                 attr[k] = nil;
51                                         end
52                                 end
53                         end
54                         local n = {}
55                         for i=1,#namespaces do
56                                 n[prefixes[i]] = namespaces[i];
57                         end
58                         stanza:tag(name, attr, n);
59                 end
60                 function handler:CharacterData(data)
61                         stanza:text(data);
62                 end
63                 function handler:EndElement()
64                         stanza:up();
65                 end
66                 local parser = lxp.new(handler, "\1");
67                 local ok, err, line, col = parser:parse(xml);
68                 if ok then ok, err, line, col = parser:parse(); end
69                 --parser:close();
70                 if ok then
71                         return stanza.tags[1];
72                 else
73                         return ok, err.." (line "..line..", col "..col..")";
74                 end
75         end;
76 end)();
77
78 return {
79         parse = parse_xml;
80 };