Package spade :: Package xmppd :: Package modules :: Module router
[hide private]
[frames] | no frames]

Source Code for Module spade.xmppd.modules.router

   1  # -*- coding: UTF-8 -*- 
   2  # Distributed under the terms of GPL version 2 or any later 
   3  # Copyright (C) Alexey Nezhdanov 2004 
   4  # Copyright (C) Kristopher Tate / BlueBridge Technologies Group 2005 
   5  # Copyright (C) Javier Palanca & Gustavo Aranda/ Computer Technology Group 2006 
   6  # router, presence tracker and probes responder for xmppd.py 
   7   
   8   
   9  import copy 
  10  from xmpp import * 
  11  from xmppd import * 
  12  #try: from xmppd import xmppd 
  13  #except: pass 
  14   
  15  # XMPP-session flags 
  16  # From the server 
  17  SESSION_NOT_AUTHED  = 1 
  18  SESSION_AUTHED      = 2 
  19  SESSION_BOUND       = 3 
  20  SESSION_OPENED      = 4 
  21   
22 -class Router(PlugIn):
23 """ The first entity that gets access to arrived stanza. """ 24 NS='presence'
25 - def plugin(self,server):
26 self._data = {} 27 server.Dispatcher.RegisterHandler('presence',self.presenceHandler,xmlns=NS_CLIENT) 28 server.Dispatcher.RegisterHandler('presence',self.presenceHandler,xmlns=NS_SERVER) 29 # server.Dispatcher.RegisterNamespaceHandler(NS_CLIENT,self.routerHandler) 30 server.Dispatcher.RegisterHandler('message',self.routerHandler,xmlns=NS_CLIENT) 31 server.Dispatcher.RegisterHandler('message',self.routerHandler,xmlns=NS_SERVER) 32 #server.Dispatcher.RegisterHandler('iq',self.routerHandler,xmlns=NS_SERVER) 33 server.Dispatcher.RegisterHandler('iq',self.routerHandler,xmlns=NS_CLIENT) 34 server.Dispatcher.RegisterHandler('iq',self.routerHandler,ns=NS_DISCO_INFO,xmlns=NS_CLIENT) 35 server.Dispatcher.RegisterHandler('iq',self.routerHandler,ns=NS_DISCO_INFO,xmlns=NS_SERVER) 36 server.Dispatcher.RegisterHandler('iq',self.routerHandler,ns=NS_DISCO_ITEMS,xmlns=NS_CLIENT) 37 server.Dispatcher.RegisterHandler('iq',self.routerHandler,ns=NS_DISCO_ITEMS,xmlns=NS_SERVER) 38 # MUC namespaces 39 server.Dispatcher.RegisterHandler('iq',self.routerHandler,ns='http://jabber.org/protocol/muc') 40 server.Dispatcher.RegisterHandler('iq',self.routerHandler,ns='http://jabber.org/protocol/muc#user') 41 server.Dispatcher.RegisterHandler('iq',self.routerHandler,ns='http://jabber.org/protocol/muc#admin') 42 server.Dispatcher.RegisterHandler('iq',self.routerHandler,ns='http://jabber.org/protocol/muc#owner') 43 server.Dispatcher.RegisterHandler('iq',self.routerHandler,ns="http://jabber.org/protocol/muc#roominfo") 44 server.Dispatcher.RegisterHandler('iq',self.routerHandler,ns="http://jabber.org/protocol/muc#traffic") 45 server.Dispatcher.RegisterHandler('iq',self.routerHandler,ns="http://jabber.org/protocol/muc#roomconfig") 46 server.Dispatcher.RegisterHandler('iq',self.routerHandler,ns="http://jabber.org/protocol/muc#register") 47 # Stream Initiation 48 server.Dispatcher.RegisterHandler('iq',self.routerHandler,ns='http://jabber.org/protocol/si') 49 50 server.Dispatcher.RegisterNamespaceHandler(NS_SERVER,self.routerHandler)
51
52 - def isFromOutside(self, domain):
53 for servername in self._owner.servernames: 54 if str(domain).endswith(servername): 55 return False 56 return True
57
58 - def pluginRelay(self,session,stanza):
59 """ 60 Relays a stanza to a plugin dispatcher if such plugin is the correct receiver of the stanza 61 """ 62 63 to = stanza['to'] 64 if not to: return False 65 for k,p in self._owner.plugins.items(): 66 if p.has_key('jid') and p['jid'] == to.getDomain(): 67 try: 68 self.DEBUG("Dispatching stanza to %s plugin"%(k),"info") 69 exec("self._owner."+k+".dispatch(session,stanza)") 70 except: 71 self.DEBUG("Could not dispatch stanza to plugin "+k, "error") 72 return False 73 return True 74 return False
75 76 77
78 - def presenceHandler(self,session,stanza,raiseFlag=True,fromLocal=False):
79 self.DEBUG('Presence handler called (%s::%s)' % (session.peer,str(stanza.getType())),'info') 80 #filter out presences that should not influate our 'roster' 81 #This is presences, that's addressed: 82 #1) any other server 83 #2) any user 84 to=stanza['to'] 85 internal = stanza.getAttr('internal') 86 fromOutside = False 87 #<presence to="test3@172.16.0.2" from="test2@172.16.1.34/Adium" type="probe" /> 88 89 #component_jids = [] 90 #for v in self._owner.components.values(): 91 # if v.has_key('jid'): 92 # component_jids.append(v['jid']) 93 94 if self.pluginRelay(session,stanza) and raiseFlag: raise NodeProcessed 95 96 if to and self.isFromOutside(to.getDomain()) and internal != 'True': 97 #self.DEBUG("Presence stanza has an invalid recipient","warn") 98 #return 99 self.DEBUG("Presence stanza has an external or component receiver: %s"%(to.getDomain()),"warn") 100 101 typ=stanza.getType() 102 jid=session.peer #172.16.1.34 103 if not jid: 104 raise NodeProcessed 105 fromOutside = False 106 try: 107 barejid = "" 108 #barejid,resource=jid.split('/') 109 #jlist =str(jid).split('/') 110 #if len(jlist) > 0: barejid = jlist[0] 111 #if len(jlist) > 1: resource = jlist[1] 112 barejid = JID(jid).getStripped() 113 resource = JID(jid).getResource() 114 domain = JID(jid).getDomain() 115 supposed_from = stanza.getFrom() 116 if (barejid and supposed_from) and (barejid == supposed_from.getDomain()): 117 barejid = str(supposed_from) 118 119 fromOutside = self.isFromOutside(JID(barejid).getDomain()) 120 121 """ 122 if not JID(barejid).getDomain() in self._owner.servernames: 123 self.DEBUG("Presence from Outside","info") 124 fromOutside = True 125 """ 126 127 self.DEBUG("The real from seems to be "+barejid, "info") 128 except: 129 #self.DEBUG('Presence: Could not set barejid from jid: %s, fromOutside=True'%(str(jid)),'error') 130 self.DEBUG('Presence: Could not set barejid from jid: %s'%(str(jid)),'error') 131 132 """ 133 if fromOutside == True: 134 if typ in ['subscribe','subscribed','unsubscribe','unsubscribed']: 135 self.DEBUG("Redirecting stanza to subscriber", "warn") 136 self.subscriber(session,stanza) 137 else: 138 # Find each session 139 s = None 140 # If it's our server 141 if to.getDomain() in self._owner.servernames: 142 s = self._owner.getsession(str(to)) 143 if s == None: 144 # TODO: Store if it's real user 145 self.DEBUG("Could not find session for "+str(to),"error") 146 return 147 # Enqueue the message to the receiver 148 self.DEBUG("Redirecting stanza to receiver", "info") 149 p = Presence(to=s.peer,frm=stanza.getFrom(),typ=stanza.getType()) 150 151 152 for child in stanza.getChildren(): 153 if child.getName() == 'status' or child.getName() == 'Status': 154 p.setStatus(child.getData()) 155 elif child.getName() == 'show' or child.getName() == 'Show': 156 p.setShow(child.getData()) 157 elif child.getName() == 'priority' or child.getName() == 'Priority': 158 p.setPriority(child.getData()) 159 160 s.enqueue(p) 161 raise NodeProcessed 162 else: 163 self.DEBUG("Stupid situation: from and to are outsiders. WTF?","error") 164 return 165 """ 166 167 168 if not typ or typ=='available': # and fromOutside == False: 169 if fromOutside: 170 # Find each session 171 s = None 172 s = self._owner.getsession(str(to)) 173 if s == None: 174 # TODO: Store if it's real user 175 self.DEBUG("Could not find session for "+str(to),"warn") 176 return 177 stanza.setNamespace(NS_CLIENT) 178 s.enqueue(stanza) 179 raise NodeProcessed 180 181 else: 182 183 if not self._data.has_key(barejid): self._data[barejid]={} 184 if not self._data[barejid].has_key(resource): self._data[barejid][resource]=Presence(frm=jid,typ=typ) 185 bp=self._data[barejid][resource] 186 187 try: priority=int(stanza.getTagData('priority')) 188 except: priority=0 189 bp.T.priority=`priority` 190 self._owner.activatesession(session) 191 192 show=stanza.getTag('show') 193 if show: bp.T.show=show 194 else: bp.T.show='' 195 status=stanza.getTag('status') 196 if status: bp.T.status=status 197 else: bp.T.status='' 198 for x in bp.getTags('x',namespace='jabber:x:delay'): 199 bp.delChild(x) 200 bp.setTimestamp() 201 self.update(barejid) 202 203 self.DEBUG("available presence " + str(bp),'info') 204 205 206 self.broadcastAvailable(session) # Pass onto broadcaster! 207 208 if self._owner._socker != None: 209 self._owner._socker.add_data_root({'jid':{barejid:self._data[barejid].keys()}}) 210 self._owner._socker.add_data({'jid':{barejid:self._data[barejid].keys()}}) 211 212 elif (typ=='unavailable' or typ=='error'): # and fromOutside == False: 213 214 if not fromOutside: 215 jid_info = self._owner.tool_split_jid(barejid) 216 contacts = session.getRoster() 217 for k,v in contacts.iteritems(): 218 if v['subscription'] in ['from','both']: 219 self.DEBUG('Un-Presence attempt for contact "%s":'%k,'warn') 220 p = Presence(to=k,frm=session.peer,typ='unavailable') 221 status=stanza.getTag('status') 222 if status: p.T.show=status 223 else: p.T.show='Logged Out' 224 #self._owner.Dispatcher.dispatch(p,session) 225 226 k_jid = JID(k) 227 # Find each session 228 s = None 229 # If it's our server 230 if k_jid.getDomain() in self._owner.servernames or k in self._owner.servernames: 231 s = self._owner.getsession(k) 232 if s == None: 233 # TODO: Store 234 pass 235 else: 236 s=self._owner.S2S(session.ourname,k_jid.getDomain()) 237 238 # Send the presence 239 if s != None: 240 s.enqueue(p) 241 else: 242 self.DEBUG('Could not find active session for contact %s'%(k),'warn') 243 244 else: 245 # Find each session 246 s = None 247 s = self._owner.getsession(str(to)) 248 if s == None: 249 # TODO: Store if it's real user 250 self.DEBUG("Could not find session for "+str(to),"warn") 251 return 252 stanza.setNamespace(NS_CLIENT) 253 s.enqueue(stanza) 254 raise NodeProcessed 255 256 257 self.DEBUG('Finished for "%s"'%k,'info') 258 259 if self._owner._socker != None: 260 self._owner._socker.remove_data_root(['jid',barejid]) 261 self._owner._socker.remove_data(['jid',barejid]) 262 263 if not self._data.has_key(barejid) and raiseFlag == True: raise NodeProcessed 264 #if self._data[barejid].has_key(resource): del self._data[barejid][resource] 265 #self.update(barejid) 266 #if not self._data[barejid]: del self._data[barejid] 267 #self._owner.deactivatesession(session.peer) 268 269 elif typ=='invisible' and fromOutside == False: 270 271 jid_info = self._owner.tool_split_jid(barejid) 272 contacts = session.getRoster() 273 for k,v in contacts.iteritems(): 274 self.DEBUG('Un-Presence attempt for contact [INVISIBLE!!!]"%s":'%k,'warn') 275 p = Presence(to=k,frm=session.peer,typ='unavailable') 276 status=stanza.getTag('status') 277 if status: p.T.show=status 278 else: p.T.show='Logged Out' 279 session.dispatch(p) 280 self.DEBUG('Finished for "%s" [INVISIBLE!!!]'%k,'info') 281 282 elif typ=='probe': 283 self.DEBUG('Probe activated!','info') 284 if stanza.getTo() in self._owner.servernames: 285 session.enqueue(Presence(to=stanza.getFrom(),frm=stanza.getTo())) 286 raise NodeProcessed 287 if stanza.getAttr('internal') == 'True': 288 self.DEBUG('Internal Probe activated!','info') 289 try: 290 resources=[stanza.getTo().getResource()] 291 if not resources[0]: resources=self._data[stanza.getTo()].keys() 292 flag=1 293 for resource in resources: 294 p=Presence(to=stanza.getFrom(),frm='%s/%s'%(stanza.getTo(),resource),node=self._data[stanza.getTo()][resource]) 295 if flag: 296 #self._owner.Privacy(session,p) # Desactivado de momento 297 flag=None 298 session.enqueue(p) 299 except KeyError: pass #Wanted session is not active! #session.enqueue(Presence(to=stanza.getTo(),frm=jid,typ='unavailable')) 300 else: 301 if not self.isFromOutside(stanza.getTo().getDomain()): 302 #TODO: test if contact is authorized to know its presence 303 bareto = stanza.getTo().getStripped() 304 #resourceto = stanza.getTo().getResource() 305 if self._data.has_key(bareto): 306 #if self._data[bareto].has_key(resourceto): 307 for resourceto in self._data[bareto].keys(): 308 p = copy.copy(self._data[bareto][resourceto]) 309 p.setTo(barejid) 310 s = None 311 if not fromOutside: 312 s = self._owner.getsession(barejid) 313 else: 314 s = self._owner.S2S(session.ourname,domain) 315 if s: 316 s.enqueue(p) 317 self.DEBUG('Probe '+str(p) +' sent','info') 318 raise NodeProcessed 319 session.enqueue(Presence(to=stanza.getFrom(),frm=stanza.getTo(),typ='unavailable')) 320 self.DEBUG('Probe "unavailable" sent','info') 321 raise NodeProcessed 322 else: 323 self.DEBUG("Probe message ignored",'warn') 324 325 elif typ in ['subscribe', 'subscribed', 'unsubscribe', 'unsubscribed']: 326 self.DEBUG("Redirecting stanza to subscriber", "warn") 327 self.subscriber(session, stanza) 328 329 else: 330 self.DEBUG('Woah, nothing to call???','warn') 331 if raiseFlag: raise NodeProcessed
332
333 - def broadcastAvailable(self,session,to=None):
334 try: 335 #barejid,resource=session.peer.split('/') 336 barejid = JID(session.peer).getStripped() 337 resource = JID(session.peer).getResource() 338 except: 339 fromOutside = True 340 self.DEBUG('Presence: Could not set barejid, fromOutside=True','warn') 341 342 contacts = session.getRoster() 343 if contacts == None: return 344 345 #print "MY CONTACTS: "+ str(contacts) 346 347 #for x,y in contacts.iteritems(): 348 for x,y in contacts.items(): 349 #x_split = self._owner.tool_split_jid(x) 350 j = JID(x) 351 name = j.getNode(); domain = j.getDomain() 352 self.DEBUG('Presence attempt for contact "%s":'%x,'warn') 353 #print str(y['subscription']) 354 try: 355 if y['subscription'] in ['from','both']: 356 #if x_split[1] not in self._owner.servernames \ 357 #or self._owner.DB.pull_roster(x_split[1],x_split[0],barejid) != None: 358 if self.isFromOutside(domain) \ 359 or self._owner.DB.pull_roster(domain,name,barejid) != None: 360 if (x in self._owner.servernames): 361 self.DEBUG("Contact %s is the server. returning"%(str(x)),'warn') 362 return 363 self.DEBUG('Contact "%s" has from/both'%x,'warn') 364 ###session.dispatch(Presence(to=x,frm=session.peer,node=self._data[barejid][session.getResource()])) 365 366 # Find each session 367 s = None 368 # If it's our server 369 #if x_split[1] in self._owner.servernames or x in self._owner.servernames: 370 pres = Presence(to=x,frm=session.peer,node=self._data[barejid][session.getResource()]) 371 if not self.isFromOutside(domain) or x in self._owner.servernames: 372 s = self._owner.getsession(x) 373 if s == None: 374 # TODO: Store 375 pass 376 else: 377 #s=self._owner.S2S(session.ourname,x_split[1]) 378 s=self._owner.S2S(session.ourname,domain) 379 #pres.setNamespace(NS_SERVER) 380 381 # Send the presence 382 if s != None: 383 s.enqueue(pres) 384 self.DEBUG('Finished for "%s" (from/both)'%x,'info') 385 386 if y['subscription'] in ['to','both']: 387 #if (x_split == None and x in self._owner.servernames) \ 388 #or x_split[1] not in self._owner.servernames \ 389 #or self._owner.DB.pull_roster(x_split[1],x_split[0],barejid) != None: 390 if (x in self._owner.servernames) \ 391 or self.isFromOutside(domain) \ 392 or self._owner.DB.pull_roster(domain,name,barejid) != None: 393 self.DEBUG('Contact "%s" has to/both'%x,'warn') 394 #p = Presence(to=x,frm=session.peer,typ='probe') 395 #p.setAttr('type','probe') 396 #session.dispatch(p) 397 if self._data.has_key(str(x)): 398 # Send the presence information of this client 399 for res in self._data[str(x)].keys(): 400 session.enqueue(self._data[x][res]) 401 elif x in self._owner.servernames or str(x) in self._owner.servernames: 402 # Generate an 'available' presence on our behalf (the server is always available) 403 session.enqueue(Presence(to=session.peer, frm=str(x), typ='available')) 404 elif self.isFromOutside(domain): 405 s=self._owner.S2S(session.ourname,domain) 406 if s: 407 s.enqueue(Presence(to=x, frm=session.peer, typ='probe')) 408 else: 409 # Generate an 'unavailable' presence on behalf of this client 410 session.enqueue(Presence(to=session.peer, frm=str(x), typ='unavailable')) 411 412 self.DEBUG('Finished for "%s" (to/both)'%x,'info') 413 414 except Exception, err: 415 self.DEBUG("PRESENCE_BROADCAST_ERR: %s\nx:%s\ny:%s"%(err,x,y),'error')
416
417 - def update(self,barejid):
418 self.DEBUG("Router update","info") 419 pri=-1 420 s=None 421 for resource in self._data[barejid].keys(): 422 rpri=int(self._data[barejid][resource].getTagData('priority')) 423 if rpri>pri: s=self._owner.getsession(barejid+'/'+resource) 424 if s: 425 self._owner.activatesession(s,barejid) 426 else: 427 self._owner.deactivatesession(barejid)
428 429
430 - def safeguard(self,session,stanza):
431 if stanza.getNamespace() not in [NS_CLIENT,NS_SERVER]: return # this is not XMPP stanza 432 433 if session._session_state<SESSION_AUTHED: # NOT AUTHED yet (stream's stuff already done) 434 session.terminate_stream(STREAM_NOT_AUTHORIZED) 435 raise NodeProcessed 436 437 frm=stanza['from'] 438 to=stanza['to'] 439 if stanza.getNamespace()==NS_SERVER: 440 if not frm or not to \ 441 or frm.getDomain()<>session.peer \ 442 or to.getDomain()<>session.ourname: 443 session.terminate_stream(STREAM_IMPROPER_ADDRESSING) 444 raise NodeProcessed 445 else: 446 if frm and frm<>session.peer: # if the from address specified and differs 447 if frm.getResource() or not frm.bareMatch(session.peer): # ...it can differ only while comparing inequally 448 session.terminate_stream(STREAM_INVALID_FROM) 449 raise NodeProcessed 450 451 if session._session_state<SESSION_BOUND: # NOT BOUND yet (bind stuff already done) 452 if stanza.getType()<>'error': session.send(Error(stanza,ERR_NOT_AUTHORIZED)) 453 raise NodeProcessed 454 455 if name=='presence' and session._session_state<SESSION_OPENED: 456 if stanza.getType()<>'error': session.send(Error(stanza,ERR_NOT_ALLOWED)) 457 raise NodeProcessed 458 stanza.setFrom(session.peer)
459
460 - def subscriber(self,session,stanza):
461 """ 462 Subscription manager that actually works (cough, cough, BoP, cough, cough ;-) 463 464 0. COMMON TASKS 465 0.1 Get 'from' and 'to' 466 0.2 Get the session of the receiver 467 """ 468 469 try: 470 supposed_from = JID(session.peer) 471 if supposed_from.getNode() == '': # and supposed_from.getDomain() not in self._owner.servernames: 472 fromOutside = self.isFromOutside(JID(supposed_from).getDomain()) 473 if fromOutside: 474 frm = str(stanza.getFrom()) 475 else: 476 frm = str(JID(session.peer).getStripped()) 477 else: 478 frm = str(JID(session.peer).getStripped()) 479 fromOutside = False 480 #print "### JID FOUND = %s"%(str(frm)) 481 except: 482 self.DEBUG('Could not state real from', 'error') 483 raise NodeProcessed 484 485 to = stanza['to'] 486 session_jid = [to.getNode(), to.getDomain()] 487 jfrom_node = JID(session.peer).getNode() 488 jfrom_domain = JID(session.peer).getDomain() 489 490 # Construct correct bareto 491 if session_jid[0] == '': bareto = session_jid[1] # OMG it's a component! 492 else: bareto = session_jid[0] + '@' + session_jid[1] # It's a regular client 493 494 # 0.2 Get the session of the receiver 495 s = None 496 s = self._owner.getsession(bareto) 497 if s == None: 498 if self.isFromOutside(to.getDomain()): 499 s=self._owner.S2S(session.ourname,to.getDomain()) 500 501 if s == None: 502 self.DEBUG("DANGER! There is still no session for the receiver","error") 503 # There was no session for the receiver 504 # TODO: Store the stanza and relay it later 505 raise NodeProcessed 506 507 508 if not fromOutside: 509 contact = self._owner.DB.pull_roster(str(jfrom_domain),str(jfrom_node),str(bareto)) 510 #print "### CONTACT: " + str(contact) 511 else: contact = None 512 513 # Inbound-Outbound Shit 514 if not self.isFromOutside(to.getDomain()): 515 inbound = True 516 inbound_contact = self._owner.DB.pull_roster(to.getDomain(), to.getNode(), frm) 517 self.DEBUG("Inbound Contact: %s : %s"%(bareto,str(inbound_contact)),"warn") 518 else: 519 inbound = False 520 521 if not self.isFromOutside(jfrom_domain): 522 outbound = True 523 outbound_contact = self._owner.DB.pull_roster( str(jfrom_domain), str(jfrom_node), str(bareto)) 524 self.DEBUG("Outbound Contact: %s : %s"%(frm,str(outbound_contact)),"warn") 525 else: 526 outbound = False 527 528 529 if stanza.getType() == "subscribe": 530 #print "### IT'S SUBSCRIBE" 531 pass 532 """ 533 1. SUBSCRIBE 534 1.2 Modify and push the sender's roster 535 1.3 Relay the presence stanza to the receiver 536 1.4 Do the victory dance and raise NodeProcessed 537 """ 538 539 """ 540 self.DEBUG("Checking if contact is already subscribed","info") 541 try: 542 if contact and contact['subscription'] in ['both', 'to']: 543 # We're ALREADY subscribed, we should dismiss this request to prevent 544 # infinite loops of presence stanzas 545 print "### CONTACT ALREADY SUBSCRIBED" 546 raise NodeProcessed 547 except KeyError: 548 # This contact wasn't in the roster or the subscription wasn't done 549 self.DEBUG("This contact wasn't in the roster or the subscription wasn't done","warn") 550 except AttributeError: 551 self.DEBUG("The subscription wasn't done","warn") 552 """ 553 554 #self.DEBUG("Contact is not subscribed yet. jfrom = %s %s"%(str(jfrom_node),str(jfrom_domain)),"info") 555 # 1.2 Modify and push the sender's roster (if it's a client of this server) 556 #if str(jfrom_domain) in self._owner.servernames: 557 route=False 558 if inbound: 559 560 """ 561 if contact and not fromOutside: 562 # Add 'ask' key to the roster 563 contact.update({'ask':'subscribe'}) 564 self._owner.DB.save_to_roster(jfrom_domain, jfrom_node, bareto, contact) 565 elif not fromOutside: 566 """ 567 568 # Create roster for this contact 569 #print "### JFROM = %s %s ; BARETO = %s"%(jfrom_node, jfrom_node, bareto) 570 # F?CKING inbound-outbound control schema 571 if inbound_contact and inbound_contact.has_key('subscription'): subs = inbound_contact['subscription'] 572 else: subs = "none" 573 if inbound_contact and inbound_contact.has_key('state'): state = inbound_contact['state'] 574 else: state = "" 575 576 #print "subs",subs,state,route 577 578 route = True 579 if subs == "none" and not state: state="pending_in" 580 elif subs == "none" and state == "pending_out": state = "pending_out_in" 581 elif subs == "to" and not state: state = "pending_in" 582 else: route = False 583 584 #print "subs",subs,state,route 585 586 if subs in ['from','both']: 587 session.enqueue(Presence(frm=bareto,typ='subscribed',to=frm)) # Auto-Reply 588 raise NodeProcessed 589 590 try: 591 if not inbound_contact: inbound_contact = {} 592 inbound_contact.update({'subscription':subs}) 593 inbound_contact.update({'state':state}) 594 self._owner.DB.save_to_roster(to.getDomain(), to.getNode(), frm, inbound_contact) 595 596 self._owner.ROSTER.RosterPushOneToClient(contact=frm,to=bareto,to_session=s) 597 #print "route: ",route,outbound 598 599 except: 600 self.DEBUG("Could not create roster for client "+str(bareto), 'error') 601 602 603 if outbound: 604 try: 605 # F?CKING inbound-outbound control schema 606 if outbound_contact and outbound_contact.has_key('subscription'):subs=outbound_contact['subscription'] 607 else: subs = "none" 608 if outbound_contact and outbound_contact.has_key('state'): state = outbound_contact['state'] 609 else: state = "" 610 611 if subs == "none" and not state: state="pending_out" 612 elif subs == "none" and state == "pending_in": state = "pending_out_in" 613 elif subs == "from" and not state: state = "pending_out" 614 615 616 if not outbound_contact: outbound_contact = {} 617 outbound_contact.update({'subscription':subs}) 618 outbound_contact.update({'state':state}) 619 if not subs or subs == "none": outbound_contact.update({'ask':'subscribe'}) 620 self._owner.DB.save_to_roster(jfrom_domain, jfrom_node, bareto, outbound_contact) 621 622 self._owner.ROSTER.RosterPushOneToClient(contact=bareto,to=frm,to_session=session) 623 624 except: 625 self.DEBUG("Could not create roster for client "+str(frm), 'error') 626 627 628 # 1.3 Relay the presence stanza to the receiver 629 if outbound or route: 630 self.DEBUG("Routing stanza to receiver: "+str(bareto),"info") 631 stanza.setFrom(frm) 632 stanza.setNamespace(NS_CLIENT) 633 s.enqueue(stanza) 634 635 # 1.4 Do the victory dance 636 # o 637 # \|/ 638 # A 639 # / \ 640 raise NodeProcessed 641 642 elif stanza.getType() == "subscribed": 643 #print "### IT'S SUBSCRIBED" 644 """ 645 2. SUBSCRIBED 646 2.1 Relay the presence stanza to the receiver 647 2.2 Modify and push the receiver's roster 648 2.2.2 Modify and push the sender's roster ?? 649 2.3 Send a presence probe on behalf of the sender 650 """ 651 route=False 652 if inbound: 653 #print "### JFROM = %s %s ; BARETO = %s"%(jfrom_node, jfrom_node, bareto) 654 try: 655 # F?CKING inbound-outbound control schema 656 if inbound_contact and inbound_contact.has_key('subscription'): subs = inbound_contact['subscription'] 657 else: subs = "none" 658 if inbound_contact and inbound_contact.has_key('state'): state = inbound_contact['state'] 659 else: state = "" 660 661 route = True 662 if subs == "none" and state == "pending_out": subs = "to"; state = "" 663 elif subs == "none" and state == "pending_out_in": subs = "to"; state = "pending_in" 664 elif subs == "from" and state == "pending_out": subs = "both"; state = "" 665 elif subs == "from" and not state: subs = "both" #this is not standard 666 else: route = False 667 668 if not inbound_contact: inbound_contact = {} 669 inbound_contact.update({'subscription':subs}) 670 inbound_contact.update({'state':state}) 671 if inbound_contact.has_key('ask'): del inbound_contact['ask'] 672 self._owner.DB.save_to_roster(to.getDomain(), to.getNode(), frm, inbound_contact) 673 674 #self._owner.ROSTER.RosterPushOne(session,stanza) 675 self._owner.ROSTER.RosterPushOneToClient(contact=frm, to=bareto, to_session=s) 676 677 except: 678 self.DEBUG("Could not create roster for client "+str(bareto), 'error') 679 680 if outbound: 681 try: 682 # F?CKING inbound-outbound control schema 683 if outbound_contact and outbound_contact.has_key('subscription'): subs = outbound_contact['subscription'] 684 else: subs = None 685 if outbound_contact and outbound_contact.has_key('state'): state = outbound_contact['state'] 686 else: state = None 687 688 if subs == "none" and state == "pending_in": subs = "from"; state = ""; route = True 689 elif subs == "none" and state == "pending_out_in": subs = "from"; state = "pending_out"; route = True 690 elif subs == "to" and state == "pending_in": subs = "both"; state = ""; route = True 691 692 if not outbound_contact: outbound_contact = {} 693 outbound_contact.update({'subscription':subs}) 694 outbound_contact.update({'state':state}) 695 self._owner.DB.save_to_roster(jfrom_domain, jfrom_node, bareto, outbound_contact) 696 697 #self._owner.ROSTER.RosterPushOne(session,stanza) 698 self._owner.ROSTER.RosterPushOneToClient(contact=bareto,to=frm, to_session=session) 699 700 except: 701 self.DEBUG("Could not create roster for client "+str(frm), 'error') 702 703 if route: 704 stanza.setFrom(frm) 705 stanza.setNamespace(NS_CLIENT) 706 s.enqueue(stanza) 707 self.DEBUG("Subscribed-stanza relied to proper client","ok") 708 709 # 2.3 Send an available presence probe on behalf of the one who allows subscription 710 try: 711 barefrom = JID(frm).getStripped() 712 if self._data.has_key(barefrom): 713 for pres in self._data[barefrom].values(): 714 pres_c = copy.copy(pres) 715 pres_c.setType('available') 716 pres_c.setTo(bareto) 717 s.enqueue(pres_c) 718 except Exception, e: 719 print "### EXCEPTION " + str(e) 720 721 #print "### PRESENCE STANZA ON BEHALF OF " + barefrom 722 #self._owner.DB.print_database() 723 724 raise NodeProcessed 725 726 elif stanza.getType() == "unsubscribe": 727 #print "### IT'S UNSUBSCRIBE" 728 """ 729 3. UNSUBSCRIBE 730 3.1 Check if the contact is already subscribed 731 3.2 Modify and push the sender's roster 732 3.3 Relay the presence stanza to the receiver 733 3.4 Send the sender (wow) an 'unsubscribed' presence probe on behalf of the receiver 734 3.5 Send the sender (wowwow) probes of 'unavailable' on behalf of the receiver 735 """ 736 737 route = False 738 if inbound: 739 740 #print "### JFROM = %s %s ; BARETO = %s"%(jfrom_node, jfrom_node, bareto) 741 try: 742 # F?CKING inbound-outbound control schema 743 if inbound_contact and inbound_contact.has_key('subscription'): subs = inbound_contact['subscription'] 744 else: subs = "none" 745 if inbound_contact and inbound_contact.has_key('state'): state = inbound_contact['state'] 746 else: state = "" 747 748 if subs == "none" and state == "pending_in": state=""; route = True 749 elif subs == "none" and state == "pending_out_in": state = "pending_out"; route = True 750 elif subs == "to" and state == "pending_in": state=""; route = True 751 elif subs == "from" and not state : subs="none"; route = True 752 elif subs == "from" and state == "pending_out" : subs="none"; route = True 753 elif subs == "both" and not state : subs="to"; route = True 754 755 if not inbound_contact: inbound_contact = {} 756 inbound_contact.update({'subscription':subs}) 757 inbound_contact.update({'state':state}) 758 self._owner.DB.save_to_roster(to.getDomain(), to.getNode(), frm, inbound_contact) 759 760 self._owner.ROSTER.RosterPushOneToClient(contact=frm,to=bareto,to_session=s) 761 762 except: 763 self.DEBUG("Could not create roster for client "+str(bareto), 'error') 764 765 766 if outbound: 767 try: 768 # F?CKING inbound-outbound control schema 769 if outbound_contact and outbound_contact.has_key('subscription'):subs=outbound_contact['subscription'] 770 else: subs = "none" 771 if outbound_contact and outbound_contact.has_key('state'): state = outbound_contact['state'] 772 else: state = "" 773 774 if subs == "none" and state == "pending_out": state="" #; route = True 775 elif subs == "none" and state == "pending_out_in": state = "pending_in" #; route = True 776 elif subs == "to" and not state: subs = "none" #; route = True 777 elif subs == "to" and state == "pending_in" : subs="none" #; route = True 778 elif subs == "from" and state == "pending_out" : state="" #; route = True 779 elif subs == "both" and not state : subs="from" #; route = True 780 781 if not outbound_contact: outbound_contact = {} 782 outbound_contact.update({'subscription':subs}) 783 outbound_contact.update({'state':state}) 784 self._owner.DB.save_to_roster(jfrom_domain, jfrom_node, bareto, outbound_contact) 785 786 self._owner.ROSTER.RosterPushOneToClient(contact=bareto,to=frm,to_session=session) 787 788 except: 789 self.DEBUG("Could not create roster for client "+str(frm), 'error') 790 791 # 1.3 Relay the presence stanza to the receiver 792 if outbound or route: 793 stanza.setFrom(frm) 794 stanza.setNamespace(NS_CLIENT) 795 s.enqueue(stanza) 796 if route: 797 session.enqueue(Presence(to=frm,frm=bareto,typ="unsubscribed")) 798 799 raise NodeProcessed 800 801 # 3.5 Send the sender (wowwow) probes of 'unavailable' on behalf of the receiver 802 """ 803 for pres in self._data[bareto].values(): 804 p = copy.copy(pres) 805 p.setType('unavailable') 806 p.setTo(frm) 807 session.enqueue(p) 808 """ 809 810 raise NodeProcessed 811 812 813 elif stanza.getType() == "unsubscribed": 814 pass 815 #print "### IT'S UNSUBSCRIBED" 816 """ 817 4. UNSUBSCRIBED 818 4.1 Relay the presence stanza to the receiver 819 4.2 Modify and push the receiver and sender rosters 820 """ 821 822 route = False 823 if inbound: 824 #print "### JFROM = %s %s ; BARETO = %s"%(jfrom_node, jfrom_node, bareto) 825 try: 826 # F?CKING inbound-outbound control schema 827 if inbound_contact and inbound_contact.has_key('subscription'): subs = inbound_contact['subscription'] 828 else: subs = "none" 829 if inbound_contact and inbound_contact.has_key('state'): state = inbound_contact['state'] 830 else: state = "" 831 832 if subs == "none" and state == "pending_out": subs = "none"; state = ""; route = True 833 elif subs == "none" and state == "pending_out_in": subs = "none"; state = "pending_in"; route = True 834 elif subs == "to" and not state: subs = "none"; route = True 835 elif subs == "to" and state == "pending_in": subs = "none"; route = True 836 elif subs == "from" and state == "pending_out": subs = "from"; route = True 837 elif subs == "both" and not state: subs = "from"; route = True 838 839 if not inbound_contact: inbound_contact = {} 840 inbound_contact.update({'subscription':subs}) 841 inbound_contact.update({'state':state}) 842 self._owner.DB.save_to_roster(to.getDomain(), to.getNode(), frm, inbound_contact) 843 844 #self._owner.ROSTER.RosterPushOne(session,stanza) 845 self._owner.ROSTER.RosterPushOneToClient(contact=frm,to=bareto,to_session=s) 846 847 except: 848 self.DEBUG("Could not create roster for client "+str(bareto), 'error') 849 850 if outbound: 851 try: 852 # F?CKING inbound-outbound control schema 853 if outbound_contact and outbound_contact.has_key('subscription'): subs = outbound_contact['subscription'] 854 else: subs = "none" 855 if outbound_contact and outbound_contact.has_key('state'): state = outbound_contact['state'] 856 else: state = "" 857 858 if subs == "none" and state == "pending_in": state = ""; route = True 859 elif subs == "none" and state == "pending_out_in": state = "pending_out"; route = True 860 elif subs == "to" and state == "pending_in": state = ""; route = True 861 elif subs == "from" and not state: subs = "none"; route = True 862 elif subs == "from" and state == "pending_out": subs = "none"; route = True 863 elif subs == "both" and not state: subs = "to"; route = True 864 865 if not outbound_contact: outbound_contact = {} 866 outbound_contact.update({'subscription':subs}) 867 outbound_contact.update({'state':state}) 868 self._owner.DB.save_to_roster(jfrom_domain, jfrom_node, bareto, outbound_contact) 869 870 #self._owner.ROSTER.RosterPushOne(session,stanza) 871 self._owner.ROSTER.RosterPushOneToClient(contact=bareto, to=frm, to_session=session) 872 873 874 except: 875 self.DEBUG("Could not create roster for client "+str(frm), 'error') 876 877 if route: 878 stanza.setFrom(frm) 879 stanza.setNamespace(NS_CLIENT) 880 s.enqueue(stanza) 881 882 """ 883 # 4.3 Send the receiver probes of 'unavailable' on behalf of the sender 884 # This is highly optional, so we don't do it 885 self.DEBUG("Now, we are going to send 'unavailable' probes on behalf of the client "+str(frm), 'warn') 886 for pres in self._data[frm].values(): 887 pres.setType('unavailable') 888 s.enqueue(pres) 889 """ 890 891 raise NodeProcessed
892 893 894 895 896 """ 897 def balance_of_presence(self,session,stanza): 898 "Figures-out what should be done to a particular contact" 899 self.DEBUG('###BoP: SYSTEM PASS-THROUGH INSTIGATED [%s]'%unicode(stanza).encode('utf-8'),'warn') 900 #Predefined settings 901 # ["subscribe", "subscribed", "unsubscribe", "unsubscribed"] 902 903 #Stanza Stuff 904 try: 905 frm=stanza['from'] 906 frm_node=frm.getNode() 907 frm_domain=frm.getDomain() 908 outfrom=frm_node+'@'+frm_domain 909 self.DEBUG('###BoP: RECIEVED STANZA WITH \'FROM\' ATTR','warn') 910 except: 911 frm = None 912 self.DEBUG('###BoP: RECIEVED STANZA WITHOUT \'FROM\' ATTR','warn') 913 914 915 component = False 916 session_jid = session.getSplitJID() 917 to=stanza['to'] 918 if not to: return # Not for us. 919 to_node=to.getNode() 920 #if not to_node: return # Yep, not for us. 921 to_domain=to.getDomain() 922 if str(to_domain) in self._owner.components.keys(): component = True 923 if not component or (component and to_node): bareto=to_node+'@'+to_domain 924 else: bareto = to_domain 925 926 if component: 927 s = self._owner.getsession(to) 928 if s: 929 s.enqueue(stanza) 930 raise NodeProcessed 931 932 933 #bareto=to_node+'@'+to_domain 934 935 # 1. If the user wants to request a subscription to the contact's presence information, 936 # the user's client MUST send a presence stanza of type='subscribe' to the contact: 937 938 if stanza.getType() == 'subscribe': 939 if not self.isFromOutside(session_jid[1]) and frm == None: 940 self.DEBUG('###BoP: TYP=SUBSCRIBE;U->C','warn') 941 942 # 2. As a result, the user's server MUST initiate a second roster push to all of the user's 943 # available resources that have requested the roster, setting the contact to the pending 944 # sub-state of the 'none' subscription state; this pending sub-state is denoted by the 945 # inclusion of the ask='subscribe' attribute in the roster item: 946 # 947 # Note: If the user did not create a roster item before sending the subscription request, 948 # the server MUST now create one on behalf of the user: 949 950 ''' 951 <iq type='set' id='set1'> 952 <query xmlns='jabber:iq:roster'> 953 <item 954 jid='contact@example.org' 955 subscription='none' 956 ask='subscribe'> 957 </item> 958 </query> 959 </iq> 960 ''' 961 962 963 rsess = Iq(typ='set') 964 rsess.T.query.setNamespace(NS_ROSTER) 965 newitem = rsess.T.query.NT.item 966 newitem.setAttr('jid',bareto) 967 newitem.setAttr('ask','subscribe') 968 # Subscription? 969 #Dispatch? 970 session.dispatch(rsess) 971 self._owner.ROSTER.RosterAdd(session,rsess) #add to roster! 972 973 #{'attr':{'ask':'subscribe'}} 974 self._owner.ROSTER.RosterPushOne(session,stanza) #We'll let roster services take the bullet. 975 976 # 3. The user's server MUST also stamp the presence stanza of type "subscribe" with the user's 977 # bare JID (i.e., <user@example.com>) as the 'from' address (if the user provided a 'from' 978 # address set to the user's full JID, the server SHOULD remove the resource identifier). If 979 # the contact is served by a different host than the user, the user's server MUST route the 980 # presence stanza to the contact's server for delivery to the contact (this case is assumed 981 # throughout; however, if the contact is served by the same host, then the server can simply 982 # deliver the presence stanza directly): 983 984 stanza.setFrom(session.getBareJID()) 985 self.DEBUG('###BoP: TYP=SUBSCRIBE;U->C [DONE]','warn') 986 elif not self.isFromOutside(to_domain) and frm != None: 987 self.DEBUG('###BoP: THE UN-EXPECTED WAS TRIGGERED','warn') 988 pass 989 elif stanza.getType() == 'subscribed': 990 991 # 5. As a result, the contact's server (1) MUST initiate a roster push to all available resources 992 # associated with the contact that have requested the roster, containing a roster item for the 993 # user with the subscription state set to 'from' (the server MUST send this even if the contact 994 # did not perform a roster set); (2) MUST return an IQ result to the sending resource indicating 995 # the success of the roster set; (3) MUST route the presence stanza of type "subscribed" to the user, 996 # first stamping the 'from' address as the bare JID (<contact@example.org>) of the contact; and 997 # (4) MUST send available presence from all of the contact's available resources to the user: 998 ''' 999 <iq type='set' to='contact@example.org/resource'> 1000 <query xmlns='jabber:iq:roster'> 1001 <item 1002 jid='user@example.com' 1003 subscription='from' 1004 name='SomeUser'> 1005 <group>SomeGroup</group> 1006 </item> 1007 </query> 1008 </iq> 1009 ''' 1010 if not self.isFromOutside(session_jid[1]) and frm != None: 1011 self.DEBUG('###BoP: TYP=SUBSCRIBED;C->U','warn') 1012 roster = self._owner.DB.pull_roster(session_jid[1],session_jid[0],bareto) 1013 if roster == None: 1014 self.DEBUG('###BoP: TYP=SUBSCRIBED;U->C [NO ROSTER; KILLING NODEPROCESS!','error') 1015 raise NodeProcessed 1016 rsess = Iq(typ='set') 1017 rsess.NT.query.setNamespace(NS_ROSTER) 1018 newitem = rsess.T.query.NT.item 1019 newitem.setAttr('jid',bareto) 1020 self.DEBUG('###BoP: TYP=SUBSCRIBED;U->C SUBSCRIPTION=%s'%roster['subscription'],'warn') 1021 if roster['subscription'] != 'to': 1022 self.DEBUG('###BoP: TYP=SUBSCRIBED;U->C [TO IS NOT ACTIVE!]','warn') 1023 newitem.setAttr('subscription','from') 1024 else: 1025 self.DEBUG('###BoP: TYP=SUBSCRIBED;U->C [TO IS ACTIVE!]','warn') 1026 newitem.setAttr('subscription','both') 1027 newitem.setAttr('ask','InternalDelete') 1028 self._owner.ROSTER.RosterAdd(session,rsess) #add to roster! 1029 1030 self._owner.ROSTER.RosterPushOne(session,stanza) #,{'attr':{'ask':'subscribe'}}) 1031 barejid = session.getBareJID() 1032 stanza.setFrom(barejid) 1033 session.dispatch(stanza) 1034 s=None 1035 for resource in self._data[barejid].keys(): 1036 s=self._owner.getsession(barejid+'/'+resource) 1037 if s: self.broadcastAvailable(s) 1038 self.DEBUG('###BoP: TYP=SUBSCRIBED;C->U [DONE]','warn') 1039 self.DEBUG('###BoP: PASS-THROUGH COMPLETE','warn') 1040 raise NodeProcessed 1041 return session,stanza 1042 1043 # 6. Upon receiving the presence stanza of type "subscribed" addressed to the user, the user's 1044 # server MUST first verify that the contact is in the user's roster with either of the following 1045 # states: (a) subscription='none' and ask='subscribe' or (b) subscription='from' and ask='subscribe'. 1046 # If the contact is not in the user's roster with either of those states, the user's server MUST 1047 # silently ignore the presence stanza of type "subscribed" (i.e., it MUST NOT route it to the user, 1048 # modify the user's roster, or generate a roster push to the user's available resources). If the 1049 # contact is in the user's roster with either of those states, the user's server (1) MUST deliver 1050 # the presence stanza of type "subscribed" from the contact to the user; (2) MUST initiate a roster 1051 # push to all of the user's available resources that have requested the roster, containing an updated 1052 # roster item for the contact with the 'subscription' attribute set to a value of "to"; and (3) MUST 1053 # deliver the available presence stanza received from each of the contact's available resources to 1054 # each of the user's available resources: 1055 elif not self.isFromOutside(to_domain) and frm == None: 1056 self.DEBUG('###BoP: TYP=SUBSCRIBED;U->C','warn') 1057 roster = self._owner.DB.pull_roster(to_domain,to_node,outfrom) 1058 if roster == None: raise NodeProcessed 1059 ask_good = False 1060 subscription_good = False 1061 from_active = False 1062 for x,y in roster.iteritems(): 1063 if x=='ask' and y=='subscribe': ask_good = True 1064 if x=='subscription' and y in ['none','from']: subscription_good = True 1065 if y == 'from': from_active = True 1066 if subscription_good==True and ask_good==True: break 1067 1068 if subscription_good!=True and ask_good!=True: raise NodeProcessed 1069 1070 rsess = Iq(typ='set') 1071 rsess.T.query.setNamespace(NS_ROSTER) 1072 newitem = rsess.T.query.NT.item 1073 newitem.setAttr('jid',outfrom) 1074 if from_active == True: 1075 self.DEBUG('###BoP: TYP=SUBSCRIBED;U->C [FROM IS ACTIVE!]','warn') 1076 newitem.setAttr('subscription','both') 1077 else: 1078 self.DEBUG('###BoP: TYP=SUBSCRIBED;U->C [FROM IS NOT ACTIVE!]','warn') 1079 newitem.setAttr('subscription','to') 1080 newitem.setAttr('ask','InternalDelete') 1081 self._owner.ROSTER.RosterAdd(session,rsess) #add to roster! 1082 1083 self._owner.ROSTER.RosterPushOne(session,stanza) #,{'attr':{'ask':'subscribe'}}) 1084 self.DEBUG('###BoP: TYP=SUBSCRIBED;U->C [DONE]','warn') 1085 elif stanza.getType() == 'unsubscribe': 1086 1087 if not self.isFromOutside(session_jid[1]) and frm == None: 1088 self.DEBUG('###BoP: TYP=UNSUBSCRIBE;U->C','warn') 1089 roster = self._owner.DB.pull_roster(session_jid[1],session_jid[0],bareto) 1090 if roster == None: raise NodeProcessed 1091 rsess = Iq(typ='set') 1092 rsess.T.query.setNamespace(NS_ROSTER) 1093 newitem = rsess.T.query.NT.item 1094 newitem.setAttr('jid',outfrom) 1095 if roster['subscription'] == 'both': 1096 newitem.setAttr('subscription','from') 1097 else: 1098 newitem.setAttr('subscription','none') 1099 newitem.setAttr('ask','InternalDelete') 1100 self._owner.ROSTER.RosterAdd(session,rsess) #add to roster! 1101 1102 self._owner.ROSTER.RosterPushOne(session,stanza) #,{'attr':{'ask':'subscribe'}}) 1103 self.DEBUG('###BoP: TYP=UNSUBSCRIBE;U->C [DONE]','warn') 1104 1105 if not self.isFromOutside(to_domain) and frm != None: 1106 self.DEBUG('###BoP: TYP=UNSUBSCRIBE;C->U','warn') 1107 roster = self._owner.DB.pull_roster(to_domain,to_node,outfrom) 1108 if roster == None: raise NodeProcessed 1109 rsess = Iq(typ='set') 1110 rsess.T.query.setNamespace(NS_ROSTER) 1111 newitem = rsess.T.query.NT.item 1112 newitem.setAttr('jid',outfrom) 1113 if roster['subscription'] == 'both': 1114 newitem.setAttr('subscription','to') 1115 else: 1116 newitem.setAttr('subscription','none') 1117 newitem.setAttr('ask','InternalDelete') 1118 self._owner.ROSTER.RosterAdd(session,rsess) #add to roster! 1119 1120 self._owner.ROSTER.RosterPushOne(session,stanza) #,{'attr':{'ask':'subscribe'}}) 1121 self.DEBUG('###BoP: TYP=UNSUBSCRIBE;C->U [DONE]','warn') 1122 elif stanza.getType() == 'unsubscribed': 1123 self.DEBUG('###BoP: TYP=UNSUBSCRIBED','warn') 1124 1125 # 1. If the contact wants to refuse the request, the contact's client MUST send a presence stanza of 1126 # type "unsubscribed" to the user (instead of the presence stanza of type "subscribed" sent in Step 1127 # 6 of Section 8.2): 1128 1129 # 2. As a result, the contact's server MUST route the presence stanza of type "unsubscribed" to the user, 1130 # first stamping the 'from' address as the bare JID (<contact@example.org>) of the contact: 1131 if not self.isFromOutside(session_jid[1]) and frm == None: 1132 stanza.setFrom(session.getBareJID()) 1133 1134 # Note: If the contact's server previously added the user to the contact's roster for tracking purposes, 1135 # it MUST remove the relevant item at this time. 1136 self._owner.DB.del_from_roster(session_jid[1],session_jid[0],bareto) 1137 1138 # 3. Upon receiving the presence stanza of type "unsubscribed" addressed to the user, the user's server 1139 # (1) MUST deliver that presence stanza to the user and (2) MUST initiate a roster push to all of the 1140 # user's available resources that have requested the roster, containing an updated roster item for the 1141 # contact with the 'subscription' attribute set to a value of "none" and with no 'ask' attribute: 1142 1143 if not self.isFromOutside(to_domain) and frm != None: 1144 self.DEBUG('###BoP: TYP=UNSUBSCRIBED;C->U','warn') 1145 roster = self._owner.DB.pull_roster(to_domain,to_node,outfrom) 1146 if roster == None: raise NodeProcessed 1147 rsess = Iq(typ='set') 1148 rsess.T.query.setNamespace(NS_ROSTER) 1149 newitem = rsess.T.query.NT.item 1150 newitem.setAttr('jid',outfrom) 1151 if roster['subscription'] == 'to': 1152 newitem.setAttr('subscription','none') 1153 elif roster['subscription'] == 'both': 1154 newitem.setAttr('subscription','from') 1155 else: 1156 newitem.setAttr('subscription','none') 1157 newitem.setAttr('ask','InternalDelete') 1158 self._owner.ROSTER.RosterAdd(session,rsess) #add to roster! 1159 1160 self._owner.ROSTER.RosterPushOne(session,stanza) #,{'attr':{'ask':'subscribe'}}) 1161 p=Presence(to=outfrom,frm=bareto,typ='unavailable') 1162 p.setNamespace(NS_CLIENT) 1163 session.dispatch(p) 1164 self.DEBUG('###BoP: TYP=UNSUBSCRIBED;C->U [DONE]','warn') 1165 1166 self.DEBUG('###BoP: PASS-THROUGH COMPLETE','warn') 1167 return session,stanza 1168 """ 1169
1170 - def karmatize_me_captain(self,s,stanza):
1171 karma = s.getKarma() 1172 data_len = len(unicode(stanza)) 1173 if karma != None and time.time() - karma['last_time'] >= 60: # reset counters and stuff! 1174 karma['last_time'] == time.time() 1175 karma['tot_up'] = 0 1176 karma['tot_down'] = 0 1177 if karma != None and karma['tot_up'] + data_len > karma['up']: 1178 s.send(Error(stanza,ERR_NOT_ALLOWED)) 1179 raise NodeProcessed 1180 else: 1181 if karma != None: 1182 karma['tot_up'] += data_len 1183 s.updateKarma(karma)
1184
1185 - def intra_route(self,stanza):
1186 getsession=self._owner.getsession 1187 internal_router = getsession('__ir__@127.0.0.1/ROUTER') 1188 if internal_router: 1189 internal_router.enqueue(stanza)
1190 1191
1192 - def routerHandler(self,session,stanza,raiseFlag=True):
1193 """ XMPP-Core 9.1.1 rules """ 1194 1195 name=stanza.getName() 1196 self.DEBUG('Router handler called','info') 1197 #self.DEBUG('Recent Errors', "error") 1198 1199 if name == "presence": 1200 # They won't catch me alive ! 1201 return 1202 1203 try: 1204 self.DEBUG('with stanza %s'%(str(stanza)),'info') 1205 except: 1206 self.DEBUG('with UNKNOWN stanza','info') 1207 # try: print stanza 1208 # except: pass 1209 1210 1211 #print "### ROUTES: %s"%(self._owner.routes) 1212 #print stanza 1213 1214 """ 1215 # Sometimes, a presence stanza arrives here. Let's redirect it to presenceHandler 1216 if name == "presence" and stanza.getNamespace() == NS_SERVER: 1217 """ 1218 1219 to=stanza['to'] 1220 if stanza.getNamespace()==NS_CLIENT and \ 1221 (not to or to==session.ourname) and \ 1222 stanza.props in ( [NS_AUTH], [NS_REGISTER], [NS_BIND], [NS_SESSION] ): 1223 return 1224 1225 try: 1226 if not session.trusted and session.peer != '__ir__@127.0.0.1/ROUTER': self.safeguard(session,stanza) 1227 except: 1228 self.DEBUG("NOT SAFEGUARD","error") 1229 1230 if not to: #return 1231 stanza.setTo(session.ourname) 1232 to=stanza['to'] 1233 #print "### SESSION.OURNAME = " + str(session.ourname) 1234 1235 if self.pluginRelay(session,stanza) and raiseFlag: raise NodeProcessed 1236 self.DEBUG("Stanza not for a plugin","warn") 1237 1238 #Karma stuff! 1239 #if name != 'presence': self.karmatize_me_captain(session,stanza) 1240 1241 domain=to.getDomain() 1242 #print "DOMAIN:",domain 1243 1244 component = False 1245 """ 1246 component_jids = [] 1247 try: 1248 for v in self._owner.components.values(): 1249 print "V: "+ str(v) 1250 if v.has_key('jid'): 1251 component_jids.append(v['jid']) 1252 except Exception, e: 1253 print str(e) 1254 """ 1255 1256 1257 getsession=self._owner.getsession 1258 if not self.isFromOutside(domain): 1259 #if domain in self._owner.servernames or domain in self._owner.components.keys(): 1260 for v in self._owner.components.values(): 1261 try: 1262 #if domain in v['jid']: 1263 if v['jid'] in domain: 1264 component = True 1265 break 1266 except: 1267 pass 1268 1269 #if domain in self._owner.components.keys(): component = True 1270 node=to.getNode() 1271 if not node and not component: 1272 return 1273 #self._owner.Privacy(session,stanza) # it will if raiseFlag: raise NodeProcessed if needed # Desactivado de momento 1274 if not component or (component and node): bareto=node+'@'+domain 1275 else: bareto = domain 1276 resource=to.getResource() 1277 1278 #print "***BARETO:",bareto 1279 1280 1281 # 1. If the JID is of the form <user@domain/resource> and an available resource matches the full JID, 1282 # the recipient's server MUST deliver the stanza to that resource. 1283 try: 1284 rpri=int(self._data[bareto][resource].getTagData('priority')) 1285 if rpri<0: 1286 session.enqueue(Error(stanza,ERR_SERVICE_UNAVAILABLE)) 1287 return 1288 except: 1289 rpri = None 1290 if resource and rpri != None and rpri>-1: 1291 to=bareto+'/'+resource 1292 s=getsession(to) 1293 if s: 1294 s.enqueue(stanza) 1295 if raiseFlag: raise NodeProcessed 1296 # else: 1297 ## print "SPOOOOOTTTTTTTSPOOOOOTTTTTTT 1" 1298 # self.intra_route(stanza) 1299 # raise NodeProcessed 1300 # 2. Else if the JID is of the form <user@domain> or <user@domain/resource> and the associated user account 1301 # does not exist, the recipient's server (a) SHOULD silently ignore the stanza (i.e., neither deliver it 1302 # nor return an error) if it is a presence stanza, (b) MUST return a <service-unavailable/> stanza error 1303 # to the sender if it is an IQ stanza, and (c) SHOULD return a <service-unavailable/> stanza error to the 1304 # sender if it is a message stanza. 1305 if component: 1306 node,domain = domain.split(".",1) 1307 if not self._owner.AUTH.isuser(node,domain): 1308 if name in ['iq','message']: 1309 if stanza.getType()<>'error': session.enqueue(Error(stanza,ERR_SERVICE_UNAVAILABLE)) 1310 if raiseFlag: raise NodeProcessed 1311 # 3. Else if the JID is of the form <user@domain/resource> and no available resource matches the full JID, 1312 # the recipient's server (a) SHOULD silently ignore the stanza (i.e., neither deliver it nor return an 1313 # error) if it is a presence stanza, (b) MUST return a <service-unavailable/> stanza error to the sender 1314 # if it is an IQ stanza, and (c) SHOULD treat the stanza as if it were addressed to <user@domain> if it 1315 # is a message stanza. 1316 if resource and name<>'message': 1317 self.intra_route(stanza) # Maybe there is one elsewhere? 1318 if name=='iq' and stanza.getType()<>'error': session.enqueue(Error(stanza,ERR_SERVICE_UNAVAILABLE)) 1319 if raiseFlag: raise NodeProcessed 1320 # 4. Else if the JID is of the form <user@domain> and there is at least one available resource available 1321 # for the user, the recipient's server MUST follow these rules: 1322 1323 pri=-1 1324 highest_pri = {'pri':0,'s':None} 1325 s=None 1326 try: 1327 for resource in self._data[bareto].keys(): 1328 rpri=int(self._data[bareto][resource].getTagData('priority')) 1329 if rpri>pri and rpri>=highest_pri['pri']: 1330 highest_pri['pri'] = rpri 1331 highest_pri['s'] = self._owner.getsession(bareto+'/'+resource) 1332 1333 if highest_pri['s'] != None: 1334 s=highest_pri['s'] 1335 else: 1336 s=getsession(to) 1337 except: 1338 s=getsession(to) 1339 if s: 1340 # 1. For message stanzas, the server SHOULD deliver the stanza to the highest-priority available 1341 # resource (if the resource did not provide a value for the <priority/> element, the server SHOULD 1342 # consider it to have provided a value of zero). If two or more available resources have the same 1343 # priority, the server MAY use some other rule (e.g., most recent connect time, most recent 1344 # activity time, or highest availability as determined by some hierarchy of <show/> values) 1345 # to choose between them or MAY deliver the message to all such resources. However, the server 1346 # MUST NOT deliver the stanza to an available resource with a negative priority; if the only 1347 # available resource has a negative priority, the server SHOULD handle the message as if there 1348 # were no available resources (defined below). In addition, the server MUST NOT rewrite the 'to' 1349 # attribute (i.e., it MUST leave it as <user@domain> rather than change it to <user@domain/resource>). 1350 if name=='message': 1351 s.enqueue(stanza) 1352 if raiseFlag: raise NodeProcessed 1353 # 2. For presence stanzas other than those of type "probe", the server MUST deliver the stanza to all 1354 # available resources; for presence probes, the server SHOULD reply based on the rules defined in 1355 # Presence Probes. In addition, the server MUST NOT rewrite the 'to' attribute (i.e., it MUST leave 1356 # it as <user@domain> rather than change it to <user@domain/resource>). 1357 # elif name=='presence' and stanza.getType() == 'probe': # differ to Presence Handler! 1358 # return self.presenceHandler(session,stanza,raiseFlag) 1359 elif name=='presence': 1360 self.DEBUG('Presence stanza detected! (%s)'%stanza['to'],'warn') 1361 if stanza.getType() == 'probe' and stanza['to'].getDomain() in self._owner.servernames: 1362 stanza.setAttr('internal','True') 1363 self.presenceHandler(session,stanza,raiseFlag) 1364 1365 if stanza.getType() in ["subscribe", "subscribed", "unsubscribe", "unsubscribed"]: 1366 #session,stanza = self.balance_of_presence(session,stanza) 1367 session,stanza = self.subscriber(session,stanza) 1368 1369 # all probes already processed so safely assuming "other" type 1370 ps = None 1371 for resource in self._data[bareto].keys(): 1372 ps=getsession(bareto+'/'+resource) 1373 if ps: ps.enqueue(stanza) 1374 if raiseFlag: raise NodeProcessed 1375 # 3. For IQ stanzas, the server itself MUST reply on behalf of the user with either an IQ result or an 1376 # IQ error, and MUST NOT deliver the IQ stanza to any of the available resources. Specifically, if 1377 # the semantics of the qualifying namespace define a reply that the server can provide, the server 1378 # MUST reply to the stanza on behalf of the user; if not, the server MUST reply with a 1379 # <service-unavailable/> stanza error. 1380 # UPDATE: Or not. 1381 if name=='iq': 1382 s.enqueue(stanza) 1383 if raiseFlag: raise NodeProcessed 1384 return 1385 # 5. Else if the JID is of the form <user@domain> and there are no available resources associated with 1386 # the user, how the stanza is handled depends on the stanza type: 1387 else: 1388 self.intra_route(stanza) ## Try to outsource this guy! 1389 1390 # 1. For presence stanzas of type "subscribe", "subscribed", "unsubscribe", and "unsubscribed", 1391 # the server MUST maintain a record of the stanza and deliver the stanza at least once (i.e., when 1392 # the user next creates an available resource); in addition, the server MUST continue to deliver 1393 # presence stanzas of type "subscribe" until the user either approves or denies the subscription 1394 # request (see also Presence Subscriptions). 1395 if name=='presence': 1396 if stanza.getType() == 'probe' and stanza['to'].getDomain() in self._owner.servernames: 1397 stanza.setAttr('internal','True') 1398 self.presenceHandler(session,stanza,raiseFlag) 1399 1400 if stanza.getType() in ["subscribe", "subscribed", "unsubscribe", "unsubscribed"]: 1401 #session,stanza = self.balance_of_presence(session,stanza) 1402 session,stanza = self.subscriber(session,stanza) 1403 #elif stanza.getType() == 'probe': # differ to Presence Handler! 1404 # return self.presenceHandler(session,stanza,raiseFlag) 1405 # 2. For all other presence stanzas, the server SHOULD silently ignore the stanza by not storing it 1406 # for later delivery or replying to it on behalf of the user. 1407 if raiseFlag: raise NodeProcessed 1408 # 3. For message stanzas, the server MAY choose to store the stanza on behalf of the user and deliver 1409 # it when the user next becomes available, or forward the message to the user via some other means 1410 # (e.g., to the user's email account). However, if offline message storage or message forwarding 1411 # is not enabled, the server MUST return to the sender a <service-unavailable/> stanza error. (Note: 1412 # Offline message storage and message forwarding are not defined in XMPP, since they are strictly a 1413 # matter of implementation and service provisioning.) 1414 elif name=='message': 1415 #self._owner.DB.store(domain,node,stanza) 1416 # if stanza.getType()<>'error': session.enqueue(Error(stanza,ERR_RECIPIENT_UNAVAILABLE)) 1417 if raiseFlag: raise NodeProcessed 1418 # 4. For IQ stanzas, the server itself MUST reply on behalf of the user with either an IQ result or 1419 # an IQ error. Specifically, if the semantics of the qualifying namespace define a reply that the 1420 # server can provide, the server MUST reply to the stanza on behalf of the user; if not, the server 1421 # MUST reply with a <service-unavailable/> stanza error. 1422 return 1423 else: 1424 s=getsession(domain) 1425 if not s: 1426 s=self._owner.S2S(session.ourname,domain) 1427 1428 s.enqueue(stanza) 1429 if raiseFlag: raise NodeProcessed
1430