view src/plugins/plugin_xep_0059.py @ 1235:80d2ed788b40

core (config): added the Exception default value which raise an exception instead of returning the default in getConfig
author Goffi <goffi@goffi.org>
date Sat, 11 Oct 2014 16:26:43 +0200
parents 16484ebb695b
children ea692d51a0ee
line wrap: on
line source

#!/usr/bin/python
# -*- coding: utf-8 -*-

# SAT plugin for Result Set Management (XEP-0059)
# Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014 Jérôme Poisson (goffi@goffi.org)
# Copyright (C) 2013, 2014 Adrien Cossa (souliane@mailoo.org)

# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, 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 Affero General Public License for more details.

# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

from sat.core.i18n import _
from sat.core.log import getLogger
log = getLogger(__name__)

from twisted.words.xish import domish
from wokkel import disco, iwokkel
try:
    from twisted.words.protocols.xmlstream import XMPPHandler
except ImportError:
    from wokkel.subprotocols import XMPPHandler
from zope.interface import implements


NS_RSM = 'http://jabber.org/protocol/rsm'

PLUGIN_INFO = {
    "name": "Result Set Management",
    "import_name": "XEP-0059",
    "type": "XEP",
    "protocols": ["XEP-0059"],
    "main": "XEP_0059",
    "handler": "no",
    "description": _("""Implementation of Result Set Management""")
}


class XEP_0059(object):

    def __init__(self, host):
        log.info(_("Result Set Management plugin initialization"))
        self.host = host

    def requestPage(self, stanza, limit=10, index=None, after=None, before=None):
        """Embed a RSM page request in the given stanza.

        @param stanza (domish.Element): any stanza to which RSM applies
        @param limit (int): the maximum number of items in the page
        @param index (int): the starting index of the requested page
        @param after (str, int): the element immediately preceding the page
        @param before (str, int): the element immediately following the page
        """
        main_elt = None
        try:
            main_elt = domish.generateElementsNamed(stanza.elements(), name="query").next()
        except StopIteration:
            try:
                main_elt = domish.generateElementsNamed(stanza.elements(), name="pubsub").next()
            except StopIteration:
                log.warning("Injection of a RSM element only applies to query or pubsub stanzas")
                return
        limit = str(int(limit))

        # in case the service doesn't support RSM, do this at least
        main_elt.items.attributes['max_items'] = limit

        set_elt = main_elt.addElement('set', NS_RSM)
        set_elt.addElement('max').addContent(limit)
        if index:
            assert(after is None and before is None)
            set_elt.addElement('index').addContent(str(int(index)))
        if after:
            assert(before is None)  # could not specify both at the same time
            set_elt.addElement('after').addContent(str(after))
        if before is not None:
            if before == '':  # request the last page, according to http://xmpp.org/extensions/xep-0059.html#last
                set_elt.addElement('before')
            else:
                set_elt.addElement('before').addContent(str(before))

    def countItems(self, stanza):
        """Count the items without retrieving any of them.

        @param stanza (domish.Element): any stanza to which RSM applies
        """
        self.requestPage(stanza, limit=0)

    def extractMetadata(self, stanza):
        """Extract the RSM metadata from the given stanza.

        @param stanza (domish.Element, wokkel.pubsub.PubSubRequest):
           any stanza to which RSM applies. When used by XEP-0060,
           wokkel's PubSubRequest instance is also accepted.
        @return: dict containing the page metadata
        """
        try:
            main_elt = domish.generateElementsNamed(stanza.elements(), name="query").next()
        except StopIteration:
            try:
                main_elt = domish.generateElementsNamed(stanza.elements(), name="pubsub").next()
            except StopIteration:
                log.warning("Extracting data from a RSM element only applies to query or pubsub stanzas")
                return {}
        try:
            set_elt = domish.generateElementsQNamed(main_elt.elements(), name="set", uri=NS_RSM).next()
        except StopIteration:
            log.debug("There's no RSM element in the stanza")
            return {}

        data = {}
        elts = set_elt.elements()
        try:
            elt = elts.next()
            if elt.name == "first":
                data["first"] = "".join(elt.children)
                data["first_index"] = int(elt.getAttribute("index"))
            elif elt.name == "last":
                data["last"] = "".join(elt.children)
            elif elt.name == "count":
                data["count"] = int("".join(elt.children))
        except StopIteration:
            pass
        if "count" not in data:
            log.warning("There's no 'count' element in the RSM element!")
        return data


class XEP_0059_handler(XMPPHandler):
    implements(iwokkel.IDisco)

    def __init__(self, plugin_parent, profile):
        self.plugin_parent = plugin_parent
        self.host = plugin_parent.host
        self.profile = profile

    def getDiscoInfo(self, requestor, target, nodeIdentifier=''):
        return [disco.DiscoFeature(NS_RSM)]

    def getDiscoItems(self, requestor, target, nodeIdentifier=''):
        return []