Ignore:
Timestamp:
Aug 22, 2007, 3:43:43 PM (14 years ago)
Author:
Ralph Meijer <ralphm@…>
Branch:
default
Convert:
svn:b33ecbfc-034c-dc11-8662-000475d9059e/trunk@3
Message:

Add in pubsub client support, client helpers and generic XMPP subprotocol handlers.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • wokkel/pubsub.py

    r1 r2  
    1414
    1515from twisted.internet import defer
    16 from twisted.words.protocols.jabber import jid, error
     16from twisted.words.protocols.jabber import jid, error, xmlstream
    1717from twisted.words.xish import domish
    1818
    1919from wokkel import disco, data_form
    2020from wokkel.subprotocols import IQHandlerMixin, XMPPHandler
    21 from wokkel.iwokkel import IPubSubService
     21from wokkel.iwokkel import IPubSubClient, IPubSubService
    2222
    2323# Iq get and set XPath queries
     
    7171
    7272class BadRequest(error.StanzaError):
     73    """
     74    Bad request stanza error.
     75    """
    7376    def __init__(self):
    7477        error.StanzaError.__init__(self, 'bad-request')
    7578
     79
     80
     81class SubscriptionPending(Exception):
     82    """
     83    Raised when the requested subscription is pending acceptance.
     84    """
     85
     86
     87class SubscriptionUnconfigured(Exception):
     88    """
     89    Raised when the requested subscription needs to be configured before
     90    becoming active.
     91    """
     92
     93
    7694class PubSubError(error.StanzaError):
     95    """
     96    Exception with publish-subscribe specific condition.
     97    """
    7798    def __init__(self, condition, pubsubCondition, feature=None, text=None):
    7899        appCondition = domish.Element((NS_PUBSUB_ERRORS, pubsubCondition))
     
    83104                                         appCondition=appCondition)
    84105
     106
    85107class Unsupported(PubSubError):
    86108    def __init__(self, feature, text=None):
     
    90112                                   text)
    91113
     114
    92115class OptionsUnavailable(Unsupported):
    93116    def __init__(self):
    94117        Unsupported.__init__(self, 'subscription-options-unavailable')
     118
     119
     120class Item(domish.Element):
     121    """
     122    Publish subscribe item.
     123
     124    This behaves like an object providing L{domish.IElement}.
     125
     126    Item payload can be added using C{addChild} or C{addRawXml}, or using the
     127    C{payload} keyword argument to C{__init__}.
     128    """
     129
     130    def __init__(self, id=None, payload=None):
     131        """
     132        @param id: optional item identifier
     133        @type id: L{unicode}
     134        @param payload: optional item payload. Either as a domish element, or
     135                        as serialized XML.
     136        @type payload: object providing L{domish.IElement} or L{unicode}.
     137        """
     138
     139        domish.Element.__init__(self, (None, 'item'))
     140        if id is not None:
     141            self['id'] = id
     142        if payload is not None:
     143            if isinstance(payload, basestring):
     144                self.addRawXml(payload)
     145            else:
     146                self.addChild(payload)
     147
     148class PubSubRequest(xmlstream.IQ):
     149    """
     150    Base class for publish subscribe user requests.
     151
     152    @cvar namespace: request namespace
     153    @cvar verb: request verb
     154    @cvar method: type attribute of the IQ request. Either C{'set'} or C{'get'}
     155    @ivar command: command element of the request. This is the direct child of
     156                   the C{pubsub} element in the C{namespace} with the name
     157                   C{verb}.
     158    """
     159
     160    namespace = NS_PUBSUB
     161    method = 'set'
     162
     163    def __init__(self, xs):
     164        xmlstream.IQ.__init__(self, xs, self.method)
     165        self.addElement((self.namespace, 'pubsub'))
     166
     167        self.command = self.pubsub.addElement(self.verb)
     168
     169    def send(self, to):
     170        destination = unicode(to)
     171        return xmlstream.IQ.send(self, destination)
     172
     173class CreateNode(PubSubRequest):
     174    verb = 'create'
     175
     176    def __init__(self, xs, node=None):
     177        PubSubRequest.__init__(self, xs)
     178        if node:
     179            self.command["node"] = node
     180
     181class DeleteNode(PubSubRequest):
     182    verb = 'delete'
     183    def __init__(self, xs, node):
     184        PubSubRequest.__init__(self, xs)
     185        self.command["node"] = node
     186
     187class Subscribe(PubSubRequest):
     188    verb = 'subscribe'
     189
     190    def __init__(self, xs, node, subscriber):
     191        PubSubRequest.__init__(self, xs)
     192        self.command["node"] = node
     193        self.command["jid"] = subscriber.full()
     194
     195class Publish(PubSubRequest):
     196    verb = 'publish'
     197
     198    def __init__(self, xs, node):
     199        PubSubRequest.__init__(self, xs)
     200        self.command["node"] = node
     201
     202    def addItem(self, id=None, payload=None):
     203        item = self.command.addElement("item")
     204        item.addChild(payload)
     205
     206        if id is not None:
     207            item["id"] = id
     208
     209        return item
     210
     211class PubSubClient(XMPPHandler):
     212    """
     213    Publish subscribe client protocol.
     214    """
     215
     216    implements(IPubSubClient)
     217
     218    def connectionInitialized(self):
     219        self.xmlstream.addObserver('/message/event[@xmlns="%s"]/items' %
     220                                   NS_PUBSUB_EVENT, self._onItems)
     221
     222    def _onItems(self, message):
     223        try:
     224            notifier = jid.JID(message["from"])
     225            node = message.event.items["node"]
     226        except KeyError:
     227            return
     228
     229        items = [element for element in message.event.items.elements()
     230                         if element.name == 'item']
     231
     232        self.itemsReceived(notifier, node, items)
     233
     234    def itemsReceived(self, notifier, node, items):
     235        pass
     236
     237    def createNode(self, service, node=None):
     238        request = CreateNode(self.xmlstream, node)
     239
     240        def cb(iq):
     241            try:
     242                new_node = iq.pubsub.create["node"]
     243            except AttributeError:
     244                # the suggested node identifier was accepted
     245                new_node = node
     246            return new_node
     247
     248        return request.send(service).addCallback(cb)
     249
     250    def deleteNode(self, service, node):
     251        return DeleteNode(self.xmlstream, node).send(service)
     252
     253    def subscribe(self, service, node, subscriber):
     254        request = Subscribe(self.xmlstream, node, subscriber)
     255
     256        def cb(iq):
     257            subscription = iq.pubsub.subscription["subscription"]
     258
     259            if subscription == 'pending':
     260                raise SubscriptionPending
     261            elif subscription == 'unconfigured':
     262                raise SubscriptionUnconfigured
     263            else:
     264                # we assume subscription == 'subscribed'
     265                # any other value would be invalid, but that should have
     266                # yielded a stanza error.
     267                return None
     268
     269        return request.send(service).addCallback(cb)
     270
     271    def publish(self, service, node, items=[]):
     272        request = Publish(self.xmlstream, node)
     273        for item in items:
     274            request.command.addChild(item)
     275
     276        return request.send(service)
    95277
    96278class PubSubService(XMPPHandler, IQHandlerMixin):
     
    215397
    216398        items = []
    217         for child in iq.pubsub.publish.children:
    218             if child.__class__ == domish.Element and child.name == 'item':
    219                 items.append(child)
     399        for element in iq.pubsub.publish.elements():
     400            if element.uri == NS_PUBSUB and element.name == 'item':
     401                items.append(element)
    220402
    221403        return self.publish(requestor, nodeIdentifier, items)
Note: See TracChangeset for help on using the changeset viewer.