2006-02-03 12:17:34 +00:00
## roster_nb.py
2010-02-08 15:08:40 +01:00
## based on roster.py
2006-02-03 12:17:34 +00:00
##
## Copyright (C) 2003-2005 Alexey "Snake" Nezhdanov
2010-02-08 15:08:40 +01: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 $
2009-11-26 17:46:48 +02:00
"""
2006-02-03 12:17:34 +00:00
Simple roster implementation . Can be used though for different tasks like
mass - renaming of contacts .
2009-11-26 17:46:48 +02:00
"""
2006-02-03 12:17:34 +00:00
2009-07-14 18:48:24 +02:00
from protocol import JID , Iq , Presence , Node , NodeProcessed , NS_MUC_USER , 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 ) :
2010-02-08 15:08:40 +01:00
"""
Defines a plenty of methods that will allow you to manage roster . 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
"""
2010-02-11 18:57:39 +01:00
def __init__ ( self , version = None ) :
2010-02-08 15:08:40 +01:00
"""
Init internal variables
"""
PlugIn . __init__ ( self )
self . version = version
self . _data = { }
self . set = None
self . _exported_methods = [ self . getRoster ]
self . received_from_server = False
def Request ( self , force = 0 ) :
"""
Request roster from server if it were not yet requested ( or if the
' force ' argument is set )
"""
if self . set is None :
self . set = 0
elif not force :
return
2010-02-10 19:24:11 +01:00
iq = Iq ( ' get ' , NS_ROSTER )
2010-02-11 18:57:39 +01:00
if self . version is not None :
iq . setTagAttr ( ' query ' , ' ver ' , self . version )
2010-02-08 15:08:40 +01:00
id_ = self . _owner . getAnID ( )
iq . setID ( id_ )
self . _owner . send ( iq )
log . info ( ' Roster requested from server ' )
return id_
def RosterIqHandler ( self , dis , stanza ) :
"""
Subscription tracker . Used internally for setting items state in internal
roster representation
"""
sender = stanza . getAttr ( ' from ' )
if not sender is None and not sender . bareMatch (
self . _owner . User + ' @ ' + self . _owner . Server ) :
return
query = stanza . getTag ( ' query ' )
if query :
self . received_from_server = True
self . version = stanza . getTagAttr ( ' query ' , ' ver ' )
if self . version is None :
self . version = ' '
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 ]
# Looks like we have a workaround
# raise NodeProcessed # a MUST
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 ' ) :
if group . getData ( ) not in self . _data [ jid ] [ ' groups ' ] :
self . _data [ jid ] [ ' groups ' ] . append ( group . getData ( ) )
2010-02-10 19:24:11 +01:00
self . _data [ self . _owner . User + ' @ ' + self . _owner . Server ] = { ' resources ' : { } , ' name ' : None , ' ask ' : None , ' subscription ' : None , ' groups ' : None , }
2010-02-08 15:08:40 +01:00
self . set = 1
# Looks like we have a workaround
# raise NodeProcessed # a MUST. Otherwise you'll get back an <iq type='error'/>
def PresenceHandler ( self , dis , pres ) :
"""
Presence tracker . Used internally for setting items ' resources state in
internal roster representation
"""
if pres . getTag ( ' x ' , namespace = NS_MUC_USER ) :
return
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 :
2010-02-10 19:24:11 +01:00
log . info ( ' Setting roster item %s for resource %s ... ' % ( jid . getStripped ( ) , jid . getResource ( ) ) )
2010-02-08 15:08:40 +01: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 ) :
"""
Return specific jid ' s representation in internal format. Used internally
"""
jid = jid [ : ( jid + ' / ' ) . find ( ' / ' ) ]
return self . _data [ jid ] [ dataname ]
def _getResourceData ( self , jid , dataname ) :
"""
Return specific jid ' s resource representation in internal format. Used
internally
"""
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 :
2010-02-10 19:24:11 +01:00
resource , lastpri = r , int ( self . _data [ jid ] [ ' resources ' ] [ r ] [ ' priority ' ] )
2010-02-08 15:08:40 +01:00
return self . _data [ jid ] [ ' resources ' ] [ resource ] [ dataname ]
def delItem ( self , jid ) :
"""
Delete contact ' jid ' from roster
"""
self . _owner . send ( Iq ( ' set ' , NS_ROSTER , payload = [ Node ( ' item ' , { ' jid ' : jid , ' subscription ' : ' remove ' } ) ] ) )
def getAsk ( self , jid ) :
"""
Return ' ask ' value of contact ' jid '
"""
return self . _getItemData ( jid , ' ask ' )
def getGroups ( self , jid ) :
"""
Return groups list that contact ' jid ' belongs to
"""
return self . _getItemData ( jid , ' groups ' )
def getName ( self , jid ) :
"""
Return name of contact ' jid '
"""
return self . _getItemData ( jid , ' name ' )
def getPriority ( self , jid ) :
"""
Return priority of contact ' jid ' . ' jid ' should be a full ( not bare ) JID
"""
return self . _getResourceData ( jid , ' priority ' )
def getRawRoster ( self ) :
"""
Return roster representation in internal format
"""
return self . _data
def getRawItem ( self , jid ) :
"""
Return roster item ' jid ' representation in internal format
"""
return self . _data [ jid [ : ( jid + ' / ' ) . find ( ' / ' ) ] ]
def getShow ( self , jid ) :
"""
Return ' show ' value of contact ' jid ' . ' jid ' should be a full ( not bare )
JID
"""
return self . _getResourceData ( jid , ' show ' )
def getStatus ( self , jid ) :
"""
Return ' status ' value of contact ' jid ' . ' jid ' should be a full ( not bare )
JID
"""
return self . _getResourceData ( jid , ' status ' )
def getSubscription ( self , jid ) :
"""
Return ' subscription ' value of contact ' jid '
"""
return self . _getItemData ( jid , ' subscription ' )
def getResources ( self , jid ) :
"""
Return list of connected resources of contact ' jid '
"""
return self . _data [ jid [ : ( jid + ' / ' ) . find ( ' / ' ) ] ] [ ' resources ' ] . keys ( )
def setItem ( self , jid , name = None , groups = [ ] ) :
"""
Rename contact ' jid ' and sets the groups list that it now belongs to
"""
2010-02-10 19:24:11 +01:00
iq = Iq ( ' set ' , NS_ROSTER )
2010-02-08 15:08:40 +01:00
query = iq . getTag ( ' query ' )
attrs = { ' jid ' : jid }
if name :
attrs [ ' name ' ] = name
2010-02-10 19:24:11 +01:00
item = query . setTag ( ' item ' , attrs )
2010-02-08 15:08:40 +01:00
for group in groups :
item . addChild ( node = Node ( ' group ' , payload = [ group ] ) )
self . _owner . send ( iq )
def setItemMulti ( self , items ) :
"""
Rename multiple contacts and sets their group lists
"""
iq = Iq ( ' set ' , NS_ROSTER )
query = iq . getTag ( ' query ' )
for i in items :
attrs = { ' jid ' : i [ ' jid ' ] }
if i [ ' name ' ] :
attrs [ ' name ' ] = i [ ' name ' ]
item = query . setTag ( ' item ' , attrs )
for group in i [ ' groups ' ] :
item . addChild ( node = Node ( ' group ' , payload = [ group ] ) )
self . _owner . send ( iq )
def getItems ( self ) :
"""
Return list of all [ bare ] JIDs that the roster is currently tracks
"""
return self . _data . keys ( )
def keys ( self ) :
"""
Same as getItems . Provided for the sake of dictionary interface
"""
return self . _data . keys ( )
def __getitem__ ( self , item ) :
"""
Get the contact in the internal format . Raises KeyError if JID ' item ' is
not in roster
"""
return self . _data [ item ]
2010-02-10 19:24:11 +01:00
def getItem ( self , item ) :
2010-02-08 15:08:40 +01:00
"""
Get the contact in the internal format ( or None if JID ' item ' is not in
roster )
"""
if self . _data . has_key ( item ) :
return self . _data [ item ]
def Subscribe ( self , jid ) :
"""
Send subscription request to JID ' jid '
"""
self . _owner . send ( Presence ( jid , ' subscribe ' ) )
2010-02-10 19:24:11 +01:00
def Unsubscribe ( self , jid ) :
2010-02-08 15:08:40 +01:00
"""
Ask for removing our subscription for JID ' jid '
"""
self . _owner . send ( Presence ( jid , ' unsubscribe ' ) )
def Authorize ( self , jid ) :
"""
Authorize JID ' jid ' . Works only if these JID requested auth previously
"""
self . _owner . send ( Presence ( jid , ' subscribed ' ) )
def Unauthorize ( self , jid ) :
"""
Unauthorise JID ' jid ' . Use for declining authorisation request or for
removing existing authorization
"""
self . _owner . send ( Presence ( jid , ' unsubscribed ' ) )
def getRaw ( self ) :
"""
Return the internal data representation of the roster
"""
return self . _data
def setRaw ( self , data ) :
"""
Return 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
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 )
if request :
return self . Request ( )
def _on_roster_set ( self , data ) :
if data :
self . _owner . Dispatcher . ProcessNonBlocking ( data )
if not self . set :
return
2010-06-23 14:54:50 +02:00
if not self . _owner :
# Connection has been closed by receiving a <stream:error> for ex,
return
2010-02-08 15:08:40 +01:00
self . _owner . onreceive ( None )
if self . on_ready :
self . on_ready ( self )
self . on_ready = None
return True
def getRoster ( self , on_ready = None , force = False ) :
"""
Request roster from server if neccessary and returns self
"""
return_self = True
if not self . set :
self . on_ready = on_ready
self . _owner . onreceive ( self . _on_roster_set )
return_self = False
elif on_ready :
on_ready ( self )
return_self = False
if return_self or force :
return self
return None