xmlstream.lua

1
-- Prosody IM
2
-- Copyright (C) 2008-2010 Matthew Wild
3
-- Copyright (C) 2008-2010 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
local lxp = require "lxp";
11
local st = require "stanza";
12
 
13
local tostring = tostring;
14
local t_insert = table.insert;
15
local t_concat = table.concat;
16
 
17
local error = error;
18
 
19
module "xmlstream"
20
 
21
local new_parser = lxp.new;
22
 
23
local ns_prefixes = {
24
	["http://www.w3.org/XML/1998/namespace"] = "xml";
25
};
26
 
27
local xmlns_streams = "http://etherx.jabber.org/streams";
28
 
29
local ns_separator = "\1";
30
local ns_pattern = "^([^"..ns_separator.."]*)"..ns_separator.."?(.*)$";
31
 
32
function new_sax_handlers(session, stream_callbacks)
33
	local xml_handlers = {};
34
	
35
	local log = session.log;
36
	
37
	local cb_streamopened = stream_callbacks.streamopened;
38
	local cb_streamclosed = stream_callbacks.streamclosed;
39
	local cb_error = stream_callbacks.error or function(session, e) error("XML stream error: "..tostring(e)); end;
40
	local cb_handlestanza = stream_callbacks.handlestanza;
41
	
42
	local stream_ns = stream_callbacks.stream_ns or xmlns_streams;
43
	local stream_tag = stream_ns..ns_separator..(stream_callbacks.stream_tag or "stream");
44
	local stream_error_tag = stream_ns..ns_separator..(stream_callbacks.error_tag or "error");
45
	
46
	local stream_default_ns = stream_callbacks.default_ns;
47
	
48
	local chardata, stanza = {};
49
	function xml_handlers:StartElement(tagname, attr)
50
		if stanza and #chardata > 0 then
51
			-- We have some character data in the buffer
52
			stanza:text(t_concat(chardata));
53
			chardata = {};
54
		end
55
		local curr_ns,name = tagname:match(ns_pattern);
56
		if name == "" then
57
			curr_ns, name = "", curr_ns;
58
		end
59
 
60
		if curr_ns ~= stream_default_ns then
61
			attr.xmlns = curr_ns;
62
		end
63
		
64
		-- FIXME !!!!!
65
		for i=1,#attr do
66
			local k = attr[i];
67
			attr[i] = nil;
68
			local ns, nm = k:match(ns_pattern);
69
			if nm ~= "" then
70
				ns = ns_prefixes[ns]; 
71
				if ns then 
72
					attr[ns..":"..nm] = attr[k];
73
					attr[k] = nil;
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 cb_streamopened then
81
					cb_streamopened(session, attr, name);
82
				end
83
				return;
84
			end
85
			stanza = st.stanza(name, attr);
86
		else -- we are inside a stanza, so add a tag
87
			attr.xmlns = nil;
88
			if curr_ns ~= stream_default_ns then
89
				attr.xmlns = curr_ns;
90
			end
91
			stanza:tag(name, attr);
92
		end
93
	end
94
	function xml_handlers:CharacterData(data)
95
		if stanza then
96
			t_insert(chardata, data);
97
		end
98
	end
99
	function xml_handlers:EndElement(tagname)
100
		if stanza then
101
			if #chardata > 0 then
102
				-- We have some character data in the buffer
103
				stanza:text(t_concat(chardata));
104
				chardata = {};
105
			end
106
			-- Complete stanza
107
			local last_add = stanza.last_add;
108
			if not(last_add) or #last_add == 0 then
109
				if tagname ~= stream_error_tag then
110
					cb_handlestanza(session, stanza);
111
				else
112
					cb_error(session, "stream-error", stanza);
113
				end
114
				stanza = nil;
115
			else
116
				stanza:up();
117
			end
118
		else
119
			if cb_streamclosed then
120
				cb_streamclosed(session);
121
			end
122
			stanza, chardata = nil, {};
123
		end
124
	end
125
	
126
	local function reset()
127
		stanza, chardata = nil, {};
128
	end
129
	
130
	return xml_handlers, { reset = reset };
131
end
132
 
133
function new(session, stream_callbacks)
134
	local handlers, meta = new_sax_handlers(session, stream_callbacks);
135
	local parser = new_parser(handlers, ns_separator);
136
	local parse = parser.parse;
137
 
138
	return {
139
		reset = function ()
140
			parser = new_parser(handlers, ns_separator);
141
			parse = parser.parse;
142
			meta.reset();
143
		end,
144
		feed = function (self, data)
145
			return parse(parser, data);
146
		end
147
	};
148
end
149
 
150
return _M;