comparison sat/plugins/plugin_xep_0260.py @ 2624:56f94936df1e

code style reformatting using black
author Goffi <goffi@goffi.org>
date Wed, 27 Jun 2018 20:14:46 +0200
parents 26edcf3a30eb
children 378188abe941
comparison
equal deleted inserted replaced
2623:49533de4540b 2624:56f94936df1e
18 # along with this program. If not, see <http://www.gnu.org/licenses/>. 18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
19 19
20 from sat.core.i18n import _ 20 from sat.core.i18n import _
21 from sat.core.constants import Const as C 21 from sat.core.constants import Const as C
22 from sat.core.log import getLogger 22 from sat.core.log import getLogger
23
23 log = getLogger(__name__) 24 log = getLogger(__name__)
24 from sat.core import exceptions 25 from sat.core import exceptions
25 from wokkel import disco, iwokkel 26 from wokkel import disco, iwokkel
26 from zope.interface import implements 27 from zope.interface import implements
27 from twisted.words.xish import domish 28 from twisted.words.xish import domish
33 from twisted.words.protocols.xmlstream import XMPPHandler 34 from twisted.words.protocols.xmlstream import XMPPHandler
34 except ImportError: 35 except ImportError:
35 from wokkel.subprotocols import XMPPHandler 36 from wokkel.subprotocols import XMPPHandler
36 37
37 38
38 NS_JINGLE_S5B = 'urn:xmpp:jingle:transports:s5b:1' 39 NS_JINGLE_S5B = "urn:xmpp:jingle:transports:s5b:1"
39 40
40 PLUGIN_INFO = { 41 PLUGIN_INFO = {
41 C.PI_NAME: "Jingle SOCKS5 Bytestreams", 42 C.PI_NAME: "Jingle SOCKS5 Bytestreams",
42 C.PI_IMPORT_NAME: "XEP-0260", 43 C.PI_IMPORT_NAME: "XEP-0260",
43 C.PI_TYPE: "XEP", 44 C.PI_TYPE: "XEP",
44 C.PI_MODES: C.PLUG_MODE_BOTH, 45 C.PI_MODES: C.PLUG_MODE_BOTH,
45 C.PI_PROTOCOLS: ["XEP-0260"], 46 C.PI_PROTOCOLS: ["XEP-0260"],
46 C.PI_DEPENDENCIES: ["XEP-0166", "XEP-0065"], 47 C.PI_DEPENDENCIES: ["XEP-0166", "XEP-0065"],
47 C.PI_RECOMMENDATIONS: ["XEP-0261"], # needed for fallback 48 C.PI_RECOMMENDATIONS: ["XEP-0261"], # needed for fallback
48 C.PI_MAIN: "XEP_0260", 49 C.PI_MAIN: "XEP_0260",
49 C.PI_HANDLER: "yes", 50 C.PI_HANDLER: "yes",
50 C.PI_DESCRIPTION: _("""Implementation of Jingle SOCKS5 Bytestreams""") 51 C.PI_DESCRIPTION: _("""Implementation of Jingle SOCKS5 Bytestreams"""),
51 } 52 }
52 53
53 54
54 class ProxyError(Exception): 55 class ProxyError(Exception):
55
56 def __str__(self): 56 def __str__(self):
57 return "an error happened while trying to use the proxy" 57 return "an error happened while trying to use the proxy"
58 58
59 59
60 class XEP_0260(object): 60 class XEP_0260(object):
61 # TODO: udp handling 61 # TODO: udp handling
62 62
63 def __init__(self, host): 63 def __init__(self, host):
64 log.info(_("plugin Jingle SOCKS5 Bytestreams")) 64 log.info(_("plugin Jingle SOCKS5 Bytestreams"))
65 self.host = host 65 self.host = host
66 self._j = host.plugins["XEP-0166"] # shortcut to access jingle 66 self._j = host.plugins["XEP-0166"] # shortcut to access jingle
67 self._s5b = host.plugins["XEP-0065"] # and socks5 bytestream 67 self._s5b = host.plugins["XEP-0065"] # and socks5 bytestream
68 try: 68 try:
69 self._jingle_ibb = host.plugins["XEP-0261"] 69 self._jingle_ibb = host.plugins["XEP-0261"]
70 except KeyError: 70 except KeyError:
71 self._jingle_ibb = None 71 self._jingle_ibb = None
72 self._j.registerTransport(NS_JINGLE_S5B, self._j.TRANSPORT_STREAMING, self, 100) 72 self._j.registerTransport(NS_JINGLE_S5B, self._j.TRANSPORT_STREAMING, self, 100)
79 79
80 @param transport_elt(domish.Element): parent <transport> element 80 @param transport_elt(domish.Element): parent <transport> element
81 @return (list[plugin_xep_0065.Candidate): list of parsed candidates 81 @return (list[plugin_xep_0065.Candidate): list of parsed candidates
82 """ 82 """
83 candidates = [] 83 candidates = []
84 for candidate_elt in transport_elt.elements(NS_JINGLE_S5B, 'candidate'): 84 for candidate_elt in transport_elt.elements(NS_JINGLE_S5B, "candidate"):
85 try: 85 try:
86 cid = candidate_elt['cid'] 86 cid = candidate_elt["cid"]
87 host = candidate_elt['host'] 87 host = candidate_elt["host"]
88 jid_= jid.JID(candidate_elt['jid']) 88 jid_ = jid.JID(candidate_elt["jid"])
89 port = int(candidate_elt.getAttribute('port', 1080)) 89 port = int(candidate_elt.getAttribute("port", 1080))
90 priority = int(candidate_elt['priority']) 90 priority = int(candidate_elt["priority"])
91 type_ = candidate_elt.getAttribute('type', self._s5b.TYPE_DIRECT) 91 type_ = candidate_elt.getAttribute("type", self._s5b.TYPE_DIRECT)
92 except (KeyError, ValueError): 92 except (KeyError, ValueError):
93 raise exceptions.DataError() 93 raise exceptions.DataError()
94 candidate = self._s5b.Candidate(host, port, type_, priority, jid_, cid) 94 candidate = self._s5b.Candidate(host, port, type_, priority, jid_, cid)
95 candidates.append(candidate) 95 candidates.append(candidate)
96 # self._s5b.registerCandidate(candidate) 96 # self._s5b.registerCandidate(candidate)
104 @param sid(unicode): transport stream id 104 @param sid(unicode): transport stream id
105 @param client: %(doc_client)s 105 @param client: %(doc_client)s
106 @param mode(str, None): 'tcp' or 'udp', or None to have no attribute 106 @param mode(str, None): 'tcp' or 'udp', or None to have no attribute
107 @return (domish.Element): parent <transport> element where <candidate> elements must be added 107 @return (domish.Element): parent <transport> element where <candidate> elements must be added
108 """ 108 """
109 proxy = next((candidate for candidate in candidates if candidate.type == self._s5b.TYPE_PROXY), None) 109 proxy = next(
110 (
111 candidate
112 for candidate in candidates
113 if candidate.type == self._s5b.TYPE_PROXY
114 ),
115 None,
116 )
110 transport_elt = domish.Element((NS_JINGLE_S5B, "transport")) 117 transport_elt = domish.Element((NS_JINGLE_S5B, "transport"))
111 transport_elt['sid'] = sid 118 transport_elt["sid"] = sid
112 if proxy is not None: 119 if proxy is not None:
113 transport_elt['dstaddr'] = session_hash 120 transport_elt["dstaddr"] = session_hash
114 if mode is not None: 121 if mode is not None:
115 transport_elt['mode'] = 'tcp' # XXX: we only manage tcp for now 122 transport_elt["mode"] = "tcp" # XXX: we only manage tcp for now
116 123
117 for candidate in candidates: 124 for candidate in candidates:
118 log.debug(u"Adding candidate: {}".format(candidate)) 125 log.debug(u"Adding candidate: {}".format(candidate))
119 candidate_elt = transport_elt.addElement('candidate', NS_JINGLE_S5B) 126 candidate_elt = transport_elt.addElement("candidate", NS_JINGLE_S5B)
120 if candidate.id is None: 127 if candidate.id is None:
121 candidate.id = unicode(uuid.uuid4()) 128 candidate.id = unicode(uuid.uuid4())
122 candidate_elt['cid'] = candidate.id 129 candidate_elt["cid"] = candidate.id
123 candidate_elt['host'] = candidate.host 130 candidate_elt["host"] = candidate.host
124 candidate_elt['jid'] = candidate.jid.full() 131 candidate_elt["jid"] = candidate.jid.full()
125 candidate_elt['port'] = unicode(candidate.port) 132 candidate_elt["port"] = unicode(candidate.port)
126 candidate_elt['priority'] = unicode(candidate.priority) 133 candidate_elt["priority"] = unicode(candidate.priority)
127 candidate_elt['type'] = candidate.type 134 candidate_elt["type"] = candidate.type
128 return transport_elt 135 return transport_elt
129 136
130 @defer.inlineCallbacks 137 @defer.inlineCallbacks
131 def jingleSessionInit(self, client, session, content_name): 138 def jingleSessionInit(self, client, session, content_name):
132 content_data = session['contents'][content_name] 139 content_data = session["contents"][content_name]
133 transport_data = content_data['transport_data'] 140 transport_data = content_data["transport_data"]
134 sid = transport_data['sid'] = unicode(uuid.uuid4()) 141 sid = transport_data["sid"] = unicode(uuid.uuid4())
135 session_hash = transport_data['session_hash'] = self._s5b.getSessionHash(client.jid, session['peer_jid'], sid) 142 session_hash = transport_data["session_hash"] = self._s5b.getSessionHash(
136 transport_data['peer_session_hash'] = self._s5b.getSessionHash(session['peer_jid'], client.jid, sid) # requester and target are inversed for peer candidates 143 client.jid, session["peer_jid"], sid
137 transport_data['stream_d'] = self._s5b.registerHash(client, session_hash, None) 144 )
138 candidates = transport_data['candidates'] = yield self._s5b.getCandidates(client) 145 transport_data["peer_session_hash"] = self._s5b.getSessionHash(
139 mode = 'tcp' # XXX: we only manage tcp for now 146 session["peer_jid"], client.jid, sid
140 transport_elt = self._buildCandidates(session, candidates, sid, session_hash, client, mode) 147 ) # requester and target are inversed for peer candidates
148 transport_data["stream_d"] = self._s5b.registerHash(client, session_hash, None)
149 candidates = transport_data["candidates"] = yield self._s5b.getCandidates(client)
150 mode = "tcp" # XXX: we only manage tcp for now
151 transport_elt = self._buildCandidates(
152 session, candidates, sid, session_hash, client, mode
153 )
141 154
142 defer.returnValue(transport_elt) 155 defer.returnValue(transport_elt)
143 156
144 def _proxyActivatedCb(self, iq_result_elt, client, candidate, session, content_name): 157 def _proxyActivatedCb(self, iq_result_elt, client, candidate, session, content_name):
145 """Called when activation confirmation has been received from proxy 158 """Called when activation confirmation has been received from proxy
146 159
147 cf XEP-0260 § 2.4 160 cf XEP-0260 § 2.4
148 """ 161 """
149 # now that the proxy is activated, we have to inform other peer 162 # now that the proxy is activated, we have to inform other peer
150 iq_elt, transport_elt = self._j.buildAction(client, self._j.A_TRANSPORT_INFO, session, content_name) 163 iq_elt, transport_elt = self._j.buildAction(
151 activated_elt = transport_elt.addElement('activated') 164 client, self._j.A_TRANSPORT_INFO, session, content_name
152 activated_elt['cid'] = candidate.id 165 )
166 activated_elt = transport_elt.addElement("activated")
167 activated_elt["cid"] = candidate.id
153 iq_elt.send() 168 iq_elt.send()
154 169
155 def _proxyActivatedEb(self, stanza_error, client, candidate, session, content_name): 170 def _proxyActivatedEb(self, stanza_error, client, candidate, session, content_name):
156 """Called when activation error has been received from proxy 171 """Called when activation error has been received from proxy
157 172
158 cf XEP-0260 § 2.4 173 cf XEP-0260 § 2.4
159 """ 174 """
160 # TODO: fallback to IBB 175 # TODO: fallback to IBB
161 # now that the proxy is activated, we have to inform other peer 176 # now that the proxy is activated, we have to inform other peer
162 iq_elt, transport_elt = self._j.buildAction(client, self._j.A_TRANSPORT_INFO, session, content_name) 177 iq_elt, transport_elt = self._j.buildAction(
163 transport_elt.addElement('proxy-error') 178 client, self._j.A_TRANSPORT_INFO, session, content_name
179 )
180 transport_elt.addElement("proxy-error")
164 iq_elt.send() 181 iq_elt.send()
165 log.warning(u"Can't activate proxy, we need to fallback to IBB: {reason}" 182 log.warning(
166 .format(reason = stanza_error.value.condition)) 183 u"Can't activate proxy, we need to fallback to IBB: {reason}".format(
184 reason=stanza_error.value.condition
185 )
186 )
167 self.doFallback(session, content_name, client) 187 self.doFallback(session, content_name, client)
168 188
169 def _foundPeerCandidate(self, candidate, session, transport_data, content_name, client): 189 def _foundPeerCandidate(
190 self, candidate, session, transport_data, content_name, client
191 ):
170 """Called when the best candidate from other peer is found 192 """Called when the best candidate from other peer is found
171 193
172 @param candidate(XEP_0065.Candidate, None): selected candidate, 194 @param candidate(XEP_0065.Candidate, None): selected candidate,
173 or None if no candidate is accessible 195 or None if no candidate is accessible
174 @param session(dict): session data 196 @param session(dict): session data
175 @param transport_data(dict): transport data 197 @param transport_data(dict): transport data
176 @param content_name(unicode): name of the current content 198 @param content_name(unicode): name of the current content
177 @param client(unicode): %(doc_client)s 199 @param client(unicode): %(doc_client)s
178 """ 200 """
179 201
180 transport_data['best_candidate'] = candidate 202 transport_data["best_candidate"] = candidate
181 # we need to disconnect all non selected candidates before removing them 203 # we need to disconnect all non selected candidates before removing them
182 for c in transport_data['peer_candidates']: 204 for c in transport_data["peer_candidates"]:
183 if c is None or c is candidate: 205 if c is None or c is candidate:
184 continue 206 continue
185 c.discard() 207 c.discard()
186 del transport_data['peer_candidates'] 208 del transport_data["peer_candidates"]
187 iq_elt, transport_elt = self._j.buildAction(client, self._j.A_TRANSPORT_INFO, session, content_name) 209 iq_elt, transport_elt = self._j.buildAction(
210 client, self._j.A_TRANSPORT_INFO, session, content_name
211 )
188 if candidate is None: 212 if candidate is None:
189 log.warning(u"Can't connect to any peer candidate") 213 log.warning(u"Can't connect to any peer candidate")
190 candidate_elt = transport_elt.addElement('candidate-error') 214 candidate_elt = transport_elt.addElement("candidate-error")
191 else: 215 else:
192 log.info(u"Found best peer candidate: {}".format(unicode(candidate))) 216 log.info(u"Found best peer candidate: {}".format(unicode(candidate)))
193 candidate_elt = transport_elt.addElement('candidate-used') 217 candidate_elt = transport_elt.addElement("candidate-used")
194 candidate_elt['cid'] = candidate.id 218 candidate_elt["cid"] = candidate.id
195 iq_elt.send() # TODO: check result stanza 219 iq_elt.send() # TODO: check result stanza
196 self._checkCandidates(session, content_name, transport_data, client) 220 self._checkCandidates(session, content_name, transport_data, client)
197 221
198 def _checkCandidates(self, session, content_name, transport_data, client): 222 def _checkCandidates(self, session, content_name, transport_data, client):
199 """Called when a candidate has been choosed 223 """Called when a candidate has been choosed
200 224
202 @param session(dict): session data 226 @param session(dict): session data
203 @param content_name(unicode): name of the current content 227 @param content_name(unicode): name of the current content
204 @param transport_data(dict): transport data 228 @param transport_data(dict): transport data
205 @param client(unicode): %(doc_client)s 229 @param client(unicode): %(doc_client)s
206 """ 230 """
207 content_data = session['contents'][content_name] 231 content_data = session["contents"][content_name]
208 try: 232 try:
209 best_candidate = transport_data['best_candidate'] 233 best_candidate = transport_data["best_candidate"]
210 except KeyError: 234 except KeyError:
211 # we have not our best candidate yet 235 # we have not our best candidate yet
212 return 236 return
213 try: 237 try:
214 peer_best_candidate = transport_data['peer_best_candidate'] 238 peer_best_candidate = transport_data["peer_best_candidate"]
215 except KeyError: 239 except KeyError:
216 # we have not peer best candidate yet 240 # we have not peer best candidate yet
217 return 241 return
218 242
219 # at this point we have both candidates, it's time to choose one 243 # at this point we have both candidates, it's time to choose one
220 if best_candidate is None or peer_best_candidate is None: 244 if best_candidate is None or peer_best_candidate is None:
221 choosed_candidate = best_candidate or peer_best_candidate 245 choosed_candidate = best_candidate or peer_best_candidate
222 else: 246 else:
223 if best_candidate.priority == peer_best_candidate.priority: 247 if best_candidate.priority == peer_best_candidate.priority:
224 # same priority, we choose initiator one according to XEP-0260 §2.4 #4 248 # same priority, we choose initiator one according to XEP-0260 §2.4 #4
225 log.debug(u"Candidates have same priority, we select the one choosed by initiator") 249 log.debug(
226 if session['initiator'] == client.jid: 250 u"Candidates have same priority, we select the one choosed by initiator"
251 )
252 if session["initiator"] == client.jid:
227 choosed_candidate = best_candidate 253 choosed_candidate = best_candidate
228 else: 254 else:
229 choosed_candidate = peer_best_candidate 255 choosed_candidate = peer_best_candidate
230 else: 256 else:
231 choosed_candidate = max(best_candidate, peer_best_candidate, key=lambda c:c.priority) 257 choosed_candidate = max(
258 best_candidate, peer_best_candidate, key=lambda c: c.priority
259 )
232 260
233 if choosed_candidate is None: 261 if choosed_candidate is None:
234 log.warning(u"Socks5 negociation failed, we need to fallback to IBB") 262 log.warning(u"Socks5 negociation failed, we need to fallback to IBB")
235 self.doFallback(session, content_name, client) 263 self.doFallback(session, content_name, client)
236 else: 264 else:
239 # so our_candidate is true if choosed_candidate is peer_best_candidate 267 # so our_candidate is true if choosed_candidate is peer_best_candidate
240 our_candidate = True 268 our_candidate = True
241 # than also mean that best_candidate must be discarded ! 269 # than also mean that best_candidate must be discarded !
242 try: 270 try:
243 best_candidate.discard() 271 best_candidate.discard()
244 except AttributeError: # but it can be None 272 except AttributeError: # but it can be None
245 pass 273 pass
246 else: 274 else:
247 our_candidate = False 275 our_candidate = False
248 276
249 log.info(u"Socks5 negociation successful, {who} candidate will be used: {candidate}".format( 277 log.info(
250 who = u'our' if our_candidate else u'other peer', 278 u"Socks5 negociation successful, {who} candidate will be used: {candidate}".format(
251 candidate = choosed_candidate)) 279 who=u"our" if our_candidate else u"other peer",
252 del transport_data['best_candidate'] 280 candidate=choosed_candidate,
253 del transport_data['peer_best_candidate'] 281 )
282 )
283 del transport_data["best_candidate"]
284 del transport_data["peer_best_candidate"]
254 285
255 if choosed_candidate.type == self._s5b.TYPE_PROXY: 286 if choosed_candidate.type == self._s5b.TYPE_PROXY:
256 # the stream transfer need to wait for proxy activation 287 # the stream transfer need to wait for proxy activation
257 # (see XEP-0260 § 2.4) 288 # (see XEP-0260 § 2.4)
258 if our_candidate: 289 if our_candidate:
259 d = self._s5b.connectCandidate(client, choosed_candidate, transport_data['session_hash']) 290 d = self._s5b.connectCandidate(
260 d.addCallback(lambda dummy: choosed_candidate.activate(transport_data['sid'], session['peer_jid'], client)) 291 client, choosed_candidate, transport_data["session_hash"]
292 )
293 d.addCallback(
294 lambda dummy: choosed_candidate.activate(
295 transport_data["sid"], session["peer_jid"], client
296 )
297 )
261 args = [client, choosed_candidate, session, content_name] 298 args = [client, choosed_candidate, session, content_name]
262 d.addCallbacks(self._proxyActivatedCb, self._proxyActivatedEb, args, None, args) 299 d.addCallbacks(
300 self._proxyActivatedCb, self._proxyActivatedEb, args, None, args
301 )
263 else: 302 else:
264 # this Deferred will be called when we'll receive activation confirmation from other peer 303 # this Deferred will be called when we'll receive activation confirmation from other peer
265 d = transport_data['activation_d'] = defer.Deferred() 304 d = transport_data["activation_d"] = defer.Deferred()
266 else: 305 else:
267 d = defer.succeed(None) 306 d = defer.succeed(None)
268 307
269 if content_data['senders'] == session['role']: 308 if content_data["senders"] == session["role"]:
270 # we can now start the stream transfer (or start it after proxy activation) 309 # we can now start the stream transfer (or start it after proxy activation)
271 d.addCallback(lambda dummy: choosed_candidate.startTransfer(transport_data['session_hash'])) 310 d.addCallback(
311 lambda dummy: choosed_candidate.startTransfer(
312 transport_data["session_hash"]
313 )
314 )
272 d.addErrback(self._startEb, session, content_name, client) 315 d.addErrback(self._startEb, session, content_name, client)
273 316
274 def _startEb(self, fail, session, content_name, client): 317 def _startEb(self, fail, session, content_name, client):
275 """Called when it's not possible to start the transfer 318 """Called when it's not possible to start the transfer
276 319
281 except AttributeError: 324 except AttributeError:
282 reason = unicode(fail) 325 reason = unicode(fail)
283 log.warning(u"Cant start transfert, we'll try fallback method: {}".format(reason)) 326 log.warning(u"Cant start transfert, we'll try fallback method: {}".format(reason))
284 self.doFallback(session, content_name, client) 327 self.doFallback(session, content_name, client)
285 328
286 def _candidateInfo(self, candidate_elt, session, content_name, transport_data, client): 329 def _candidateInfo(
330 self, candidate_elt, session, content_name, transport_data, client
331 ):
287 """Called when best candidate has been received from peer (or if none is working) 332 """Called when best candidate has been received from peer (or if none is working)
288 333
289 @param candidate_elt(domish.Element): candidate-used or candidate-error element 334 @param candidate_elt(domish.Element): candidate-used or candidate-error element
290 (see XEP-0260 §2.3) 335 (see XEP-0260 §2.3)
291 @param session(dict): session data 336 @param session(dict): session data
292 @param content_name(unicode): name of the current content 337 @param content_name(unicode): name of the current content
293 @param transport_data(dict): transport data 338 @param transport_data(dict): transport data
294 @param client(unicode): %(doc_client)s 339 @param client(unicode): %(doc_client)s
295 """ 340 """
296 if candidate_elt.name == 'candidate-error': 341 if candidate_elt.name == "candidate-error":
297 # candidate-error, no candidate worked 342 # candidate-error, no candidate worked
298 transport_data['peer_best_candidate'] = None 343 transport_data["peer_best_candidate"] = None
299 else: 344 else:
300 # candidate-used, one candidate was choosed 345 # candidate-used, one candidate was choosed
301 try: 346 try:
302 cid = candidate_elt.attributes['cid'] 347 cid = candidate_elt.attributes["cid"]
303 except KeyError: 348 except KeyError:
304 log.warning(u"No cid found in <candidate-used>") 349 log.warning(u"No cid found in <candidate-used>")
305 raise exceptions.DataError 350 raise exceptions.DataError
306 try: 351 try:
307 candidate = (c for c in transport_data['candidates'] if c.id == cid).next() 352 candidate = (
353 c for c in transport_data["candidates"] if c.id == cid
354 ).next()
308 except StopIteration: 355 except StopIteration:
309 log.warning(u"Given cid doesn't correspond to any known candidate !") 356 log.warning(u"Given cid doesn't correspond to any known candidate !")
310 raise exceptions.DataError # TODO: send an error to other peer, and use better exception 357 raise exceptions.DataError # TODO: send an error to other peer, and use better exception
311 except KeyError: 358 except KeyError:
312 # a transport-info can also be intentionaly sent too early by other peer 359 # a transport-info can also be intentionaly sent too early by other peer
313 # but there is little probability 360 # but there is little probability
314 log.error(u'"candidates" key doesn\'t exists in transport_data, it should at this point') 361 log.error(
362 u'"candidates" key doesn\'t exists in transport_data, it should at this point'
363 )
315 raise exceptions.InternalError 364 raise exceptions.InternalError
316 # at this point we have the candidate choosed by other peer 365 # at this point we have the candidate choosed by other peer
317 transport_data['peer_best_candidate'] = candidate 366 transport_data["peer_best_candidate"] = candidate
318 log.info(u"Other peer best candidate: {}".format(candidate)) 367 log.info(u"Other peer best candidate: {}".format(candidate))
319 368
320 del transport_data['candidates'] 369 del transport_data["candidates"]
321 self._checkCandidates(session, content_name, transport_data, client) 370 self._checkCandidates(session, content_name, transport_data, client)
322 371
323 def _proxyActivationInfo(self, proxy_elt, session, content_name, transport_data, client): 372 def _proxyActivationInfo(
373 self, proxy_elt, session, content_name, transport_data, client
374 ):
324 """Called when proxy has been activated (or has sent an error) 375 """Called when proxy has been activated (or has sent an error)
325 376
326 @param proxy_elt(domish.Element): <activated/> or <proxy-error/> element 377 @param proxy_elt(domish.Element): <activated/> or <proxy-error/> element
327 (see XEP-0260 §2.4) 378 (see XEP-0260 §2.4)
328 @param session(dict): session data 379 @param session(dict): session data
329 @param content_name(unicode): name of the current content 380 @param content_name(unicode): name of the current content
330 @param transport_data(dict): transport data 381 @param transport_data(dict): transport data
331 @param client(unicode): %(doc_client)s 382 @param client(unicode): %(doc_client)s
332 """ 383 """
333 try: 384 try:
334 activation_d = transport_data.pop('activation_d') 385 activation_d = transport_data.pop("activation_d")
335 except KeyError: 386 except KeyError:
336 log.warning(u"Received unexpected transport-info for proxy activation") 387 log.warning(u"Received unexpected transport-info for proxy activation")
337 388
338 if proxy_elt.name == 'activated': 389 if proxy_elt.name == "activated":
339 activation_d.callback(None) 390 activation_d.callback(None)
340 else: 391 else:
341 activation_d.errback(ProxyError()) 392 activation_d.errback(ProxyError())
342 393
343 @defer.inlineCallbacks 394 @defer.inlineCallbacks
344 def jingleHandler(self, client, action, session, content_name, transport_elt): 395 def jingleHandler(self, client, action, session, content_name, transport_elt):
345 content_data = session['contents'][content_name] 396 content_data = session["contents"][content_name]
346 transport_data = content_data['transport_data'] 397 transport_data = content_data["transport_data"]
347 398
348 if action in (self._j.A_ACCEPTED_ACK, self._j.A_PREPARE_RESPONDER): 399 if action in (self._j.A_ACCEPTED_ACK, self._j.A_PREPARE_RESPONDER):
349 pass 400 pass
350 401
351 elif action == self._j.A_SESSION_ACCEPT: 402 elif action == self._j.A_SESSION_ACCEPT:
352 # initiator side, we select a candidate in the ones sent by responder 403 # initiator side, we select a candidate in the ones sent by responder
353 assert 'peer_candidates' not in transport_data 404 assert "peer_candidates" not in transport_data
354 transport_data['peer_candidates'] = self._parseCandidates(transport_elt) 405 transport_data["peer_candidates"] = self._parseCandidates(transport_elt)
355 406
356 elif action == self._j.A_START: 407 elif action == self._j.A_START:
357 session_hash = transport_data['session_hash'] 408 session_hash = transport_data["session_hash"]
358 peer_candidates = transport_data['peer_candidates'] 409 peer_candidates = transport_data["peer_candidates"]
359 stream_object = content_data['stream_object'] 410 stream_object = content_data["stream_object"]
360 self._s5b.associateStreamObject(client, session_hash, stream_object) 411 self._s5b.associateStreamObject(client, session_hash, stream_object)
361 stream_d = transport_data.pop('stream_d') 412 stream_d = transport_data.pop("stream_d")
362 stream_d.chainDeferred(content_data['finished_d']) 413 stream_d.chainDeferred(content_data["finished_d"])
363 peer_session_hash = transport_data['peer_session_hash'] 414 peer_session_hash = transport_data["peer_session_hash"]
364 d = self._s5b.getBestCandidate(client, peer_candidates, session_hash, peer_session_hash) 415 d = self._s5b.getBestCandidate(
365 d.addCallback(self._foundPeerCandidate, session, transport_data, content_name, client) 416 client, peer_candidates, session_hash, peer_session_hash
417 )
418 d.addCallback(
419 self._foundPeerCandidate, session, transport_data, content_name, client
420 )
366 421
367 elif action == self._j.A_SESSION_INITIATE: 422 elif action == self._j.A_SESSION_INITIATE:
368 # responder side, we select a candidate in the ones sent by initiator 423 # responder side, we select a candidate in the ones sent by initiator
369 # and we give our candidates 424 # and we give our candidates
370 assert 'peer_candidates' not in transport_data 425 assert "peer_candidates" not in transport_data
371 sid = transport_data['sid'] = transport_elt['sid'] 426 sid = transport_data["sid"] = transport_elt["sid"]
372 session_hash = transport_data['session_hash'] = self._s5b.getSessionHash(client.jid, session['peer_jid'], sid) 427 session_hash = transport_data["session_hash"] = self._s5b.getSessionHash(
373 peer_session_hash = transport_data['peer_session_hash'] = self._s5b.getSessionHash(session['peer_jid'], client.jid, sid) # requester and target are inversed for peer candidates 428 client.jid, session["peer_jid"], sid
374 peer_candidates = transport_data['peer_candidates'] = self._parseCandidates(transport_elt) 429 )
375 stream_object = content_data['stream_object'] 430 peer_session_hash = transport_data[
431 "peer_session_hash"
432 ] = self._s5b.getSessionHash(
433 session["peer_jid"], client.jid, sid
434 ) # requester and target are inversed for peer candidates
435 peer_candidates = transport_data["peer_candidates"] = self._parseCandidates(
436 transport_elt
437 )
438 stream_object = content_data["stream_object"]
376 stream_d = self._s5b.registerHash(client, session_hash, stream_object) 439 stream_d = self._s5b.registerHash(client, session_hash, stream_object)
377 stream_d.chainDeferred(content_data['finished_d']) 440 stream_d.chainDeferred(content_data["finished_d"])
378 d = self._s5b.getBestCandidate(client, peer_candidates, session_hash, peer_session_hash) 441 d = self._s5b.getBestCandidate(
379 d.addCallback(self._foundPeerCandidate, session, transport_data, content_name, client) 442 client, peer_candidates, session_hash, peer_session_hash
443 )
444 d.addCallback(
445 self._foundPeerCandidate, session, transport_data, content_name, client
446 )
380 candidates = yield self._s5b.getCandidates(client) 447 candidates = yield self._s5b.getCandidates(client)
381 # we remove duplicate candidates 448 # we remove duplicate candidates
382 candidates = [candidate for candidate in candidates if candidate not in peer_candidates] 449 candidates = [
383 450 candidate for candidate in candidates if candidate not in peer_candidates
384 transport_data['candidates'] = candidates 451 ]
452
453 transport_data["candidates"] = candidates
385 # we can now build a new <transport> element with our candidates 454 # we can now build a new <transport> element with our candidates
386 transport_elt = self._buildCandidates(session, candidates, sid, session_hash, client) 455 transport_elt = self._buildCandidates(
456 session, candidates, sid, session_hash, client
457 )
387 458
388 elif action == self._j.A_TRANSPORT_INFO: 459 elif action == self._j.A_TRANSPORT_INFO:
389 # transport-info can be about candidate or proxy activation 460 # transport-info can be about candidate or proxy activation
390 candidate_elt = None 461 candidate_elt = None
391 462
392 for method, names in ((self._candidateInfo, ('candidate-used', 'candidate-error')), 463 for method, names in (
393 (self._proxyActivationInfo, ('activated', 'proxy-error'))): 464 (self._candidateInfo, ("candidate-used", "candidate-error")),
465 (self._proxyActivationInfo, ("activated", "proxy-error")),
466 ):
394 for name in names: 467 for name in names:
395 try: 468 try:
396 candidate_elt = transport_elt.elements(NS_JINGLE_S5B, name).next() 469 candidate_elt = transport_elt.elements(NS_JINGLE_S5B, name).next()
397 except StopIteration: 470 except StopIteration:
398 continue 471 continue
399 else: 472 else:
400 method(candidate_elt, session, content_name, transport_data, client) 473 method(
474 candidate_elt, session, content_name, transport_data, client
475 )
401 break 476 break
402 477
403 if candidate_elt is None: 478 if candidate_elt is None:
404 log.warning(u"Unexpected transport element: {}".format(transport_elt.toXml())) 479 log.warning(
480 u"Unexpected transport element: {}".format(transport_elt.toXml())
481 )
405 elif action == self._j.A_DESTROY: 482 elif action == self._j.A_DESTROY:
406 # the transport is replaced (fallback ?), We need mainly to kill XEP-0065 session. 483 # the transport is replaced (fallback ?), We need mainly to kill XEP-0065 session.
407 # note that sid argument is not necessary for sessions created by this plugin 484 # note that sid argument is not necessary for sessions created by this plugin
408 self._s5b.killSession(None, transport_data['session_hash'], None, client) 485 self._s5b.killSession(None, transport_data["session_hash"], None, client)
409 else: 486 else:
410 log.warning(u"FIXME: unmanaged action {}".format(action)) 487 log.warning(u"FIXME: unmanaged action {}".format(action))
411 488
412 defer.returnValue(transport_elt) 489 defer.returnValue(transport_elt)
413 490
414 def jingleTerminate(self, client, action, session, content_name, reason_elt): 491 def jingleTerminate(self, client, action, session, content_name, reason_elt):
415 if reason_elt.decline: 492 if reason_elt.decline:
416 log.debug(u"Session declined, deleting S5B session") 493 log.debug(u"Session declined, deleting S5B session")
417 # we just need to clean the S5B session if it is declined 494 # we just need to clean the S5B session if it is declined
418 content_data = session['contents'][content_name] 495 content_data = session["contents"][content_name]
419 transport_data = content_data['transport_data'] 496 transport_data = content_data["transport_data"]
420 self._s5b.killSession(None, transport_data['session_hash'], None, client) 497 self._s5b.killSession(None, transport_data["session_hash"], None, client)
421 498
422 def _doFallback(self, feature_checked, session, content_name, client): 499 def _doFallback(self, feature_checked, session, content_name, client):
423 """Do the fallback, method called once feature is checked 500 """Do the fallback, method called once feature is checked
424 501
425 @param feature_checked(bool): True if other peer can do IBB 502 @param feature_checked(bool): True if other peer can do IBB
426 """ 503 """
427 if not feature_checked: 504 if not feature_checked:
428 log.warning(u"Other peer can't manage jingle IBB, be have to terminate the session") 505 log.warning(
506 u"Other peer can't manage jingle IBB, be have to terminate the session"
507 )
429 self._j.terminate(client, self._j.REASON_CONNECTIVITY_ERROR, session) 508 self._j.terminate(client, self._j.REASON_CONNECTIVITY_ERROR, session)
430 else: 509 else:
431 self._j.transportReplace(client, self._jingle_ibb.NAMESPACE, session, content_name) 510 self._j.transportReplace(
511 client, self._jingle_ibb.NAMESPACE, session, content_name
512 )
432 513
433 def doFallback(self, session, content_name, client): 514 def doFallback(self, session, content_name, client):
434 """Fallback to IBB transport, used in last resort 515 """Fallback to IBB transport, used in last resort
435 516
436 @param session(dict): session data 517 @param session(dict): session data
437 @param content_name(unicode): name of the current content 518 @param content_name(unicode): name of the current content
438 @param client(unicode): %(doc_client)s 519 @param client(unicode): %(doc_client)s
439 """ 520 """
440 if session['role'] != self._j.ROLE_INITIATOR: 521 if session["role"] != self._j.ROLE_INITIATOR:
441 # only initiator must do the fallback, see XEP-0260 §3 522 # only initiator must do the fallback, see XEP-0260 §3
442 return 523 return
443 if self._jingle_ibb is None: 524 if self._jingle_ibb is None:
444 log.warning(u"Jingle IBB (XEP-0261) plugin is not available, we have to close the session") 525 log.warning(
526 u"Jingle IBB (XEP-0261) plugin is not available, we have to close the session"
527 )
445 self._j.terminate(client, self._j.REASON_CONNECTIVITY_ERROR, session) 528 self._j.terminate(client, self._j.REASON_CONNECTIVITY_ERROR, session)
446 else: 529 else:
447 d = self.host.hasFeature(client, self._jingle_ibb.NAMESPACE, session['peer_jid']) 530 d = self.host.hasFeature(
531 client, self._jingle_ibb.NAMESPACE, session["peer_jid"]
532 )
448 d.addCallback(self._doFallback, session, content_name, client) 533 d.addCallback(self._doFallback, session, content_name, client)
449 return d 534 return d
450 535
451 536
452 class XEP_0260_handler(XMPPHandler): 537 class XEP_0260_handler(XMPPHandler):
453 implements(iwokkel.IDisco) 538 implements(iwokkel.IDisco)
454 539
455 def getDiscoInfo(self, requestor, target, nodeIdentifier=''): 540 def getDiscoInfo(self, requestor, target, nodeIdentifier=""):
456 return [disco.DiscoFeature(NS_JINGLE_S5B)] 541 return [disco.DiscoFeature(NS_JINGLE_S5B)]
457 542
458 def getDiscoItems(self, requestor, target, nodeIdentifier=''): 543 def getDiscoItems(self, requestor, target, nodeIdentifier=""):
459 return [] 544 return []