2006-02-03 12:17:34 +00:00
## roster_nb.py
2008-06-09 00:32:02 +00:00
## based on roster.py
2006-02-03 12:17:34 +00:00
##
## Copyright (C) 2003-2005 Alexey "Snake" Nezhdanov
2008-06-09 00:32:02 +00:00
## modified by Dimitur Kirov <dkirov@gmail.com>
2006-02-03 12:17:34 +00:00
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2, or (at your option)
## any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
# $Id: roster.py,v 1.17 2005/05/02 08:38:49 snakeru Exp $
'''
Simple roster implementation . Can be used though for different tasks like
mass - renaming of contacts .
'''
2008-12-28 14:53:20 +00:00
from protocol import JID , Iq , Presence , Node , NodeProcessed , NS_ROSTER
2009-01-09 00:49:58 +00:00
from plugin import PlugIn
2008-06-09 00:32:02 +00:00
2008-07-02 23:29:10 +00:00
import logging
log = logging . getLogger ( ' gajim.c.x.roster_nb ' )
2008-06-09 00:32:02 +00:00
class NonBlockingRoster ( PlugIn ) :
2008-07-18 00:34:49 +00:00
''' Defines a plenty of methods that will allow you to manage roster.
2008-06-09 00:32:02 +00:00
Also automatically track presences from remote JIDs taking into
account that every JID can have multiple resources connected . Does not
currently support ' error ' presences .
You can also use mapping interface for access to the internal representation of
contacts in roster .
2008-07-18 00:34:49 +00:00
'''
2009-06-02 22:48:32 +02:00
def __init__ ( self , version = ' ' ) :
2008-07-18 00:34:49 +00:00
''' Init internal variables. '''
2008-06-09 00:32:02 +00:00
PlugIn . __init__ ( self )
2009-06-02 22:48:32 +02:00
self . version = version
2008-06-09 00:32:02 +00:00
self . _data = { }
self . set = None
self . _exported_methods = [ self . getRoster ]
def Request ( self , force = 0 ) :
2008-07-18 00:34:49 +00:00
''' Request roster from server if it were not yet requested
( or if the ' force ' argument is set ) . '''
2008-06-09 00:32:02 +00:00
if self . set is None : self . set = 0
elif not force : return
2009-06-02 22:48:32 +02:00
iq = Iq ( ' get ' , NS_ROSTER )
iq . setTagAttr ( ' query ' , ' ver ' , self . version )
2009-06-27 17:56:04 +02:00
id_ = self . _owner . getAnID ( )
iq . setID ( id_ )
2009-06-02 22:48:32 +02:00
self . _owner . send ( iq )
2008-07-02 23:29:10 +00:00
log . info ( ' Roster requested from server ' )
2009-06-27 17:56:04 +02:00
return id_
2008-06-09 00:32:02 +00:00
def RosterIqHandler ( self , dis , stanza ) :
2008-07-18 00:34:49 +00:00
''' Subscription tracker. Used internally for setting items state in
internal roster representation . '''
2008-06-09 00:32:02 +00:00
sender = stanza . getAttr ( ' from ' )
if not sender is None and not sender . bareMatch (
self . _owner . User + ' @ ' + self . _owner . Server ) :
return
2008-12-15 21:06:08 +00:00
query = stanza . getTag ( ' query ' )
if query :
2009-06-27 17:56:04 +02:00
self . version = stanza . getTagAttr ( ' query ' , ' ver ' )
if self . version is None :
self . version = ' '
2008-12-15 21:06:08 +00:00
for item in query . getTags ( ' item ' ) :
jid = item . getAttr ( ' jid ' )
if item . getAttr ( ' subscription ' ) == ' remove ' :
if self . _data . has_key ( jid ) : del self . _data [ jid ]
2008-12-28 14:53:20 +00:00
# Looks like we have a workaround
# raise NodeProcessed # a MUST
2008-12-15 21:06:08 +00:00
log . info ( ' Setting roster item %s ... ' % jid )
if not self . _data . has_key ( jid ) : self . _data [ jid ] = { }
self . _data [ jid ] [ ' name ' ] = item . getAttr ( ' name ' )
self . _data [ jid ] [ ' ask ' ] = item . getAttr ( ' ask ' )
self . _data [ jid ] [ ' subscription ' ] = item . getAttr ( ' subscription ' )
self . _data [ jid ] [ ' groups ' ] = [ ]
if not self . _data [ jid ] . has_key ( ' resources ' ) : self . _data [ jid ] [ ' resources ' ] = { }
for group in item . getTags ( ' group ' ) : self . _data [ jid ] [ ' groups ' ] . append ( group . getData ( ) )
2008-06-09 00:32:02 +00:00
self . _data [ self . _owner . User + ' @ ' + self . _owner . Server ] = { ' resources ' : { } , ' name ' : None , ' ask ' : None , ' subscription ' : None , ' groups ' : None , }
self . set = 1
2008-12-28 14:53:20 +00:00
# Looks like we have a workaround
# raise NodeProcessed # a MUST. Otherwise you'll get back an <iq type='error'/>
2008-06-09 00:32:02 +00:00
def PresenceHandler ( self , dis , pres ) :
2008-07-18 00:34:49 +00:00
''' Presence tracker. Used internally for setting items ' resources state in
internal roster representation . '''
2008-06-09 00:32:02 +00:00
jid = pres . getFrom ( )
if not jid :
# If no from attribue, it's from server
jid = self . _owner . Server
jid = JID ( jid )
if not self . _data . has_key ( jid . getStripped ( ) ) : self . _data [ jid . getStripped ( ) ] = { ' name ' : None , ' ask ' : None , ' subscription ' : ' none ' , ' groups ' : [ ' Not in roster ' ] , ' resources ' : { } }
if type ( self . _data [ jid . getStripped ( ) ] [ ' resources ' ] ) != type ( dict ( ) ) :
self . _data [ jid . getStripped ( ) ] [ ' resources ' ] = { }
item = self . _data [ jid . getStripped ( ) ]
typ = pres . getType ( )
if not typ :
2008-07-02 23:29:10 +00:00
log . info ( ' Setting roster item %s for resource %s ... ' % ( jid . getStripped ( ) , jid . getResource ( ) ) )
2008-06-09 00:32:02 +00:00
item [ ' resources ' ] [ jid . getResource ( ) ] = res = { ' show ' : None , ' status ' : None , ' priority ' : ' 0 ' , ' timestamp ' : None }
if pres . getTag ( ' show ' ) : res [ ' show ' ] = pres . getShow ( )
if pres . getTag ( ' status ' ) : res [ ' status ' ] = pres . getStatus ( )
if pres . getTag ( ' priority ' ) : res [ ' priority ' ] = pres . getPriority ( )
if not pres . getTimestamp ( ) : pres . setTimestamp ( )
res [ ' timestamp ' ] = pres . getTimestamp ( )
elif typ == ' unavailable ' and item [ ' resources ' ] . has_key ( jid . getResource ( ) ) : del item [ ' resources ' ] [ jid . getResource ( ) ]
# Need to handle type='error' also
def _getItemData ( self , jid , dataname ) :
2008-07-18 00:34:49 +00:00
''' Return specific jid ' s representation in internal format. Used internally. '''
2008-06-09 00:32:02 +00:00
jid = jid [ : ( jid + ' / ' ) . find ( ' / ' ) ]
return self . _data [ jid ] [ dataname ]
def _getResourceData ( self , jid , dataname ) :
2008-07-18 00:34:49 +00:00
''' Return specific jid ' s resource representation in internal format. Used internally. '''
2008-06-09 00:32:02 +00:00
if jid . find ( ' / ' ) + 1 :
jid , resource = jid . split ( ' / ' , 1 )
if self . _data [ jid ] [ ' resources ' ] . has_key ( resource ) : return self . _data [ jid ] [ ' resources ' ] [ resource ] [ dataname ]
elif self . _data [ jid ] [ ' resources ' ] . keys ( ) :
lastpri = - 129
for r in self . _data [ jid ] [ ' resources ' ] . keys ( ) :
if int ( self . _data [ jid ] [ ' resources ' ] [ r ] [ ' priority ' ] ) > lastpri : resource , lastpri = r , int ( self . _data [ jid ] [ ' resources ' ] [ r ] [ ' priority ' ] )
return self . _data [ jid ] [ ' resources ' ] [ resource ] [ dataname ]
def delItem ( self , jid ) :
2008-07-18 00:34:49 +00:00
''' Delete contact ' jid ' from roster. '''
2008-06-09 00:32:02 +00:00
self . _owner . send ( Iq ( ' set ' , NS_ROSTER , payload = [ Node ( ' item ' , { ' jid ' : jid , ' subscription ' : ' remove ' } ) ] ) )
def getAsk ( self , jid ) :
2008-07-18 00:34:49 +00:00
''' Returns ' ask ' value of contact ' jid ' . '''
2008-06-09 00:32:02 +00:00
return self . _getItemData ( jid , ' ask ' )
def getGroups ( self , jid ) :
2008-07-18 00:34:49 +00:00
''' Returns groups list that contact ' jid ' belongs to. '''
2008-06-09 00:32:02 +00:00
return self . _getItemData ( jid , ' groups ' )
def getName ( self , jid ) :
2008-07-18 00:34:49 +00:00
''' Returns name of contact ' jid ' . '''
2008-06-09 00:32:02 +00:00
return self . _getItemData ( jid , ' name ' )
def getPriority ( self , jid ) :
2008-07-18 00:34:49 +00:00
''' Returns priority of contact ' jid ' . ' jid ' should be a full (not bare) JID. '''
2008-06-09 00:32:02 +00:00
return self . _getResourceData ( jid , ' priority ' )
def getRawRoster ( self ) :
2008-07-18 00:34:49 +00:00
''' Returns roster representation in internal format. '''
2008-06-09 00:32:02 +00:00
return self . _data
def getRawItem ( self , jid ) :
2008-07-18 00:34:49 +00:00
''' Returns roster item ' jid ' representation in internal format. '''
2008-06-09 00:32:02 +00:00
return self . _data [ jid [ : ( jid + ' / ' ) . find ( ' / ' ) ] ]
def getShow ( self , jid ) :
2008-07-18 00:34:49 +00:00
''' Returns ' show ' value of contact ' jid ' . ' jid ' should be a full (not bare) JID. '''
2008-06-09 00:32:02 +00:00
return self . _getResourceData ( jid , ' show ' )
def getStatus ( self , jid ) :
2008-07-18 00:34:49 +00:00
''' Returns ' status ' value of contact ' jid ' . ' jid ' should be a full (not bare) JID. '''
2008-06-09 00:32:02 +00:00
return self . _getResourceData ( jid , ' status ' )
def getSubscription ( self , jid ) :
2008-07-18 00:34:49 +00:00
''' Returns ' subscription ' value of contact ' jid ' . '''
2008-06-09 00:32:02 +00:00
return self . _getItemData ( jid , ' subscription ' )
def getResources ( self , jid ) :
2008-07-18 00:34:49 +00:00
''' Returns list of connected resources of contact ' jid ' . '''
2008-06-09 00:32:02 +00:00
return self . _data [ jid [ : ( jid + ' / ' ) . find ( ' / ' ) ] ] [ ' resources ' ] . keys ( )
def setItem ( self , jid , name = None , groups = [ ] ) :
2008-07-18 00:34:49 +00:00
''' Renames contact ' jid ' and sets the groups list that it now belongs to. '''
2008-06-09 00:32:02 +00:00
iq = Iq ( ' set ' , NS_ROSTER )
query = iq . getTag ( ' query ' )
attrs = { ' jid ' : jid }
if name : attrs [ ' name ' ] = name
item = query . setTag ( ' item ' , attrs )
for group in groups : item . addChild ( node = Node ( ' group ' , payload = [ group ] ) )
self . _owner . send ( iq )
def getItems ( self ) :
2008-07-18 00:34:49 +00:00
''' Return list of all [bare] JIDs that the roster is currently tracks. '''
2008-06-09 00:32:02 +00:00
return self . _data . keys ( )
def keys ( self ) :
2008-07-18 00:34:49 +00:00
''' Same as getItems. Provided for the sake of dictionary interface. '''
2008-06-09 00:32:02 +00:00
return self . _data . keys ( )
def __getitem__ ( self , item ) :
2008-07-18 00:34:49 +00:00
''' Get the contact in the internal format. Raises KeyError if JID ' item ' is not in roster. '''
2008-06-09 00:32:02 +00:00
return self . _data [ item ]
def getItem ( self , item ) :
2008-07-18 00:34:49 +00:00
''' Get the contact in the internal format (or None if JID ' item ' is not in roster). '''
2008-06-09 00:32:02 +00:00
if self . _data . has_key ( item ) : return self . _data [ item ]
def Subscribe ( self , jid ) :
2008-07-18 00:34:49 +00:00
''' Send subscription request to JID ' jid ' . '''
2008-06-09 00:32:02 +00:00
self . _owner . send ( Presence ( jid , ' subscribe ' ) )
def Unsubscribe ( self , jid ) :
2008-07-18 00:34:49 +00:00
''' Ask for removing our subscription for JID ' jid ' . '''
2008-06-09 00:32:02 +00:00
self . _owner . send ( Presence ( jid , ' unsubscribe ' ) )
def Authorize ( self , jid ) :
2008-07-18 00:34:49 +00:00
''' Authorise JID ' jid ' . Works only if these JID requested auth previously. '''
2008-06-09 00:32:02 +00:00
self . _owner . send ( Presence ( jid , ' subscribed ' ) )
def Unauthorize ( self , jid ) :
2008-07-18 00:34:49 +00:00
''' Unauthorise JID ' jid ' . Use for declining authorisation request
or for removing existing authorization . '''
2008-06-09 00:32:02 +00:00
self . _owner . send ( Presence ( jid , ' unsubscribed ' ) )
def getRaw ( self ) :
2008-07-18 00:34:49 +00:00
''' Returns the internal data representation of the roster. '''
2008-06-09 00:32:02 +00:00
return self . _data
2009-06-27 17:56:04 +02:00
def setRaw ( self , data ) :
''' Returns the internal data representation of the roster. '''
self . _data = data
self . _data [ self . _owner . User + ' @ ' + self . _owner . Server ] = { ' resources ' : { } , ' name ' : None , ' ask ' : None , ' subscription ' : None , ' groups ' : None , }
self . set = 1
2008-06-09 00:32:02 +00:00
# copypasted methods for roster.py from constructor to here
2006-02-03 12:17:34 +00:00
def plugin ( self , owner , request = 1 ) :
''' Register presence and subscription trackers in the owner ' s dispatcher.
Also request roster from server if the ' request ' argument is set .
Used internally . '''
self . _owner . RegisterHandler ( ' iq ' , self . RosterIqHandler , ' result ' , NS_ROSTER , makefirst = 1 )
self . _owner . RegisterHandler ( ' iq ' , self . RosterIqHandler , ' set ' , NS_ROSTER )
self . _owner . RegisterHandler ( ' presence ' , self . PresenceHandler )
2008-12-03 21:56:12 +00:00
if request :
2009-06-27 17:56:04 +02:00
return self . Request ( )
2008-12-03 21:56:12 +00:00
2006-02-03 12:17:34 +00:00
def _on_roster_set ( self , data ) :
if data :
self . _owner . Dispatcher . ProcessNonBlocking ( data )
2008-12-03 21:56:12 +00:00
if not self . set :
return
2006-02-03 12:17:34 +00:00
self . _owner . onreceive ( None )
if self . on_ready :
self . on_ready ( self )
self . on_ready = None
return True
2008-12-03 21:56:12 +00:00
2009-06-27 17:56:04 +02:00
def getRoster ( self , on_ready = None , force = False ) :
2006-02-03 12:17:34 +00:00
''' Requests roster from server if neccessary and returns self. '''
2009-06-27 17:56:04 +02:00
return_self = True
2008-12-03 21:56:12 +00:00
if not self . set :
2006-02-03 12:17:34 +00:00
self . on_ready = on_ready
self . _owner . onreceive ( self . _on_roster_set )
2009-06-27 17:56:04 +02:00
return_self = False
elif on_ready :
2006-02-03 12:17:34 +00:00
on_ready ( self )
2009-06-27 17:56:04 +02:00
return_self = False
if return_self or force :
2006-02-03 12:17:34 +00:00
return self
2009-06-27 17:56:04 +02:00
return None
2008-07-29 19:49:31 +00:00
2008-12-15 21:06:08 +00:00
# vim: se ts=3: