diff sat_pubsub/privilege.py @ 343:ff8aff4c9b79

backend, psql: implemented notifications for auto subscribers in PEP: people with presence permission from node owner and who request notification (+notify) from node will receive notifications according to XEP-0163 §4. This make SàT Pubsub a nearly fully compliant PEP service. Following features are still missing: - presence access model, should be implemented very soon as everything is already there - deleting resource on unavailable presence, will be implemented really soon - XEP-0115 hash check - and most importantly, rosters updates. XEP-0356 needs to be updated for this.
author Goffi <goffi@goffi.org>
date Sun, 20 Aug 2017 18:41:21 +0200
parents 28c9579901d3
children d1f63ae1eaf4
line wrap: on
line diff
--- a/sat_pubsub/privilege.py	Sun Aug 20 12:35:04 2017 +0200
+++ b/sat_pubsub/privilege.py	Sun Aug 20 18:41:21 2017 +0200
@@ -68,8 +68,8 @@
         #        but we need to manage properly server
         # TODO: do proper server handling
         self.server_jid = jid.JID(service_jid.host.split('.', 1)[1])
-        self.caps_map = {}  # key: full jid, value: caps_hash
-        self.hash_map = {}  # key: (hash,version), value: DiscoInfo instance
+        self.caps_map = {}  # key: bare jid, value: dict of resources with caps hash
+        self.hash_map = {}  # key: (hash,version), value: dict with DiscoInfo instance (infos) and nodes to notify (notify)
         self.roster_cache = {}  # key: jid, value: dict with "timestamp" and "roster"
         self.presence_map = {}  # inverted roster: key: jid, value: set of entities who has this jid in roster (with presence of "from" or "both")
         self.server = None
@@ -123,6 +123,7 @@
         @return: Roster as a mapping from L{JID} to L{RosterItem}.
         @rtype: L{twisted.internet.defer.Deferred}
         """
+        # TODO: cache results
         if self._permissions[PERM_ROSTER] not in ('get', 'both'):
             log.msg("WARNING: permission not allowed to get roster")
             raise failure.Failure(NotAllowedError('roster get is not allowed'))
@@ -237,8 +238,9 @@
 
             # FIXME: hash is not checked (cf. XEP-0115)
             disco_tuple = (hash_, ver)
-            if from_jid not in self.caps_map:
-                self.caps_map[from_jid] = disco_tuple
+            jid_caps = self.caps_map.setdefault(from_jid_bare, {})
+            if from_jid.resource not in jid_caps:
+                jid_caps[from_jid.resource] = disco_tuple
 
             if disco_tuple not in self.hash_map:
                 # first time we se this hash, what is behind it?
@@ -255,7 +257,36 @@
             # publishers are entities which have granted presence access to our user + user itself
             publishers = tuple(self.presence_map.get(from_jid_bare, ())) + (from_jid_bare,)
 
+            # FIXME: add "presence" access_model (for node) for getLastItems
             last_items = yield self._backend.storage.getLastItems(publishers, nodes, ('open',), ('open',), True)
             # we send message with last item, as required by https://xmpp.org/extensions/xep-0163.html#notify-last
             for pep_jid, node, item, item_access_model in last_items:
                 self.notifyPublish(pep_jid, node, [(from_jid, None, [item])])
+
+    ## misc ##
+
+    @defer.inlineCallbacks
+    def getAutoSubscribers(self, recipient, nodeIdentifier, explicit_subscribers):
+        """get automatic subscribers, i.e. subscribers with presence subscription and +notify for this node
+
+        @param recipient(jid.JID): jid of the PEP owner of this node
+        @param nodeIdentifier(unicode): node
+        @param explicit_subscribers(set(jid.JID}: jids of people which have an explicit subscription
+        @return (list[jid.JID]): full jid of automatically subscribed entities
+        """
+        auto_subscribers = []
+        roster = yield self.getRoster(recipient)
+        for roster_jid, roster_item in roster.iteritems():
+            if roster_jid in explicit_subscribers:
+                continue
+            if roster_item.subscriptionFrom:
+                try:
+                    online_resources = self.caps_map[roster_jid]
+                except KeyError:
+                    continue
+                for res, hash_ in online_resources.iteritems():
+                     notify = self.hash_map[hash_]['notify']
+                     if nodeIdentifier in notify:
+                         full_jid = jid.JID(tuple=(roster_jid.user, roster_jid.host, res))
+                         auto_subscribers.append(full_jid)
+        defer.returnValue(auto_subscribers)