Package common :: Package xmpp :: Module auth_nb
[hide private]
[frames] | no frames]

Source Code for Module common.xmpp.auth_nb

  1  ##   auth_nb.py 
  2  ##       based on auth.py, changes backported up to revision 1.41 
  3  ## 
  4  ##   Copyright (C) 2003-2005 Alexey "Snake" Nezhdanov 
  5  ##       modified by Dimitur Kirov <dkirov@gmail.com> 
  6  ## 
  7  ##   This program is free software; you can redistribute it and/or modify 
  8  ##   it under the terms of the GNU General Public License as published by 
  9  ##   the Free Software Foundation; either version 2, or (at your option) 
 10  ##   any later version. 
 11  ## 
 12  ##   This program is distributed in the hope that it will be useful, 
 13  ##   but WITHOUT ANY WARRANTY; without even the implied warranty of 
 14  ##   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 15  ##   GNU General Public License for more details. 
 16   
 17  """ 
 18  Provides plugs for SASL and NON-SASL authentication mechanisms. 
 19  Can be used both for client and transport authentication 
 20   
 21  See client_nb.py 
 22  """ 
 23   
 24  from protocol import NS_SASL, NS_SESSION, NS_STREAMS, NS_BIND, NS_AUTH 
 25  from protocol import Node, NodeProcessed, isResultNode, Iq, Protocol, JID 
 26  from plugin import PlugIn 
 27  import base64 
 28  import random 
 29  import itertools 
 30  import dispatcher_nb 
 31  import hashlib 
 32  import hmac 
 33  import hashlib 
 34   
 35  import logging 
 36  log = logging.getLogger('gajim.c.x.auth_nb') 
