1   
  2   
  3   
  4   
  5   
  6   
  7   
  8   
  9   
 10   
 11   
 12   
 13   
 14   
 15   
 16   
 17   
 18   
 19   
 20  """ 
 21  Simple roster implementation. Can be used though for different tasks like 
 22  mass-renaming of contacts. 
 23  """ 
 24   
 25  from protocol import JID, Iq, Presence, Node, NodeProcessed, NS_MUC_USER, NS_ROSTER 
 26  from plugin import PlugIn 
 27   
 28  import logging 
 29  log = logging.getLogger('nbxmpp.roster_nb') 
 30   
 31   
 33      """ 
 34      Defines a plenty of methods that will allow you to manage roster. Also 
 35      automatically track presences from remote JIDs taking into account that 
 36      every JID can have multiple resources connected. Does not currently support 
 37      'error' presences. You can also use mapping interface for access to the 
 38      internal representation of contacts in roster 
 39      """ 
 40   
 42          """ 
 43          Init internal variables 
 44          """ 
 45          PlugIn.__init__(self) 
 46          self.version = version 
 47          self._data = {} 
 48          self._set=None 
 49          self._exported_methods=[self.getRoster] 
 50          self.received_from_server = False 
  51   
 53          """ 
 54          Request roster from server if it were not yet requested (or if the 
 55          'force' argument is set) 
 56          """ 
 57          if self._set is None: 
 58              self._set = 0 
 59          elif not force: 
 60              return 
 61   
 62          iq = Iq('get', NS_ROSTER) 
 63          if self.version is not None: 
 64              iq.setTagAttr('query', 'ver', self.version) 
 65          id_ = self._owner.getAnID() 
 66          iq.setID(id_) 
 67          self._owner.send(iq) 
 68          log.info('Roster requested from server') 
 69          return id_ 
  70   
 72          """ 
 73          Subscription tracker. Used internally for setting items state in internal 
 74          roster representation 
 75          """ 
 76          sender = stanza.getAttr('from') 
 77          if not sender is None and not sender.bareMatch( 
 78          self._owner.User + '@' + self._owner.Server): 
 79              return 
 80          query = stanza.getTag('query') 
 81          if query: 
 82              self.received_from_server = True 
 83              self.version = stanza.getTagAttr('query', 'ver') 
 84              if self.version is None: 
 85                  self.version = '' 
 86              for item in query.getTags('item'): 
 87                  jid=item.getAttr('jid') 
 88                  if item.getAttr('subscription')=='remove': 
 89                      if self._data.has_key(jid): del self._data[jid] 
 90                       
 91                       
 92                  log.info('Setting roster item %s...' % jid) 
 93                  if not self._data.has_key(jid): self._data[jid]={} 
 94                  self._data[jid]['name']=item.getAttr('name') 
 95                  self._data[jid]['ask']=item.getAttr('ask') 
 96                  self._data[jid]['subscription']=item.getAttr('subscription') 
 97                  self._data[jid]['groups']=[] 
 98                  if not self._data[jid].has_key('resources'): self._data[jid]['resources']={} 
 99                  for group in item.getTags('group'): 
100                      if group.getData() not in self._data[jid]['groups']: 
101                          self._data[jid]['groups'].append(group.getData()) 
102          self._data[self._owner.User+'@'+self._owner.Server]={'resources': {}, 'name': None, 'ask': None, 'subscription': None, 'groups': None,} 
103          self._set=1 
 104           
105           
106   
108          """ 
109          Presence tracker. Used internally for setting items' resources state in 
110          internal roster representation 
111          """ 
112          if pres.getTag('x', namespace=NS_MUC_USER): 
113              return 
114          jid=pres.getFrom() 
115          if not jid: 
116               
117              jid=self._owner.Server 
118          jid=JID(jid) 
119          if not self._data.has_key(jid.getStripped()): self._data[jid.getStripped()]={'name':None,'ask':None,'subscription':'none','groups':['Not in roster'],'resources':{}} 
120          if type(self._data[jid.getStripped()]['resources'])!=type(dict()): 
121              self._data[jid.getStripped()]['resources']={} 
122          item=self._data[jid.getStripped()] 
123          typ=pres.getType() 
124   
125          if not typ: 
126              log.info('Setting roster item %s for resource %s...'%(jid.getStripped(), jid.getResource())) 
127              item['resources'][jid.getResource()]=res={'show':None,'status':None,'priority':'0','timestamp':None} 
128              if pres.getTag('show'): res['show']=pres.getShow() 
129              if pres.getTag('status'): res['status']=pres.getStatus() 
130              if pres.getTag('priority'): res['priority']=pres.getPriority() 
131              if not pres.getTimestamp(): pres.setTimestamp() 
132              res['timestamp']=pres.getTimestamp() 
133          elif typ=='unavailable' and item['resources'].has_key(jid.getResource()): del item['resources'][jid.getResource()] 
 134           
135   
137          """ 
138          Return specific jid's representation in internal format. Used internally 
139          """ 
140          jid = jid[:(jid+'/').find('/')] 
141          return self._data[jid][dataname] 
 142   
144          """ 
145          Return specific jid's resource representation in internal format. Used 
146          internally 
147          """ 
148          if jid.find('/') + 1: 
149              jid, resource = jid.split('/', 1) 
150              if self._data[jid]['resources'].has_key(resource): 
151                  return self._data[jid]['resources'][resource][dataname] 
152          elif self._data[jid]['resources'].keys(): 
153              lastpri = -129 
154              for r in self._data[jid]['resources'].keys(): 
155                  if int(self._data[jid]['resources'][r]['priority']) > lastpri: 
156                      resource, lastpri=r, int(self._data[jid]['resources'][r]['priority']) 
157              return self._data[jid]['resources'][resource][dataname] 
 158   
