plugins/jingle_ibb.lua

changeset 219
ce8ed17710cb
child 250
a5ac643a7fd6
equal deleted inserted replaced
218:39af184385cd 219:ce8ed17710cb
1 local xmlns_jingle_ibb = "urn:xmpp:jingle:transports:ibb:1";
2 local xmlns_ibb = "http://jabber.org/protocol/ibb";
3 local base64 = require "util.encodings".base64;
4 assert(base64.encode("This is a test.") == "VGhpcyBpcyBhIHRlc3Qu", "Base64 encoding failed");
5 assert(base64.decode("VGhpcyBpcyBhIHRlc3Qu") == "This is a test.", "Base64 decoding failed");
6 local t_concat = table.concat
7 local uuid_generate = require "util.uuid".generate;
8
9 local ibb_conn = {};
10 local ibb_conn_mt = { __index = ibb_conn };
11
12 function new_ibb(stream)
13 local conn = setmetatable({ stream = stream }, ibb_conn_mt)
14 conn = verse.eventable(conn);
15 return conn;
16 end
17
18 function ibb_conn:initiate(peer, sid, stanza)
19 self.block = 2048; -- ignored for now
20 self.stanza = stanza or 'iq';
21 self.peer = peer;
22 self.sid = sid or tostring(self):match("%x+$");
23 self.iseq = 0;
24 self.oseq = 0;
25 local feeder = function(stanza)
26 return self:feed(stanza)
27 end
28 self.feeder = feeder;
29 print("Hooking incomming IQs");
30 local stream = self.stream;
31 stream:hook("iq/".. xmlns_ibb, feeder)
32 if stanza == "message" then
33 stream:hook("message", feeder)
34 end
35 end
36
37 function ibb_conn:open(callback)
38 self.stream:send_iq(verse.iq{ to = self.peer, type = "set" }
39 :tag("open", {
40 xmlns = xmlns_ibb,
41 ["block-size"] = self.block,
42 sid = self.sid,
43 stanza = self.stanza
44 })
45 , function(reply)
46 if callback then
47 if reply.attr.type ~= "error" then
48 callback(true)
49 else
50 callback(false, reply:get_error())
51 end
52 end
53 end);
54 end
55
56 function ibb_conn:send(data)
57 local stanza = self.stanza;
58 local st;
59 if stanza == "iq" then
60 st = verse.iq{ type = "set", to = self.peer }
61 elseif stanza == "message" then
62 st = verse.message{ to = self.peer }
63 end
64
65 local seq = self.oseq;
66 self.oseq = seq + 1;
67
68 st:tag("data", { xmlns = xmlns_ibb, sid = self.sid, seq = seq })
69 :text(base64.encode(data));
70
71 if stanza == "iq" then
72 self.stream:send_iq(st, function(reply)
73 self:event(reply.attr.type == "result" and "drained" or "error");
74 end)
75 else
76 stream:send(st)
77 self:event("drained");
78 end
79 end
80
81 function ibb_conn:feed(stanza)
82 if stanza.attr.from ~= self.peer then return end
83 local child = stanza[1];
84 if child.attr.sid ~= self.sid then return end
85 local ok;
86 if child.name == "open" then
87 self:event("connected");
88 self.stream:send(verse.reply(stanza))
89 return true
90 elseif child.name == "data" then
91 local bdata = stanza:get_child_text("data", xmlns_ibb);
92 local seq = tonumber(child.attr.seq);
93 local expected_seq = self.iseq;
94 if bdata and seq then
95 if seq ~= expected_seq then
96 self.stream:send(verse.error_reply(stanza, "cancel", "not-acceptable", "Wrong sequence. Packet lost?"))
97 self:close();
98 self:event("error");
99 return true;
100 end
101 self.iseq = seq + 1;
102 local data = base64.decode(bdata);
103 if self.stanza == "iq" then
104 self.stream:send(verse.reply(stanza))
105 end
106 self:event("incoming-raw", data);
107 return true;
108 end
109 elseif child.name == "close" then
110 self.stream:send(verse.reply(stanza))
111 self:close();
112 return true
113 end
114 end
115
116 --[[ FIXME some day
117 function ibb_conn:receive(patt)
118 -- is this even used?
119 print("ibb_conn:receive("..tostring(patt)..")");
120 assert(patt == "*a" or tonumber(patt));
121 local data = t_concat(self.ibuffer):sub(self.pos, tonumber(patt) or nil);
122 self.pos = self.pos + #data;
123 return data
124 end
125
126 function ibb_conn:dirty()
127 print("ibb_conn:dirty()");
128 return false -- ????
129 end
130 function ibb_conn:getfd()
131 return 0
132 end
133 function ibb_conn:settimeout(n)
134 -- ignore?
135 end
136 -]]
137
138 function ibb_conn:close()
139 self.stream:unhook("iq/".. xmlns_ibb, self.feeder)
140 self:event("disconnected");
141 end
142
143 function verse.plugins.jingle_ibb(stream)
144 stream:hook("ready", function ()
145 stream:add_disco_feature(xmlns_jingle_ibb);
146 end, 10);
147
148 local ibb = {};
149
150 function ibb:_setup()
151 local conn = new_ibb(self.stream);
152 conn.sid = self.sid or conn.sid;
153 conn.stanza = self.stanza or conn.stanza;
154 conn.block = self.block or conn.block;
155 conn:initiate(self.peer, self.sid, self.stanza);
156 self.conn = conn;
157 end
158 function ibb:generate_initiate()
159 print("ibb:generate_initiate() as ".. self.role);
160 local sid = uuid_generate();
161 self.sid = sid;
162 self.stanza = 'iq';
163 self.block = 2048;
164 local transport = verse.stanza("transport", { xmlns = xmlns_jingle_ibb,
165 sid = self.sid, stanza = self.stanza, ["block-size"] = self.block });
166 return transport;
167 end
168 function ibb:generate_accept(initiate_transport)
169 print("ibb:generate_accept() as ".. self.role);
170 local attr = initiate_transport.attr;
171 self.sid = attr.sid or self.sid;
172 self.stanza = attr.stanza or self.stanza;
173 self.block = attr["block-size"] or self.block;
174 self:_setup();
175 return initiate_transport;
176 end
177 function ibb:connect(callback)
178 if not self.conn then
179 self:_setup();
180 end
181 local conn = self.conn;
182 print("ibb:connect() as ".. self.role);
183 if self.role == "initiator" then
184 conn:open(function(ok, ...)
185 assert(ok, table.concat({...}, ", "));
186 callback(conn);
187 end);
188 else
189 callback(conn);
190 end
191 end
192 function ibb:info_received(jingle_tag)
193 print("ibb:info_received()");
194 -- TODO, what exactly?
195 end
196 function ibb:disconnect()
197 if self.conn then
198 self.conn:close()
199 end
200 end
201 function ibb:handle_accepted(jingle_tag) end
202
203 local ibb_mt = { __index = ibb };
204 stream:hook("jingle/transport/"..xmlns_jingle_ibb, function (jingle)
205 return setmetatable({
206 role = jingle.role,
207 peer = jingle.peer,
208 stream = jingle.stream,
209 jingle = jingle,
210 }, ibb_mt);
211 end);
212 end

mercurial