Software /
code /
verse
Comparison
plugins/jingle_ibb.lua @ 219:ce8ed17710cb
plugins.jingle_ibb: In-Band Bytestreams, initial commit.
author | Kim Alvefur <zash@zash.se> |
---|---|
date | Fri, 30 Sep 2011 05:17:06 +0200 |
child | 250:a5ac643a7fd6 |
comparison
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 |