160          """ 
161          Delete contact 'jid' from roster 
162          """ 
163          self._owner.send(Iq('set', NS_ROSTER, payload=[Node('item', {'jid': jid, 'subscription': 'remove'})])) 
 164   
166          """ 
167          Return 'ask' value of contact 'jid' 
168          """ 
169          return self._getItemData(jid, 'ask') 
 170   
172          """ 
173          Return groups list that contact 'jid' belongs to 
174          """ 
175          return self._getItemData(jid, 'groups') 
 176   
178          """ 
179          Return name of contact 'jid' 
180          """ 
181          return self._getItemData(jid, 'name') 
 182   
184          """ 
185          Return priority of contact 'jid'. 'jid' should be a full (not bare) JID 
186          """ 
187          return self._getResourceData(jid, 'priority') 
 188   
190          """ 
191          Return roster representation in internal format 
192          """ 
193          return self._data 
 194   
196          """ 
197          Return roster item 'jid' representation in internal format 
198          """ 
199          return self._data[jid[:(jid+'/').find('/')]] 
 200   
202          """ 
203          Return 'show' value of contact 'jid'. 'jid' should be a full (not bare) 
204          JID 
205          """ 
206          return self._getResourceData(jid, 'show') 
 207   
209          """ 
210          Return 'status' value of contact 'jid'. 'jid' should be a full (not bare) 
211          JID 
212          """ 
213          return self._getResourceData(jid, 'status') 
 214   
216          """ 
217          Return 'subscription' value of contact 'jid' 
218          """ 
219          return self._getItemData(jid, 'subscription') 
 220   
222          """ 
223          Return list of connected resources of contact 'jid' 
224          """ 
225          return self._data[jid[:(jid+'/').find('/')]]['resources'].keys() 
 226   
227 -    def setItem(self, jid, name=None, groups=[]): 
 228          """ 
229          Rename contact 'jid' and sets the groups list that it now belongs to 
230          """ 
231          iq = Iq('set', NS_ROSTER) 
232          query = iq.getTag('query') 
233          attrs = {'jid': jid} 
234          if name: 
235              attrs['name'] = name 
236          item = query.setTag('item', attrs) 
237          for group in groups: 
238              item.addChild(node=Node('group', payload=[group])) 
239          self._owner.send(iq) 
 240   
242          """ 
243          Rename multiple contacts and sets their group lists 
244          """ 
245          iq = Iq('set', NS_ROSTER) 
246          query = iq.getTag('query') 
247          for i in items: 
248              attrs = {'jid': i['jid']} 
249              if i['name']: 
250                  attrs['name'] = i['name'] 
251              item = query.setTag('item', attrs) 
252              for group in i['groups']: 
253                  item.addChild(node=Node('group', payload=[group])) 
254          self._owner.send(iq) 
 255   
257          """ 
258          Return list of all [bare] JIDs that the roster is currently tracks 
259          """ 
260          return self._data.keys() 
 261   
263          """ 
264          Same as getItems. Provided for the sake of dictionary interface 
265          """ 
266          return self._data.keys() 
 267   
269          """ 
270          Get the contact in the internal format. Raises KeyError if JID 'item' is 
271          not in roster 
272          """ 
273          return self._data[item] 
 274   
276          """ 
277          Get the contact in the internal format (or None if JID 'item' is not in 
278          roster) 
279          """ 
280          if self._data.has_key(item): 
281              return self._data[item] 
 282   
284          """ 
285          Send subscription request to JID 'jid' 
286          """ 
287          self._owner.send(Presence(jid, 'subscribe')) 
 288   
290          """ 
291          Ask for removing our subscription for JID 'jid' 
292          """ 
293          self._owner.send(Presence(jid, 'unsubscribe')) 
 294   
296          """ 
297          Authorize JID 'jid'. Works only if these JID requested auth previously 
298          """ 
299          self._owner.send(Presence(jid, 'subscribed')) 
 300   
302          """ 
303          Unauthorise JID 'jid'. Use for declining authorisation request or for 
304          removing existing authorization 
305          """ 
306          self._owner.send(Presence(jid, 'unsubscribed')) 
 307   
309          """ 
310          Return the internal data representation of the roster 
311          """ 
312          return self._data 
 313   
315          """ 
316          Return the internal data representation of the roster 
317          """ 
318          self._data = data 
319          self._data[self._owner.User + '@' + self._owner.Server] = { 
320                          'resources': {}, 
321                          'name': None, 
322                          'ask': None, 
323                          'subscription': None, 
324                          'groups': None 
325          } 
326          self._set = 1 
 327   
328 -    def plugin(self, owner, request=1): 
 339   
341          if data: 
342              self._owner.Dispatcher.ProcessNonBlocking(data) 
343          if not self._set: 
344              return 
345          if not hasattr(self, '_owner') or not self._owner: 
346               
347              return 
348          self._owner.onreceive(None) 
349          if self.on_ready: 
350              self.on_ready(self) 
351              self.on_ready = None 
352          return True 
 353   
354 -    def getRoster(self, on_ready=None, force=False): 
 355          """ 
356          Request roster from server if neccessary and returns self 
357          """ 
358          return_self = True 
359          if not self._set: 
360              self.on_ready = on_ready 
361              self._owner.onreceive(self._on_roster_set) 
362              return_self = False 
363          elif on_ready: 
364              on_ready(self) 
365              return_self = False 
366          if return_self or force: 
367              return self 
368          return None 
  369