Fri, 17 Sep 2010 14:47:45 +0100
demo_string.lua: Example opening a feed from a string
0 | 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 "xmppstream" | |
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 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 | else -- we are inside a stanza, so add a tag | |
96 | attr.xmlns = nil; | |
97 | if curr_ns ~= stream_default_ns then | |
98 | attr.xmlns = curr_ns; | |
99 | end | |
100 | stanza:tag(name, attr); | |
101 | end | |
102 | end | |
103 | function xml_handlers:CharacterData(data) | |
104 | if stanza then | |
105 | t_insert(chardata, data); | |
106 | end | |
107 | end | |
108 | function xml_handlers:EndElement(tagname) | |
109 | if stanza then | |
110 | if #chardata > 0 then | |
111 | -- We have some character data in the buffer | |
112 | stanza:text(t_concat(chardata)); | |
113 | chardata = {}; | |
114 | end | |
115 | -- Complete stanza | |
116 | if #stanza.last_add == 0 then | |
117 | if tagname ~= stream_error_tag then | |
118 | cb_handlestanza(session, stanza); | |
119 | else | |
120 | cb_error(session, "stream-error", stanza); | |
121 | end | |
122 | stanza = nil; | |
123 | else | |
124 | stanza:up(); | |
125 | end | |
126 | else | |
127 | if tagname == stream_tag then | |
128 | if cb_streamclosed then | |
129 | cb_streamclosed(session); | |
130 | end | |
131 | else | |
132 | local curr_ns,name = tagname:match(ns_pattern); | |
133 | if name == "" then | |
134 | curr_ns, name = "", curr_ns; | |
135 | end | |
136 | cb_error(session, "parse-error", "unexpected-element-close", name); | |
137 | end | |
138 | stanza, chardata = nil, {}; | |
139 | end | |
140 | end | |
141 | ||
142 | local function reset() | |
143 | stanza, chardata = nil, {}; | |
144 | end | |
145 | ||
146 | return xml_handlers, { reset = reset }; | |
147 | end | |
148 | ||
149 | function new(session, stream_callbacks) | |
150 | local handlers, meta = new_sax_handlers(session, stream_callbacks); | |
151 | local parser = new_parser(handlers, ns_separator); | |
152 | local parse = parser.parse; | |
153 | ||
154 | return { | |
155 | reset = function () | |
156 | parser = new_parser(handlers, ns_separator); | |
157 | parse = parser.parse; | |
158 | meta.reset(); | |
159 | end, | |
160 | feed = function (self, data) | |
161 | return parse(parser, data); | |
162 | end | |
163 | }; | |
164 | end | |
165 | ||
166 | return _M; |