37 38 -def HH(some): return hashlib.md5(some).hexdigest()
39 -def H(some): return hashlib.md5(some).digest()
40 -def C(some): return ':'.join(some)
41 42 try: 43 import kerberos 44 have_kerberos = True 45 except ImportError: 46 have_kerberos = False 47 48 GSS_STATE_STEP = 0 49 GSS_STATE_WRAP = 1 50 SASL_FAILURE = 'failure' 51 SASL_SUCCESS = 'success' 52 SASL_UNSUPPORTED = 'not-supported' 53 SASL_IN_PROCESS = 'in-process'
54 55 -def challenge_splitter(data):
56 """ 57 Helper function that creates a dict from challenge string 58 59 Sample challenge string: 60 username="example.org",realm="somerealm",\ 61 nonce="OA6MG9tEQGm2hh",cnonce="OA6MHXh6VqTrRk",\ 62 nc=00000001,qop="auth,auth-int,auth-conf",charset=utf-8 63 64 Expected result for challan: 65 dict['qop'] = ('auth','auth-int','auth-conf') 66 dict['realm'] = 'somerealm' 67 """ 68 X_KEYWORD, X_VALUE, X_END = 0, 1, 2 69 quotes_open = False 70 keyword, value = '', '' 71 dict_ = {} 72 arr = None 73 74 expecting = X_KEYWORD 75 for iter_ in range(len(data) + 1): 76 end = False 77 if iter_ == len(data): 78 expecting = X_END 79 end = True 80 else: 81 char = data[iter_] 82 if expecting == X_KEYWORD: 83 if char == '=': 84 expecting = X_VALUE 85 elif char in (',', ' ', '\t'): 86 pass 87 else: 88 keyword = '%s%c' % (keyword, char) 89 elif expecting == X_VALUE: 90 if char == '"': 91 if quotes_open: 92 end = True 93 else: 94 quotes_open = True 95 elif char in (',', ' ', '\t'): 96 if quotes_open: 97 if not arr: 98 arr = [value] 99 else: 100 arr.append(value) 101 value = "" 102 else: 103 end = True 104 else: 105 value = '%s%c' % (value, char) 106 if end: 107 if arr: 108 arr.append(value) 109 dict_[keyword] = arr 110 arr = None 111 else: 112 dict_[keyword] = value 113 value, keyword = '', '' 114 expecting = X_KEYWORD 115 quotes_open = False 116 return dict_
117
118 -def scram_parse(chatter):
119 return dict(s.split('=', 1) for s in chatter.split(','))
120
121 -class SASL(PlugIn):
122 """ 123 Implements SASL authentication. Can be plugged into NonBlockingClient 124 to start authentication 125 """ 126
127 - def __init__(self, username, password, on_sasl):
128 """ 129 :param user: XMPP username 130 :param password: XMPP password 131 :param on_sasl: Callback, will be called after each SASL auth-step. 132 """ 133 PlugIn.__init__(self) 134 self.username = username 135 self.password = password 136 self.on_sasl = on_sasl 137 self.realm = None
138
139 - def plugin(self, owner):
140 if 'version' not in self._owner.Dispatcher.Stream._document_attrs: 141 self.startsasl = SASL_UNSUPPORTED 142 elif self._owner.Dispatcher.Stream.features: 143 try: 144 self.FeaturesHandler(self._owner.Dispatcher, 145 self._owner.Dispatcher.Stream.features) 146 except NodeProcessed: 147 pass 148 else: 149 self.startsasl = None
150
151 - def plugout(self):
152 """ 153 Remove SASL handlers from owner's dispatcher. Used internally 154 """ 155 if 'features' in self._owner.__dict__: 156 self._owner.UnregisterHandler('features', self.FeaturesHandler, 157 xmlns=NS_STREAMS) 158 if 'challenge' in self._owner.__dict__: 159 self._owner.UnregisterHandler('challenge', self.SASLHandler, 160 xmlns=NS_SASL) 161 if 'failure' in self._owner.__dict__: 162 self._owner.UnregisterHandler('failure', self.SASLHandler, 163 xmlns=NS_SASL) 164 if 'success' in self._owner.__dict__: 165 self._owner.UnregisterHandler('success', self.SASLHandler, 166 xmlns=NS_SASL)
167
168 - def auth(self):
169 """ 170 Start authentication. Result can be obtained via "SASL.startsasl" 171 attribute and will be either SASL_SUCCESS or SASL_FAILURE 172 173 Note that successfull auth will take at least two Dispatcher.Process() 174 calls. 175 """ 176 if self.startsasl: 177 pass 178 elif self._owner.Dispatcher.Stream.features: 179 try: 180 self.FeaturesHandler(self._owner.Dispatcher, 181 self._owner.Dispatcher.Stream.features) 182 except NodeProcessed: 183 pass 184 else: 185 self._owner.RegisterHandler('features', 186 self.FeaturesHandler, xmlns=NS_STREAMS)
187
188 - def FeaturesHandler(self, conn, feats):
189 """ 190 Used to determine if server supports SASL auth. Used internally 191 """ 192 if not feats.getTag('mechanisms', namespace=NS_SASL): 193 self.startsasl='not-supported' 194 log.info('SASL not supported by server') 195 return 196 self.mecs = [] 197 for mec in feats.getTag('mechanisms', namespace=NS_SASL).getTags( 198 'mechanism'): 199 self.mecs.append(mec.getData()) 200 201 self._owner.RegisterHandler('challenge', self.SASLHandler, xmlns=NS_SASL) 202 self._owner.RegisterHandler('failure', self.SASLHandler, xmlns=NS_SASL) 203 self._owner.RegisterHandler('success', self.SASLHandler, xmlns=NS_SASL) 204 self.MechanismHandler()
205
206 - def MechanismHandler(self):
207 if 'ANONYMOUS' in self.mecs and self.username is None: 208 self.mecs.remove('ANONYMOUS') 209 node = Node('auth', attrs={'xmlns': NS_SASL, 'mechanism': 'ANONYMOUS'}) 210 self.mechanism = 'ANONYMOUS' 211 self.startsasl = SASL_IN_PROCESS 212 self._owner.send(str(node)) 213 raise NodeProcessed 214 if "EXTERNAL" in self.mecs: 215 self.mecs.remove('EXTERNAL') 216 sasl_data = u'%s@%s' % (self.username, self._owner.Server) 217 sasl_data = sasl_data.encode('utf-8').encode('base64').replace( 218 '\n', '') 219 node = Node('auth', attrs={'xmlns': NS_SASL, 220 'mechanism': 'EXTERNAL'}, payload=[sasl_data]) 221 self.mechanism = 'EXTERNAL' 222 self.startsasl = SASL_IN_PROCESS 223 self._owner.send(str(node)) 224 raise NodeProcessed 225 if 'GSSAPI' in self.mecs and have_kerberos: 226 self.mecs.remove('GSSAPI') 227 try: 228 self.gss_vc = kerberos.authGSSClientInit('xmpp@' + \ 229 self._owner.xmpp_hostname)[1] 230 kerberos.authGSSClientStep(self.gss_vc, '') 231 response = kerberos.authGSSClientResponse(self.gss_vc) 232 node=Node('auth', attrs={'xmlns': NS_SASL, 'mechanism': 'GSSAPI'}, 233 payload=(response or '')) 234 self.mechanism = 'GSSAPI' 235 self.gss_step = GSS_STATE_STEP 236 self.startsasl = SASL_IN_PROCESS 237 self._owner.send(str(node)) 238 raise NodeProcessed 239 except kerberos.GSSError, e: 240 log.info('GSSAPI authentication failed: %s' % str(e)) 241 if 'SCRAM-SHA-1' in self.mecs: 242 self.mecs.remove('SCRAM-SHA-1') 243 self.mechanism = 'SCRAM-SHA-1' 244 self._owner._caller.get_password(self.set_password, self.mechanism) 245 self.scram_step = 0 246 self.startsasl = SASL_IN_PROCESS 247 raise NodeProcessed 248 if 'DIGEST-MD5' in self.mecs: 249 self.mecs.remove('DIGEST-MD5') 250 node = Node('auth', attrs={'xmlns': NS_SASL, 'mechanism': 'DIGEST-MD5'}) 251 self.mechanism = 'DIGEST-MD5' 252 self.startsasl = SASL_IN_PROCESS 253 self._owner.send(str(node)) 254 raise NodeProcessed 255 if 'PLAIN' in self.mecs: 256 self.mecs.remove('PLAIN') 257 self.mechanism = 'PLAIN' 258 self._owner._caller.get_password(self.set_password, self.mechanism) 259 self.startsasl = SASL_IN_PROCESS 260 raise NodeProcessed 261 self.startsasl = SASL_FAILURE 262 log.info('I can only use EXTERNAL, SCRAM-SHA-1, DIGEST-MD5, GSSAPI and ' 263 'PLAIN mecanisms.') 264 if self.on_sasl: 265 self.on_sasl() 266 return
267
268 - def SASLHandler(self, conn, challenge):
269 """ 270 Perform next SASL auth step. Used internally 271 """ 272 if challenge.getNamespace() != NS_SASL: 273 return 274 ### Handle Auth result 275 if challenge.getName() == 'failure': 276 self.startsasl = SASL_FAILURE 277 try: 278 reason = challenge.getChildren()[0] 279 except Exception: 280 reason = challenge 281 log.info('Failed SASL authentification: %s' % reason) 282 if len(self.mecs) > 0: 283 # There are other mechanisms to test 284 self.MechanismHandler() 285 raise NodeProcessed 286 if self.on_sasl: 287 self.on_sasl() 288 raise NodeProcessed 289 elif challenge.getName() == 'success': 290 # TODO: Need to validate any data-with-success. 291 # TODO: Important for DIGEST-MD5 and SCRAM. 292 self.startsasl = SASL_SUCCESS 293 log.info('Successfully authenticated with remote server.') 294 handlers = self._owner.Dispatcher.dumpHandlers() 295 296 # Bosh specific dispatcher replugging 297 # save old features. They will be used in case we won't get response on 298 # stream restart after SASL auth (happens with XMPP over BOSH with 299 # Openfire) 300 old_features = self._owner.Dispatcher.Stream.features 301 self._owner.Dispatcher.PlugOut() 302 dispatcher_nb.Dispatcher.get_instance().PlugIn(self._owner, 303 after_SASL=True, old_features=old_features) 304 self._owner.Dispatcher.restoreHandlers(handlers) 305 self._owner.User = self.username 306 307 if self.on_sasl: 308 self.on_sasl() 309 raise NodeProcessed 310 311 ### Perform auth step 312 incoming_data = challenge.getData() 313 data=base64.decodestring(incoming_data) 314 log.info('Got challenge:' + data) 315 316 if self.mechanism == 'GSSAPI': 317 if self.gss_step == GSS_STATE_STEP: 318 rc = kerberos.authGSSClientStep(self.gss_vc, incoming_data) 319 if rc != kerberos.AUTH_GSS_CONTINUE: 320 self.gss_step = GSS_STATE_WRAP 321 elif self.gss_step == GSS_STATE_WRAP: 322 rc = kerberos.authGSSClientUnwrap(self.gss_vc, incoming_data) 323 response = kerberos.authGSSClientResponse(self.gss_vc) 324 rc = kerberos.authGSSClientWrap(self.gss_vc, response, 325 kerberos.authGSSClientUserName(self.gss_vc)) 326 response = kerberos.authGSSClientResponse(self.gss_vc) 327 if not response: 328 response = '' 329 self._owner.send(Node('response', attrs={'xmlns': NS_SASL}, 330 payload=response).__str__()) 331 raise NodeProcessed 332 if self.mechanism == 'SCRAM-SHA-1': 333 hashfn = hashlib.sha1 334 335 def HMAC(k, s): 336 return hmac.HMAC(key=k, msg=s, digestmod=hashfn).digest()
337 338 def XOR(x, y): 339 r = (chr(ord(px) ^ ord(py)) for px, py in zip(x, y)) 340 return ''.join(r)
341 342 def Hi(s, salt, iters): 343 ii = 1 344 try: 345 s = s.encode('utf-8') 346 except: 347 pass 348 ui_1 = HMAC(s, salt + '\0\0\0\01') 349 ui = ui_1 350 for i in range(iters - 1): 351 ii += 1 352 ui_1 = HMAC(s, ui_1) 353 ui = XOR(ui, ui_1) 354 return ui 355 356 def H(s): 357 return hashfn(s).digest() 358 359 def scram_base64(s): 360 return ''.join(s.encode('base64').split('\n')) 361 362 if self.scram_step == 0: 363 self.scram_step = 1 364 self.scram_soup += ',' + data + ',' 365 data = scram_parse(data) 366 # TODO: Should check cnonce here. 367 # TODO: Channel binding data goes in here too. 368 r = 'c=' + scram_base64(self.scram_gs2) 369 r += ',r=' + data['r'] 370 self.scram_soup += r 371 salt = data['s'].decode('base64') 372 iter = int(data['i']) 373 SaltedPassword = Hi(self.password, salt, iter) 374 # TODO: Could cache this, along with salt+iter. 375 ClientKey = HMAC(SaltedPassword, 'Client Key') 376 StoredKey = H(ClientKey) 377 ClientSignature = HMAC(StoredKey, self.scram_soup) 378 ClientProof = XOR(ClientKey, ClientSignature) 379 r += ',p=' + scram_base64(ClientProof) 380 ServerKey = HMAC(SaltedPassword, 'Server Key') 381 self.scram_ServerSignature = HMAC(ServerKey, self.scram_soup) 382 sasl_data = scram_base64(r) 383 node = Node('response', attrs={'xmlns': NS_SASL}, 384 payload=[sasl_data]) 385 self._owner.send(str(node)) 386 raise NodeProcessed 387 388 if self.scram_step == 1: 389 data = scram_parse(data) 390 if data['v'].decode('base64') != self.scram_ServerSignature: 391 # TODO: Not clear what to do here - need to abort. 392 raise Exception 393 node = Node('response', attrs={'xmlns': NS_SASL}); 394 self._owner.send(str(node)) 395 raise NodeProcessed 396 397 # magic foo... 398 chal = challenge_splitter(data) 399 if not self.realm and 'realm' in chal: 400 self.realm = chal['realm'] 401 if 'qop' in chal and ((isinstance(chal['qop'], str) and \ 402 chal['qop'] =='auth') or (isinstance(chal['qop'], list) and 'auth' in \ 403 chal['qop'])): 404 self.resp = {} 405 self.resp['username'] = self.username 406 if self.realm: 407 self.resp['realm'] = self.realm 408 else: 409 self.resp['realm'] = self._owner.Server 410 self.resp['nonce'] = chal['nonce'] 411 self.resp['cnonce'] = ''.join("%x" % randint(0, 2**28) for randint in 412 itertools.repeat(random.randint, 7)) 413 self.resp['nc'] = ('00000001') 414 self.resp['qop'] = 'auth' 415 self.resp['digest-uri'] = 'xmpp/' + self._owner.Server 416 self.resp['charset'] = 'utf-8' 417 # Password is now required 418 self._owner._caller.get_password(self.set_password, self.mechanism) 419 elif 'rspauth' in chal: 420 self._owner.send(str(Node('response', attrs={'xmlns':NS_SASL}))) 421 else: 422 self.startsasl = SASL_FAILURE 423 log.info('Failed SASL authentification: unknown challenge') 424 if self.on_sasl: 425 self.on_sasl() 426 raise NodeProcessed 427 428 @staticmethod
429 - def _convert_to_iso88591(string):
430 try: 431 string = string.decode('utf-8').encode('iso-8859-1') 432 except UnicodeEncodeError: 433 pass 434 return string
435
436 - def set_password(self, password):
437 self.password = '' if password is None else password 438 if self.mechanism == 'SCRAM-SHA-1': 439 nonce = ''.join('%x' % randint(0, 2 ** 28) for randint in \ 440 itertools.repeat(random.randint, 7)) 441 self.scram_soup = 'n=' + self.username + ',r=' + nonce 442 self.scram_gs2 = 'n,,' # No CB yet. 443 sasl_data = (self.scram_gs2 + self.scram_soup).encode('base64').\ 444 replace('\n', '') 445 node = Node('auth', attrs={'xmlns': NS_SASL, 446 'mechanism': self.mechanism}, payload=[sasl_data]) 447 elif self.mechanism == 'DIGEST-MD5': 448 hash_username = self._convert_to_iso88591(self.resp['username']) 449 hash_realm = self._convert_to_iso88591(self.resp['realm']) 450 hash_password = self._convert_to_iso88591(self.password) 451 A1 = C([H(C([hash_username, hash_realm, hash_password])), 452 self.resp['nonce'], self.resp['cnonce']]) 453 A2 = C(['AUTHENTICATE', self.resp['digest-uri']]) 454 response= HH(C([HH(A1), self.resp['nonce'], self.resp['nc'], 455 self.resp['cnonce'], self.resp['qop'], HH(A2)])) 456 self.resp['response'] = response 457 sasl_data = u'' 458 for key in ('charset', 'username', 'realm', 'nonce', 'nc', 'cnonce', 459 'digest-uri', 'response', 'qop'): 460 if key in ('nc', 'qop', 'response', 'charset'): 461 sasl_data += u"%s=%s," % (key, self.resp[key]) 462 else: 463 sasl_data += u'%s="%s",' % (key, self.resp[key]) 464 sasl_data = sasl_data[:-1].encode('utf-8').encode('base64').replace( 465 '\r', '').replace('\n', '') 466 node = Node('response', attrs={'xmlns':NS_SASL}, payload=[sasl_data]) 467 elif self.mechanism == 'PLAIN': 468 sasl_data = u'\x00%s\x00%s' % (self.username, self.password) 469 sasl_data = sasl_data.encode('utf-8').encode('base64').replace( 470 '\n', '') 471 node = Node('auth', attrs={'xmlns': NS_SASL, 'mechanism': 'PLAIN'}, 472 payload=[sasl_data]) 473 self._owner.send(str(node))
474
475 476 -class NonBlockingNonSASL(PlugIn):
477 """ 478 Implements old Non-SASL (JEP-0078) authentication used in jabberd1.4 and 479 transport authentication 480 """ 481
482 - def __init__(self, user, password, resource, on_auth):
483 """ 484 Caches username, password and resource for auth 485 """ 486 PlugIn.__init__(self) 487 self.user = user 488 if password is None: 489 self.password = '' 490 else: 491 self.password = password 492 self.resource = resource 493 self.on_auth = on_auth
494
495 - def plugin(self, owner):
496 """ 497 Determine the best auth method (digest/0k/plain) and use it for auth. 498 Returns used method name on success. Used internally 499 """ 500 log.info('Querying server about possible auth methods') 501 self.owner = owner 502 503 owner.Dispatcher.SendAndWaitForResponse( 504 Iq('get', NS_AUTH, payload=[Node('username', payload=[self.user])]), 505 func=self._on_username)
506
507 - def _on_username(self, resp):
508 if not isResultNode(resp): 509 log.info('No result node arrived! Aborting...') 510 return self.on_auth(None) 511 512 iq=Iq(typ='set', node=resp) 513 query = iq.getTag('query') 514 query.setTagData('username', self.user) 515 query.setTagData('resource', self.resource) 516 517 if query.getTag('digest'): 518 log.info("Performing digest authentication") 519 query.setTagData('digest', 520 hashlib.sha1(self.owner.Dispatcher.Stream._document_attrs['id'] 521 + self.password).hexdigest()) 522 if query.getTag('password'): 523 query.delChild('password') 524 self._method = 'digest' 525 elif query.getTag('token'): 526 token = query.getTagData('token') 527 seq = query.getTagData('sequence') 528 log.info("Performing zero-k authentication") 529 530 def hasher(s): 531 return hashlib.sha1(s).hexdigest()
532 533 def hash_n_times(s, count): 534 return count and hasher(hash_n_times(s, count-1)) or s
535 536 hash_ = hash_n_times(hasher(hasher(self.password) + token), int(seq)) 537 query.setTagData('hash', hash_) 538 self._method='0k' 539 else: 540 log.warn("Secure methods unsupported, performing plain text \ 541 authentication") 542 query.setTagData('password', self.password) 543 self._method = 'plain' 544 resp = self.owner.Dispatcher.SendAndWaitForResponse(iq, func=self._on_auth) 545
546 - def _on_auth(self, resp):
547 if isResultNode(resp): 548 log.info('Sucessfully authenticated with remote host.') 549 self.owner.User = self.user 550 self.owner.Resource = self.resource 551 self.owner._registered_name = self.owner.User+'@'+self.owner.Server+\ 552 '/'+self.owner.Resource 553 return self.on_auth(self._method) 554 log.info('Authentication failed!') 555 return self.on_auth(None)
556
557 558 -class NonBlockingBind(PlugIn):
559 """ 560 Bind some JID to the current connection to allow router know of our 561 location. Must be plugged after successful SASL auth 562 """ 563
564 - def __init__(self):
565 PlugIn.__init__(self) 566 self.bound = None
567
568 - def plugin(self, owner):
569 ''' Start resource binding, if allowed at this time. Used internally. ''' 570 if self._owner.Dispatcher.Stream.features: 571 try: 572 self.FeaturesHandler(self._owner.Dispatcher, 573 self._owner.Dispatcher.Stream.features) 574 except NodeProcessed: 575 pass 576 else: 577 self._owner.RegisterHandler('features', self.FeaturesHandler, 578 xmlns=NS_STREAMS)
579
580 - def FeaturesHandler(self, conn, feats):
581 """ 582 Determine if server supports resource binding and set some internal 583 attributes accordingly 584 """ 585 if not feats.getTag('bind', namespace=NS_BIND): 586 log.info('Server does not requested binding.') 587 # we try to bind resource anyway 588 #self.bound='failure' 589 self.bound = [] 590 return 591 if feats.getTag('session', namespace=NS_SESSION): 592 self.session = 1 593 else: 594 self.session = -1 595 self.bound = []
596
597 - def plugout(self):
598 """ 599 Remove Bind handler from owner's dispatcher. Used internally 600 """ 601 self._owner.UnregisterHandler('features', self.FeaturesHandler, 602 xmlns=NS_STREAMS)
603
604 - def NonBlockingBind(self, resource=None, on_bound=None):
605 """ 606 Perform binding. Use provided resource name or random (if not provided). 607 """ 608 self.on_bound = on_bound 609 self._resource = resource 610 if self._resource: 611 self._resource = [Node('resource', payload=[self._resource])] 612 else: 613 self._resource = [] 614 615 self._owner.onreceive(None) 616 self._owner.Dispatcher.SendAndWaitForResponse( 617 Protocol('iq', typ='set', payload=[Node('bind', attrs={'xmlns':NS_BIND}, 618 payload=self._resource)]), func=self._on_bound)
619
620 - def _on_bound(self, resp):
621 if isResultNode(resp): 622 if resp.getTag('bind') and resp.getTag('bind').getTagData('jid'): 623 self.bound.append(resp.getTag('bind').getTagData('jid')) 624 log.info('Successfully bound %s.' % self.bound[-1]) 625 jid = JID(resp.getTag('bind').getTagData('jid')) 626 self._owner.User = jid.getNode() 627 self._owner.Resource = jid.getResource() 628 if hasattr(self, 'session') and self.session == -1: 629 # Server don't want us to initialize a session 630 log.info('No session required.') 631 self.on_bound('ok') 632 else: 633 self._owner.SendAndWaitForResponse(Protocol('iq', typ='set', 634 payload=[Node('session', attrs={'xmlns':NS_SESSION})]), 635 func=self._on_session) 636 return 637 if resp: 638 log.info('Binding failed: %s.' % resp.getTag('error')) 639 self.on_bound(None) 640 else: 641 log.info('Binding failed: timeout expired.') 642 self.on_bound(None)
643
644 - def _on_session(self, resp):
645 self._owner.onreceive(None) 646 if isResultNode(resp): 647 log.info('Successfully opened session.') 648 self.session = 1 649 self.on_bound('ok') 650 else: 651 log.error('Session open failed.') 652 self.session = 0 653 self.on_bound(None)
654