2 -- XEP-0295: JSON Encodings for XMPP
7 local httpserver
= require
"net.httpserver";
8 local filters
= require
"util.filters"
9 local json
= require
"util.json"
11 local json_escapes
= {
12 ["\""] = "\\\"", ["\\"] = "\\\\", ["\b"] = "\\b", ["\f"] = "\\f",
13 ["\n"] = "\\n", ["\r"] = "\\r", ["\t"] = "\\t"};
15 local s_char
= string.char
;
18 if not json_escapes
[ch
] then json_escapes
[ch
] = ("\\u%.4X"):format(i
); end
22 local state_key_before
= 1;
23 local state_key_in
= 2;
24 local state_key_escape
= 3;
25 local state_key_after
= 4;
26 local state_val_before
= 5;
27 local state_val_in
= 6;
28 local state_val_escape
= 7;
29 local state_val_after
= 8;
31 local whitespace
= { [" "] = true, ["\n"] = true, ["\r"] = true, ["\t"] = true };
32 function json_decoder()
33 local state
= state_out
;
37 return function(input
)
38 for ch
in input
:gmatch(".") do
39 module
:log("debug", "%s | %d", ch
, state
)
41 if state
== state_out
then
42 if whitespace
[ch
] then
43 elseif ch
~= "{" then return nil, "{ expected";
44 else state
= state_key_before
end
45 elseif state
== state_key_before
then
46 if whitespace
[ch
] then
47 elseif ch
~= "'" and ch
~= "\"" then return nil, "\" expected";
48 else quote
= ch
; state
= state_key_in
; end
49 elseif state
== state_key_in
then
50 if ch
== quote
then state
= state_key_after
;
51 elseif ch
~= "s" then return nil, "invalid key, 's' expected"; -- only s as key allowed
52 else end -- ignore key
53 elseif state
== state_key_after
then
54 if whitespace
[ch
] then
55 elseif ch
~= ":" then return nil, ": expected";
56 else state
= state_val_before
; end
57 elseif state
== state_val_before
then
58 if whitespace
[ch
] then
59 elseif ch
~= "'" and ch
~= "\"" then return nil, "\" expected";
60 else quote
= ch
; state
= state_val_in
; end
61 elseif state
== state_val_in
then
62 if ch
== quote
then state
= state_val_after
;
63 elseif ch
== "\\" then state
= state_val_escape
;
65 elseif state
== state_val_after
then
66 if whitespace
[ch
] then
67 elseif ch
~= "}" then return nil, "} expected";
68 else state
= state_out
;
71 elseif state
== state_val_escape
then
74 module
:log("error", "Unhandled state: "..state
);
75 return nil, "Unhandled state in parser"
79 module
:log("debug", "%s", buffer
)
81 pcall(function() tmp
= json
.decode(buffer
); end);
82 if not tmp
then return nil, "Invalid JSON"; end
83 output
, buffer
= output
..tmp
.s
, "";
86 local _
= output
; output
= "";
91 function filter_hook(session
)
92 local determined
= false;
93 local is_json
= false;
94 local function in_filter(t
)
95 if not determined
then
96 is_json
= (t
:sub(1,1) == "{") and json_decoder();
100 local s
, err
= is_json(t
);
101 if not err
then return s
; end
102 session
:close("not-well-formed");
107 local function out_filter(t
)
109 return '{"s":"' .. t
:gsub(".", json_escapes
) .. '"}'; -- encode
113 filters
.add_filter(session
, "bytes/in", in_filter
, 100);
114 filters
.add_filter(session
, "bytes/out", out_filter
, 100);
117 function module
.load()
118 filters
.add_filter_hook(filter_hook
);
120 function module
.unload()
121 filters
.remove_filter_hook(filter_hook
);
124 function encode(data
)
125 if type(data
) == "string" then
126 data
= json
.encode({ s
= data
});
127 elseif type(data
) == "table" and data
.body
then
128 data
.body
= json
.encode({ s
= data
.body
});
129 data
.headers
["Content-Type"] = "application/json";
133 function handle_request(method
, body
, request
)
134 local mod_bosh
= modulemanager
.get_module("*", "bosh")
136 if body
and method
== "POST" then
137 pcall(function() body
= json
.decode(body
).s
; end);
139 local _send
= request
.send
;
140 function request
:send(data
) return _send(self
, encode(data
)); end
141 return encode(mod_bosh
.handle_request(method
, body
, request
));
143 return "<html><body>mod_bosh not loaded</body></html>";
146 local function setup()
147 local ports
= module
:get_option("jsonstreams_ports") or { 5280 };
148 httpserver
.new_from_config(ports
, handle_request
, { base
= "jsonstreams" });
150 if prosody
.start_time
then -- already started
153 prosody
.events
.add_handler("server-started", setup
);