# HG changeset patch # User Goffi # Date 1503247281 -7200 # Node ID ff8aff4c9b7905c86ca2fac59588a736682da8fb # Parent 28c9579901d3d7c2527dfdb8b158298949ffaf17 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. diff -r 28c9579901d3 -r ff8aff4c9b79 sat_pubsub/backend.py --- 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 diff -r 28c9579901d3 -r ff8aff4c9b79 sat_pubsub/privilege.py --- 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)