changeset 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 8cf1be9572f8
files sat_pubsub/backend.py sat_pubsub/privilege.py
diffstat 2 files changed, 43 insertions(+), 4 deletions(-) [+]
line wrap: on
line diff
--- a/sat_pubsub/backend.py	Sun Aug 20 12:35:04 2017 +0200
+++ b/sat_pubsub/backend.py	Sun Aug 20 18:41:21 2017 +0200
@@ -1175,6 +1175,14 @@
         else:
             notifications = [(subscription.subscriber, [subscription], items_data)]
 
+        if pep and node.getConfiguration()[const.OPT_ACCESS_MODEL] in ('open', 'presence'):
+            # for PEP we need to manage automatic subscriptions (cf. XEP-0163 §4)
+            explicit_subscribers = {subscriber for subscriber, _, _ in notifications}
+            auto_subscribers = yield self.backend.privilege.getAutoSubscribers(recipient, node.nodeIdentifier, explicit_subscribers)
+            for sub_jid in auto_subscribers:
+                 sub = pubsub.Subscription(node.nodeIdentifier, sub_jid, 'subscribed')
+                 notifications.append((sub_jid, [sub], items_data))
+
         owners = yield node.getOwners()
         owner_roster = None
 
--- 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)