plugins/jingle.lua

changeset 380
0891b4e27766
parent 341
a95890d86fe4
child 392
cdea6a28369e
equal deleted inserted replaced
379:d80d27234e38 380:0891b4e27766
14 14
15 function verse.plugins.jingle(stream) 15 function verse.plugins.jingle(stream)
16 stream:hook("ready", function () 16 stream:hook("ready", function ()
17 stream:add_disco_feature(xmlns_jingle); 17 stream:add_disco_feature(xmlns_jingle);
18 end, 10); 18 end, 10);
19 19
20 function stream:jingle(to) 20 function stream:jingle(to)
21 return verse.eventable(setmetatable(base or { 21 return verse.eventable(setmetatable(base or {
22 role = "initiator"; 22 role = "initiator";
23 peer = to; 23 peer = to;
24 sid = uuid_generate(); 24 sid = uuid_generate();
25 stream = stream; 25 stream = stream;
26 }, jingle_mt)); 26 }, jingle_mt));
27 end 27 end
28 28
29 function stream:register_jingle_transport(transport) 29 function stream:register_jingle_transport(transport)
30 -- transport is a function that receives a 30 -- transport is a function that receives a
31 -- <transport> element, and returns a connection 31 -- <transport> element, and returns a connection
32 -- We wait for 'connected' on that connection, 32 -- We wait for 'connected' on that connection,
33 -- and use :send() and 'incoming-raw'. 33 -- and use :send() and 'incoming-raw'.
34 end 34 end
35 35
36 function stream:register_jingle_content_type(content) 36 function stream:register_jingle_content_type(content)
37 -- Call content() for every 'incoming-raw'? 37 -- Call content() for every 'incoming-raw'?
38 -- I think content() returns the object we return 38 -- I think content() returns the object we return
39 -- on jingle:accept() 39 -- on jingle:accept()
40 end 40 end
41 41
42 local function handle_incoming_jingle(stanza) 42 local function handle_incoming_jingle(stanza)
43 local jingle_tag = stanza:get_child("jingle", xmlns_jingle); 43 local jingle_tag = stanza:get_child("jingle", xmlns_jingle);
44 local sid = jingle_tag.attr.sid; 44 local sid = jingle_tag.attr.sid;
45 local action = jingle_tag.attr.action; 45 local action = jingle_tag.attr.action;
46 local result = stream:event("jingle/"..sid, stanza); 46 local result = stream:event("jingle/"..sid, stanza);
55 local reply = verse.error_reply(stanza, "cancel", "item-not-found") 55 local reply = verse.error_reply(stanza, "cancel", "item-not-found")
56 :tag("unknown-session", { xmlns = xmlns_jingle_errors }):up(); 56 :tag("unknown-session", { xmlns = xmlns_jingle_errors }):up();
57 stream:send(reply); 57 stream:send(reply);
58 return; 58 return;
59 end 59 end
60 60
61 -- Ok, session-initiate, new session 61 -- Ok, session-initiate, new session
62 62
63 -- Create new Jingle object 63 -- Create new Jingle object
64 local sid = jingle_tag.attr.sid; 64 local sid = jingle_tag.attr.sid;
65 65
66 local jingle = verse.eventable{ 66 local jingle = verse.eventable{
67 role = "receiver"; 67 role = "receiver";
68 peer = stanza.attr.from; 68 peer = stanza.attr.from;
69 sid = sid; 69 sid = sid;
70 stream = stream; 70 stream = stream;
71 }; 71 };
72 72
73 setmetatable(jingle, jingle_mt); 73 setmetatable(jingle, jingle_mt);
74 74
75 local content_tag; 75 local content_tag;
76 local content, transport; 76 local content, transport;
77 for tag in jingle_tag:childtags() do 77 for tag in jingle_tag:childtags() do
78 if tag.name == "content" and tag.attr.xmlns == xmlns_jingle then 78 if tag.name == "content" and tag.attr.xmlns == xmlns_jingle then
79 local description_tag = tag:child_with_name("description"); 79 local description_tag = tag:child_with_name("description");
82 local desc_handler = stream:event("jingle/content/"..description_xmlns, jingle, description_tag); 82 local desc_handler = stream:event("jingle/content/"..description_xmlns, jingle, description_tag);
83 if desc_handler then 83 if desc_handler then
84 content = desc_handler; 84 content = desc_handler;
85 end 85 end
86 end 86 end
87 87
88 local transport_tag = tag:child_with_name("transport"); 88 local transport_tag = tag:child_with_name("transport");
89 local transport_xmlns = transport_tag.attr.xmlns; 89 local transport_xmlns = transport_tag.attr.xmlns;
90 90
91 transport = stream:event("jingle/transport/"..transport_xmlns, jingle, transport_tag); 91 transport = stream:event("jingle/transport/"..transport_xmlns, jingle, transport_tag);
92 if content and transport then 92 if content and transport then
93 content_tag = tag; 93 content_tag = tag;
94 break; 94 break;
95 end 95 end
98 if not content then 98 if not content then
99 -- FIXME: Fail, no content 99 -- FIXME: Fail, no content
100 stream:send(verse.error_reply(stanza, "cancel", "feature-not-implemented", "The specified content is not supported")); 100 stream:send(verse.error_reply(stanza, "cancel", "feature-not-implemented", "The specified content is not supported"));
101 return true; 101 return true;
102 end 102 end
103 103
104 if not transport then 104 if not transport then
105 -- FIXME: Refuse session, no transport 105 -- FIXME: Refuse session, no transport
106 stream:send(verse.error_reply(stanza, "cancel", "feature-not-implemented", "The specified transport is not supported")); 106 stream:send(verse.error_reply(stanza, "cancel", "feature-not-implemented", "The specified transport is not supported"));
107 return true; 107 return true;
108 end 108 end
109 109
110 stream:send(verse.reply(stanza)); 110 stream:send(verse.reply(stanza));
111 111
112 jingle.content_tag = content_tag; 112 jingle.content_tag = content_tag;
113 jingle.creator, jingle.name = content_tag.attr.creator, content_tag.attr.name; 113 jingle.creator, jingle.name = content_tag.attr.creator, content_tag.attr.name;
114 jingle.content, jingle.transport = content, transport; 114 jingle.content, jingle.transport = content, transport;
115 115
116 function jingle:decline() 116 function jingle:decline()
117 -- FIXME: Decline session 117 -- FIXME: Decline session
118 end 118 end
119 119
120 stream:hook("jingle/"..sid, function (stanza) 120 stream:hook("jingle/"..sid, function (stanza)
121 if stanza.attr.from ~= jingle.peer then 121 if stanza.attr.from ~= jingle.peer then
122 return false; 122 return false;
123 end 123 end
124 local jingle_tag = stanza:get_child("jingle", xmlns_jingle); 124 local jingle_tag = stanza:get_child("jingle", xmlns_jingle);
125 return jingle:handle_command(jingle_tag); 125 return jingle:handle_command(jingle_tag);
126 end); 126 end);
127 127
128 stream:event("jingle", jingle); 128 stream:event("jingle", jingle);
129 return true; 129 return true;
130 end 130 end
131 131
132 function jingle_mt:handle_command(jingle_tag) 132 function jingle_mt:handle_command(jingle_tag)
133 local action = jingle_tag.attr.action; 133 local action = jingle_tag.attr.action;
134 stream:debug("Handling Jingle command: %s", action); 134 stream:debug("Handling Jingle command: %s", action);
135 if action == "session-terminate" then 135 if action == "session-terminate" then
136 self:destroy(); 136 self:destroy();
164 self.stream:send(stanza); 164 self.stream:send(stanza);
165 else 165 else
166 self.stream:send_iq(stanza, callback); 166 self.stream:send_iq(stanza, callback);
167 end 167 end
168 end 168 end
169 169
170 function jingle_mt:accept(options) 170 function jingle_mt:accept(options)
171 local accept_stanza = verse.iq({ to = self.peer, type = "set" }) 171 local accept_stanza = verse.iq({ to = self.peer, type = "set" })
172 :tag("jingle", { 172 :tag("jingle", {
173 xmlns = xmlns_jingle, 173 xmlns = xmlns_jingle,
174 sid = self.sid, 174 sid = self.sid,
175 action = "session-accept", 175 action = "session-accept",
176 responder = stream.jid, 176 responder = stream.jid,
177 }) 177 })
178 :tag("content", { creator = self.creator, name = self.name }); 178 :tag("content", { creator = self.creator, name = self.name });
179 179
180 local content_accept_tag = self.content:generate_accept(self.content_tag:child_with_name("description"), options); 180 local content_accept_tag = self.content:generate_accept(self.content_tag:child_with_name("description"), options);
181 accept_stanza:add_child(content_accept_tag); 181 accept_stanza:add_child(content_accept_tag);
182 182
183 local transport_accept_tag = self.transport:generate_accept(self.content_tag:child_with_name("transport"), options); 183 local transport_accept_tag = self.transport:generate_accept(self.content_tag:child_with_name("transport"), options);
184 accept_stanza:add_child(transport_accept_tag); 184 accept_stanza:add_child(transport_accept_tag);
185 185
186 local jingle = self; 186 local jingle = self;
187 stream:send_iq(accept_stanza, function (result) 187 stream:send_iq(accept_stanza, function (result)
188 if result.attr.type == "error" then 188 if result.attr.type == "error" then
189 local type, condition, text = result:get_error(); 189 local type, condition, text = result:get_error();
190 stream:error("session-accept rejected: %s", condition); -- FIXME: Notify 190 stream:error("session-accept rejected: %s", condition); -- FIXME: Notify
195 jingle.state = "active"; 195 jingle.state = "active";
196 jingle:event("connected", conn); 196 jingle:event("connected", conn);
197 end); 197 end);
198 end); 198 end);
199 end 199 end
200 200
201 201
202 stream:hook("iq/"..xmlns_jingle, handle_incoming_jingle); 202 stream:hook("iq/"..xmlns_jingle, handle_incoming_jingle);
203 return true; 203 return true;
204 end 204 end
205 205
206 function jingle_mt:offer(name, content) 206 function jingle_mt:offer(name, content)
207 local session_initiate = verse.iq({ to = self.peer, type = "set" }) 207 local session_initiate = verse.iq({ to = self.peer, type = "set" })
208 :tag("jingle", { xmlns = xmlns_jingle, action = "session-initiate", 208 :tag("jingle", { xmlns = xmlns_jingle, action = "session-initiate",
209 initiator = self.stream.jid, sid = self.sid }); 209 initiator = self.stream.jid, sid = self.sid });
210 210
211 -- Content tag 211 -- Content tag
212 session_initiate:tag("content", { creator = self.role, name = name }); 212 session_initiate:tag("content", { creator = self.role, name = name });
213 213
214 -- Need description element from someone who can turn 'content' into XML 214 -- Need description element from someone who can turn 'content' into XML
215 local description = self.stream:event("jingle/describe/"..name, content); 215 local description = self.stream:event("jingle/describe/"..name, content);
216 216
217 if not description then 217 if not description then
218 return false, "Unknown content type"; 218 return false, "Unknown content type";
219 end 219 end
220 220
221 session_initiate:add_child(description); 221 session_initiate:add_child(description);
222 222
223 -- FIXME: Sort transports by 1) recipient caps 2) priority (SOCKS vs IBB, etc.) 223 -- FIXME: Sort transports by 1) recipient caps 2) priority (SOCKS vs IBB, etc.)
224 -- Fixed to s5b in the meantime 224 -- Fixed to s5b in the meantime
225 local transport = self.stream:event("jingle/transport/".."urn:xmpp:jingle:transports:s5b:1", self); 225 local transport = self.stream:event("jingle/transport/".."urn:xmpp:jingle:transports:s5b:1", self);
226 self.transport = transport; 226 self.transport = transport;
227 227
228 session_initiate:add_child(transport:generate_initiate()); 228 session_initiate:add_child(transport:generate_initiate());
229 229
230 self.stream:debug("Hooking %s", "jingle/"..self.sid); 230 self.stream:debug("Hooking %s", "jingle/"..self.sid);
231 self.stream:hook("jingle/"..self.sid, function (stanza) 231 self.stream:hook("jingle/"..self.sid, function (stanza)
232 if stanza.attr.from ~= self.peer then 232 if stanza.attr.from ~= self.peer then
233 return false; 233 return false;
234 end 234 end
235 local jingle_tag = stanza:get_child("jingle", xmlns_jingle); 235 local jingle_tag = stanza:get_child("jingle", xmlns_jingle);
236 return self:handle_command(jingle_tag) 236 return self:handle_command(jingle_tag)
237 end); 237 end);
238 238
239 self.stream:send_iq(session_initiate, function (result) 239 self.stream:send_iq(session_initiate, function (result)
240 if result.attr.type == "error" then 240 if result.attr.type == "error" then
241 self.state = "terminated"; 241 self.state = "terminated";
242 local type, condition, text = result:get_error(); 242 local type, condition, text = result:get_error();
243 return self:event("error", { type = type, condition = condition, text = text }); 243 return self:event("error", { type = type, condition = condition, text = text });

mercurial