Welcome to mirror list, hosted at ThFree Co, Russian Federation.

dev.gajim.org/gajim/python-nbxmpp.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'nbxmpp/roster_nb.py')
-rw-r--r--nbxmpp/roster_nb.py368
1 files changed, 368 insertions, 0 deletions
diff --git a/nbxmpp/roster_nb.py b/nbxmpp/roster_nb.py
new file mode 100644
index 0000000..c07fa25
--- /dev/null
+++ b/nbxmpp/roster_nb.py
@@ -0,0 +1,368 @@
+## roster_nb.py
+## based on roster.py
+##
+## Copyright (C) 2003-2005 Alexey "Snake" Nezhdanov
+## modified by Dimitur Kirov <dkirov@gmail.com>
+##
+## 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.
+"""
+
+from protocol import JID, Iq, Presence, Node, NodeProcessed, NS_MUC_USER, NS_ROSTER
+from plugin import PlugIn
+
+import logging
+log = logging.getLogger('nbxmpp.roster_nb')
+
+
+class NonBlockingRoster(PlugIn):
+ """
+ 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
+ """
+
+ def __init__(self, version=None):
+ """
+ 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
+
+ iq = Iq('get', NS_ROSTER)
+ if self.version is not None:
+ iq.setTagAttr('query', 'ver', self.version)
+ 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())
+ self._data[self._owner.User+'@'+self._owner.Server]={'resources': {}, 'name': None, 'ask': None, 'subscription': None, 'groups': None,}
+ 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:
+ log.info('Setting roster item %s for resource %s...'%(jid.getStripped(), jid.getResource()))
+ 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:
+ resource, lastpri=r, int(self._data[jid]['resources'][r]['priority'])
+ 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
+ """
+ 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 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]
+
+ def getItem(self, item):
+ """
+ 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'))
+
+ def Unsubscribe(self, jid):
+ """
+ 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
+ if not hasattr(self, '_owner') or not self._owner:
+ # Connection has been closed by receiving a <stream:error> for ex,
+ return
+ 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