ed6f6a482b2122407f71867e3f37a698378cdf98
[prosody.git] / core / xmlhandlers.lua
1 -- Prosody IM v0.4
2 -- Copyright (C) 2008-2009 Matthew Wild
3 -- Copyright (C) 2008-2009 Waqas Hussain
4 -- 
5 -- This project is MIT/X11 licensed. Please see the
6 -- COPYING file in the source package for more information.
7 --
8
9
10
11 require "util.stanza"
12
13 local st = stanza;
14 local tostring = tostring;
15 local pairs = pairs;
16 local ipairs = ipairs;
17 local type = type;
18 local t_insert = table.insert;
19 local t_concat = table.concat;
20
21 local default_log = require "util.logger".init("xmlhandlers");
22
23 local error = error;
24
25 module "xmlhandlers"
26
27 local ns_prefixes = {
28                                                 ["http://www.w3.org/XML/1998/namespace"] = "xml";
29                                 }
30
31 function init_xmlhandlers(session, stream_callbacks)
32                 local ns_stack = { "" };
33                 local curr_ns, name = "";
34                 local curr_tag;
35                 local chardata = {};
36                 local xml_handlers = {};
37                 local log = session.log or default_log;
38                 
39                 local cb_streamopened = stream_callbacks.streamopened;
40                 local cb_streamclosed = stream_callbacks.streamclosed;
41                 local cb_error = stream_callbacks.error or function (session, e) error("XML stream error: "..tostring(e)); end;
42                 local cb_handlestanza = stream_callbacks.handlestanza;
43                 
44                 local stream_tag = stream_callbacks.stream_tag;
45                 local stream_default_ns = stream_callbacks.default_ns;
46                 
47                 local stanza
48                 function xml_handlers:StartElement(tagname, attr)
49                         if stanza and #chardata > 0 then
50                                 -- We have some character data in the buffer
51                                 stanza:text(t_concat(chardata));
52                                 chardata = {};
53                         end
54                         local curr_ns,name = tagname:match("^(.-)|?([^%|]-)$");
55                         if not name then
56                                 curr_ns, name = "", curr_ns;
57                         end
58
59                         if curr_ns ~= stream_default_ns then
60                                 attr.xmlns = curr_ns;
61                         end
62                         
63                         -- FIXME !!!!!
64                         for i, k in ipairs(attr) do
65                                 if type(k) == "string" then
66                                         local ns, nm = k:match("^([^|]+)|?([^|]-)$")
67                                         if ns and nm then
68                                                 ns = ns_prefixes[ns]; 
69                                                 if ns then 
70                                                         attr[ns..":"..nm] = attr[k];
71                                                         attr[i] = ns..":"..nm;
72                                                         attr[k] = nil;
73                                                 end
74                                         end
75                                 end
76                         end
77                         
78                         if not stanza then --if we are not currently inside a stanza
79                                 if session.notopen then
80                                         if tagname == stream_tag then
81                                                 if cb_streamopened then
82                                                         cb_streamopened(session, attr);
83                                                 end
84                                         else
85                                                 -- Garbage before stream?
86                                                 cb_error(session, "no-stream");
87                                         end
88                                         return;
89                                 end
90                                 if curr_ns == "jabber:client" and name ~= "iq" and name ~= "presence" and name ~= "message" then
91                                         cb_error(session, "invalid-top-level-element");
92                                 end
93                                 
94                                 stanza = st.stanza(name, attr);
95                                 curr_tag = stanza;
96                         else -- we are inside a stanza, so add a tag
97                                 attr.xmlns = nil;
98                                 if curr_ns ~= stream_default_ns then
99                                         attr.xmlns = curr_ns;
100                                 end
101                                 stanza:tag(name, attr);
102                         end
103                 end
104                 function xml_handlers:CharacterData(data)
105                         if stanza then
106                                 t_insert(chardata, data);
107                         end
108                 end
109                 function xml_handlers:EndElement(tagname)
110                         curr_ns,name = tagname:match("^(.-)|?([^%|]-)$");
111                         if not name then
112                                 curr_ns, name = "", curr_ns;
113                         end
114                         if (not stanza) or (#stanza.last_add > 0 and name ~= stanza.last_add[#stanza.last_add].name) then 
115                                 if tagname == stream_tag then
116                                         if cb_streamclosed then
117                                                 cb_streamclosed(session);
118                                         end
119                                         return;
120                                 elseif name == "error" then
121                                         cb_error(session, "stream-error", stanza);
122                                 else
123                                         cb_error(session, "parse-error", "unexpected-element-close", name);
124                                 end
125                         end
126                         if stanza then
127                                 if #chardata > 0 then
128                                         -- We have some character data in the buffer
129                                         stanza:text(t_concat(chardata));
130                                         chardata = {};
131                                 end
132                                 -- Complete stanza
133                                 if #stanza.last_add == 0 then
134                                         cb_handlestanza(session, stanza);
135                                         stanza = nil;
136                                 else
137                                         stanza:up();
138                                 end
139                         end
140                 end
141         return xml_handlers;
142 end
143
144 return init_xmlhandlers;