Changeset 57:bcc9bc7a0929


Ignore:
Timestamp:
Apr 22, 2009, 5:02:02 PM (12 years ago)
Author:
Ralph Meijer <ralphm@…>
Branch:
default
Convert:
svn:b33ecbfc-034c-dc11-8662-000475d9059e/trunk@169
Message:

Add a PubSubRequest? class, to parse and render publish-subscribe requests.

Author: ralphm.
Fixes #45.

Location:
wokkel
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • wokkel/generic.py

    r55 r57  
    1111
    1212from twisted.internet import defer, protocol
    13 from twisted.words.protocols.jabber import error, xmlstream
     13from twisted.words.protocols.jabber import error, jid, xmlstream
    1414from twisted.words.protocols.jabber.xmlstream import toResponse
    1515from twisted.words.xish import domish, utility
     
    163163
    164164
     165
     166class Stanza(object):
     167    """
     168    Abstract representation of a stanza.
     169
     170    @ivar sender: The sending entity.
     171    @type sender: L{jid.JID}
     172    @ivar recipient: The receiving entity.
     173    @type recipient: L{jid.JID}
     174    """
     175
     176    sender = None
     177    recipient = None
     178    stanzaType = None
     179
     180    @classmethod
     181    def fromElement(Class, element):
     182        stanza = Class()
     183        stanza.parseElement(element)
     184        return stanza
     185
     186
     187    def parseElement(self, element):
     188        self.sender = jid.internJID(element['from'])
     189        if element.hasAttribute('from'):
     190            self.sender = jid.internJID(element['from'])
     191        if element.hasAttribute('to'):
     192            self.recipient = jid.internJID(element['to'])
     193        self.stanzaType = element.getAttribute('type')
     194
     195
    165196class DeferredXmlStreamFactory(BootstrapMixin, protocol.ClientFactory):
    166197    protocol = xmlstream.XmlStream
  • wokkel/pubsub.py

    r52 r57  
    1717from twisted.words.xish import domish
    1818
    19 from wokkel import disco, data_form, shim
     19from wokkel import disco, data_form, generic, shim
    2020from wokkel.subprotocols import IQHandlerMixin, XMPPHandler
    2121from wokkel.iwokkel import IPubSubClient, IPubSubService
     
    3232NS_PUBSUB_NODE_CONFIG = NS_PUBSUB + "#node_config"
    3333NS_PUBSUB_META_DATA = NS_PUBSUB + "#meta-data"
    34 
    35 # In publish-subscribe namespace XPath query selector.
    36 IN_NS_PUBSUB = '[@xmlns="' + NS_PUBSUB + '"]'
    37 IN_NS_PUBSUB_OWNER = '[@xmlns="' + NS_PUBSUB_OWNER + '"]'
    38 
    39 # Publish-subscribe XPath queries
    40 PUBSUB_ELEMENT = '/pubsub' + IN_NS_PUBSUB
    41 PUBSUB_OWNER_ELEMENT = '/pubsub' + IN_NS_PUBSUB_OWNER
    42 PUBSUB_GET = IQ_GET + PUBSUB_ELEMENT
    43 PUBSUB_SET = IQ_SET + PUBSUB_ELEMENT
    44 PUBSUB_OWNER_GET = IQ_GET + PUBSUB_OWNER_ELEMENT
    45 PUBSUB_OWNER_SET = IQ_SET + PUBSUB_OWNER_ELEMENT
    46 
    47 # Publish-subscribe command XPath queries
    48 PUBSUB_PUBLISH = PUBSUB_SET + '/publish' + IN_NS_PUBSUB
    49 PUBSUB_CREATE = PUBSUB_SET + '/create' + IN_NS_PUBSUB
    50 PUBSUB_SUBSCRIBE = PUBSUB_SET + '/subscribe' + IN_NS_PUBSUB
    51 PUBSUB_UNSUBSCRIBE = PUBSUB_SET + '/unsubscribe' + IN_NS_PUBSUB
    52 PUBSUB_OPTIONS_GET = PUBSUB_GET + '/options' + IN_NS_PUBSUB
    53 PUBSUB_OPTIONS_SET = PUBSUB_SET + '/options' + IN_NS_PUBSUB
    54 PUBSUB_DEFAULT = PUBSUB_OWNER_GET + '/default' + IN_NS_PUBSUB_OWNER
    55 PUBSUB_CONFIGURE_GET = PUBSUB_OWNER_GET + '/configure' + IN_NS_PUBSUB_OWNER
    56 PUBSUB_CONFIGURE_SET = PUBSUB_OWNER_SET + '/configure' + IN_NS_PUBSUB_OWNER
    57 PUBSUB_SUBSCRIPTIONS = PUBSUB_GET + '/subscriptions' + IN_NS_PUBSUB
    58 PUBSUB_AFFILIATIONS = PUBSUB_GET + '/affiliations' + IN_NS_PUBSUB
    59 PUBSUB_AFFILIATIONS_GET = PUBSUB_OWNER_GET + '/affiliations' + \
    60                           IN_NS_PUBSUB_OWNER
    61 PUBSUB_AFFILIATIONS_SET = PUBSUB_OWNER_SET + '/affiliations' + \
    62                           IN_NS_PUBSUB_OWNER
    63 PUBSUB_SUBSCRIPTIONS_GET = PUBSUB_OWNER_GET + '/subscriptions' + \
    64                           IN_NS_PUBSUB_OWNER
    65 PUBSUB_SUBSCRIPTIONS_SET = PUBSUB_OWNER_SET + '/subscriptions' + \
    66                           IN_NS_PUBSUB_OWNER
    67 PUBSUB_ITEMS = PUBSUB_GET + '/items' + IN_NS_PUBSUB
    68 PUBSUB_RETRACT = PUBSUB_SET + '/retract' + IN_NS_PUBSUB
    69 PUBSUB_PURGE = PUBSUB_OWNER_SET + '/purge' + IN_NS_PUBSUB_OWNER
    70 PUBSUB_DELETE = PUBSUB_OWNER_SET + '/delete' + IN_NS_PUBSUB_OWNER
     34NS_PUBSUB_SUBSCRIBE_OPTIONS = NS_PUBSUB + "#subscribe_options"
     35
     36# XPath to match pubsub requests
     37PUBSUB_REQUEST = '/iq[@type="get" or @type="set"]/' + \
     38                    'pubsub[@xmlns="' + NS_PUBSUB + '" or ' + \
     39                           '@xmlns="' + NS_PUBSUB_OWNER + '"]'
    7140
    7241class SubscriptionPending(Exception):
     
    9968
    10069
    101 class BadRequest(PubSubError):
     70class BadRequest(error.StanzaError):
    10271    """
    10372    Bad request stanza error.
    10473    """
    10574    def __init__(self, pubsubCondition=None, text=None):
    106         PubSubError.__init__(self, 'bad-request', pubsubCondition, text)
     75        if pubsubCondition:
     76            appCondition = domish.Element((NS_PUBSUB_ERRORS, pubsubCondition))
     77        else:
     78            appCondition = None
     79        error.StanzaError.__init__(self, 'bad-request',
     80                                         text=text,
     81                                         appCondition=appCondition)
    10782
    10883
     
    168143
    169144
    170 class _PubSubRequest(xmlstream.IQ):
    171     """
    172     Publish subscribe request.
    173 
    174     @ivar verb: Request verb
    175     @type verb: C{str}
    176     @ivar namespace: Request namespace.
    177     @type namespace: C{str}
    178     @ivar method: Type attribute of the IQ request. Either C{'set'} or C{'get'}
    179     @type method: C{str}
    180     @ivar command: Command element of the request. This is the direct child of
    181                    the C{pubsub} element in the C{namespace} with the name
    182                    C{verb}.
    183     """
    184 
    185     def __init__(self, xs, verb, namespace=NS_PUBSUB, method='set'):
    186         xmlstream.IQ.__init__(self, xs, method)
    187         self.addElement((namespace, 'pubsub'))
    188 
    189         self.command = self.pubsub.addElement(verb)
    190 
    191 
    192     def send(self, to):
    193         """
    194         Send out request.
    195 
    196         Extends L{xmlstream.IQ.send} by requiring the C{to} parameter to be
    197         a L{JID} instance.
    198 
    199         @param to: Entity to send the request to.
    200         @type to: L{JID}
    201         """
    202         destination = to.full()
    203         return xmlstream.IQ.send(self, destination)
     145class PubSubRequest(generic.Stanza):
     146    """
     147    A publish-subscribe request.
     148
     149    The set of instance variables used depends on the type of request. If
     150    a variable is not applicable or not passed in the request, its value is
     151    C{None}.
     152
     153    @ivar verb: The type of publish-subscribe request. See L{_requestVerbMap}.
     154    @type verb: C{str}.
     155
     156    @ivar affiliations: Affiliations to be modified.
     157    @type affiliations: C{set}
     158    @ivar items: The items to be published, as L{domish.Element}s.
     159    @type items: C{list}
     160    @ivar itemIdentifiers: Identifiers of the items to be retrieved or
     161                           retracted.
     162    @type itemIdentifiers: C{set}
     163    @ivar maxItems: Maximum number of items to retrieve.
     164    @type maxItems: C{int}.
     165    @ivar nodeIdentifier: Identifier of the node the request is about.
     166    @type nodeIdentifier: C{unicode}
     167    @ivar nodeType: The type of node that should be created, or for which the
     168                    configuration is retrieved. C{'leaf'} or C{'collection'}.
     169    @type nodeType: C{str}
     170    @ivar options: Configurations options for nodes, subscriptions and publish
     171                   requests.
     172    @type options: L{data_form.Form}
     173    @ivar subscriber: The subscribing entity.
     174    @type subscriber: L{JID}
     175    @ivar subscriptionIdentifier: Identifier for a specific subscription.
     176    @type subscriptionIdentifier: C{unicode}
     177    @ivar subscriptions: Subscriptions to be modified, as a set of
     178                         L{Subscription}.
     179    @type subscriptions: C{set}
     180    """
     181
     182    verb = None
     183
     184    affiliations = None
     185    items = None
     186    itemIdentifiers = None
     187    maxItems = None
     188    nodeIdentifier = None
     189    nodeType = None
     190    options = None
     191    subscriber = None
     192    subscriptionIdentifier = None
     193    subscriptions = None
     194
     195    # Map request iq type and subelement name to request verb
     196    _requestVerbMap = {
     197        ('set', NS_PUBSUB, 'publish'): 'publish',
     198        ('set', NS_PUBSUB, 'subscribe'): 'subscribe',
     199        ('set', NS_PUBSUB, 'unsubscribe'): 'unsubscribe',
     200        ('get', NS_PUBSUB, 'options'): 'optionsGet',
     201        ('set', NS_PUBSUB, 'options'): 'optionsSet',
     202        ('get', NS_PUBSUB, 'subscriptions'): 'subscriptions',
     203        ('get', NS_PUBSUB, 'affiliations'): 'affiliations',
     204        ('set', NS_PUBSUB, 'create'): 'create',
     205        ('get', NS_PUBSUB_OWNER, 'default'): 'default',
     206        ('get', NS_PUBSUB_OWNER, 'configure'): 'configureGet',
     207        ('set', NS_PUBSUB_OWNER, 'configure'): 'configureSet',
     208        ('get', NS_PUBSUB, 'items'): 'items',
     209        ('set', NS_PUBSUB, 'retract'): 'retract',
     210        ('set', NS_PUBSUB_OWNER, 'purge'): 'purge',
     211        ('set', NS_PUBSUB_OWNER, 'delete'): 'delete',
     212        ('get', NS_PUBSUB_OWNER, 'affiliations'): 'affiliationsGet',
     213        ('set', NS_PUBSUB_OWNER, 'affiliations'): 'affiliationsSet',
     214        ('get', NS_PUBSUB_OWNER, 'subscriptions'): 'subscriptionsGet',
     215        ('set', NS_PUBSUB_OWNER, 'subscriptions'): 'subscriptionsSet',
     216    }
     217
     218    # Map request verb to request iq type and subelement name
     219    _verbRequestMap = dict(((v, k) for k, v in _requestVerbMap.iteritems()))
     220
     221    # Map request verb to parameter handler names
     222    _parameters = {
     223        'publish': ['node', 'items'],
     224        'subscribe': ['nodeOrEmpty', 'jid'],
     225        'unsubscribe': ['nodeOrEmpty', 'jid'],
     226        'optionsGet': ['nodeOrEmpty', 'jid'],
     227        'optionsSet': ['nodeOrEmpty', 'jid', 'options'],
     228        'subscriptions': [],
     229        'affiliations': [],
     230        'create': ['nodeOrNone'],
     231        'default': ['default'],
     232        'configureGet': ['nodeOrEmpty'],
     233        'configureSet': ['nodeOrEmpty', 'configure'],
     234        'items': ['node', 'maxItems', 'itemIdentifiers'],
     235        'retract': ['node', 'itemIdentifiers'],
     236        'purge': ['node'],
     237        'delete': ['node'],
     238        'affiliationsGet': [],
     239        'affiliationsSet': [],
     240        'subscriptionsGet': [],
     241        'subscriptionsSet': [],
     242    }
     243
     244    def __init__(self, verb=None):
     245        self.verb = verb
     246
     247
     248    @staticmethod
     249    def _findForm(element, formNamespace):
     250        """
     251        Find a Data Form.
     252
     253        Look for an element that represents a Data Form with the specified
     254        form namespace as a child element of the given element.
     255        """
     256        if not element:
     257            return None
     258
     259        form = None
     260        for child in element.elements():
     261            try:
     262                form = data_form.Form.fromElement(child)
     263            except data_form.Error:
     264                continue
     265
     266            if form.formNamespace != NS_PUBSUB_NODE_CONFIG:
     267                continue
     268
     269        return form
     270
     271
     272    def _parse_node(self, verbElement):
     273        """
     274        Parse the required node identifier out of the verbElement.
     275        """
     276        try:
     277            self.nodeIdentifier = verbElement["node"]
     278        except KeyError:
     279            raise BadRequest('nodeid-required')
     280
     281
     282    def _render_node(self, verbElement):
     283        """
     284        Render the required node identifier on the verbElement.
     285        """
     286        if not self.nodeIdentifier:
     287            raise Exception("Node identifier is required")
     288
     289        verbElement['node'] = self.nodeIdentifier
     290
     291
     292    def _parse_nodeOrEmpty(self, verbElement):
     293        """
     294        Parse the node identifier out of the verbElement. May be empty.
     295        """
     296        self.nodeIdentifier = verbElement.getAttribute("node", '')
     297
     298
     299    def _render_nodeOrEmpty(self, verbElement):
     300        """
     301        Render the node identifier on the verbElement. May be empty.
     302        """
     303        if self.nodeIdentifier:
     304            verbElement['node'] = self.nodeIdentifier
     305
     306
     307    def _parse_nodeOrNone(self, verbElement):
     308        """
     309        Parse the optional node identifier out of the verbElement.
     310        """
     311        self.nodeIdentifier = verbElement.getAttribute("node")
     312
     313
     314    def _render_nodeOrNone(self, verbElement):
     315        """
     316        Render the optional node identifier on the verbElement.
     317        """
     318        if self.nodeIdentifier:
     319            verbElement['node'] = self.nodeIdentifier
     320
     321
     322    def _parse_items(self, verbElement):
     323        """
     324        Parse items out of the verbElement for publish requests.
     325        """
     326        self.items = []
     327        for element in verbElement.elements():
     328            if element.uri == NS_PUBSUB and element.name == 'item':
     329                self.items.append(element)
     330
     331
     332    def _render_items(self, verbElement):
     333        """
     334        Render items into the verbElement for publish requests.
     335        """
     336        if self.items:
     337            for item in self.items:
     338                verbElement.addChild(item)
     339
     340
     341    def _parse_jid(self, verbElement):
     342        """
     343        Parse subscriber out of the verbElement for un-/subscribe requests.
     344        """
     345        try:
     346            self.subscriber = jid.internJID(verbElement["jid"])
     347        except KeyError:
     348            raise BadRequest('jid-required')
     349
     350
     351    def _render_jid(self, verbElement):
     352        """
     353        Render subscriber into the verbElement for un-/subscribe requests.
     354        """
     355        verbElement['jid'] = self.subscriber.full()
     356
     357
     358    def _parse_default(self, verbElement):
     359        """
     360        Parse node type out of a request for the default node configuration.
     361        """
     362        form = PubSubRequest._findForm(verbElement, NS_PUBSUB_NODE_CONFIG)
     363        if form and form.formType == 'submit':
     364            values = form.getValues()
     365            self.nodeType = values.get('pubsub#node_type', 'leaf')
     366        else:
     367            self.nodeType = 'leaf'
     368
     369
     370    def _parse_configure(self, verbElement):
     371        """
     372        Parse options out of a request for setting the node configuration.
     373        """
     374        form = PubSubRequest._findForm(verbElement, NS_PUBSUB_NODE_CONFIG)
     375        if form:
     376            if form.formType == 'submit':
     377                self.options = form.getValues()
     378            elif form.formType == 'cancel':
     379                self.options = {}
     380            else:
     381                raise BadRequest(text="Unexpected form type %r" % form.formType)
     382        else:
     383            raise BadRequest(text="Missing configuration form")
     384
     385
     386
     387    def _parse_itemIdentifiers(self, verbElement):
     388        """
     389        Parse item identifiers out of items and retract requests.
     390        """
     391        self.itemIdentifiers = []
     392        for element in verbElement.elements():
     393            if element.uri == NS_PUBSUB and element.name == 'item':
     394                try:
     395                    self.itemIdentifiers.append(element["id"])
     396                except KeyError:
     397                    raise BadRequest()
     398
     399
     400    def _render_itemIdentifiers(self, verbElement):
     401        """
     402        Render item identifiers into items and retract requests.
     403        """
     404        if self.itemIdentifiers:
     405            for itemIdentifier in self.itemIdentifiers:
     406                item = verbElement.addElement('item')
     407                item['id'] = itemIdentifier
     408
     409
     410    def _parse_maxItems(self, verbElement):
     411        """
     412        Parse maximum items out of an items request.
     413        """
     414        value = verbElement.getAttribute('max_items')
     415
     416        if value:
     417            try:
     418                self.maxItems = int(value)
     419            except ValueError:
     420                raise BadRequest(text="Field max_items requires a positive " +
     421                                      "integer value")
     422
     423
     424    def _render_maxItems(self, verbElement):
     425        """
     426        Parse maximum items into an items request.
     427        """
     428        if self.maxItems:
     429            verbElement['max_items'] = unicode(self.maxItems)
     430
     431
     432    def _parse_options(self, verbElement):
     433        form = PubSubRequest._findForm(verbElement, NS_PUBSUB_SUBSCRIBE_OPTIONS)
     434        if form:
     435            if form.formType == 'submit':
     436                self.options = form.getValues()
     437            elif form.formType == 'cancel':
     438                self.options = {}
     439            else:
     440                raise BadRequest(text="Unexpected form type %r" % form.formType)
     441        else:
     442            raise BadRequest(text="Missing options form")
     443
     444    def parseElement(self, element):
     445        """
     446        Parse the publish-subscribe verb and parameters out of a request.
     447        """
     448        generic.Stanza.parseElement(self, element)
     449
     450        for child in element.pubsub.elements():
     451            key = (self.stanzaType, child.uri, child.name)
     452            try:
     453                verb = self._requestVerbMap[key]
     454            except KeyError:
     455                continue
     456            else:
     457                self.verb = verb
     458                break
     459
     460        if not self.verb:
     461            raise NotImplementedError()
     462
     463        for parameter in self._parameters[verb]:
     464            getattr(self, '_parse_%s' % parameter)(child)
     465
     466
     467    def send(self, xs):
     468        """
     469        Send this request to its recipient.
     470
     471        This renders all of the relevant parameters for this specific
     472        requests into an L{xmlstream.IQ}, and invoke its C{send} method.
     473        This returns a deferred that fires upon reception of a response. See
     474        L{xmlstream.IQ} for details.
     475
     476        @param xs: The XML stream to send the request on.
     477        @type xs: L{xmlstream.XmlStream}
     478        @rtype: L{defer.Deferred}.
     479        """
     480
     481        try:
     482            (self.stanzaType,
     483             childURI,
     484             childName) = self._verbRequestMap[self.verb]
     485        except KeyError:
     486            raise NotImplementedError()
     487
     488        iq = xmlstream.IQ(xs, self.stanzaType)
     489        iq.addElement((childURI, 'pubsub'))
     490        verbElement = iq.pubsub.addElement(childName)
     491
     492        if self.sender:
     493            iq['from'] = self.sender.full()
     494        if self.recipient:
     495            iq['to'] = self.recipient.full()
     496
     497        for parameter in self._parameters[self.verb]:
     498            getattr(self, '_render_%s' % parameter)(verbElement)
     499
     500        return iq.send()
    204501
    205502
     
    337634        @type nodeIdentifier: C{unicode}
    338635        """
    339 
    340 
    341         request = _PubSubRequest(self.xmlstream, 'create')
    342         if nodeIdentifier:
    343             request.command['node'] = nodeIdentifier
     636        request = PubSubRequest('create')
     637        request.recipient = service
     638        request.nodeIdentifier = nodeIdentifier
    344639
    345640        def cb(iq):
     
    351646            return new_node
    352647
    353         return request.send(service).addCallback(cb)
     648        d = request.send(self.xmlstream)
     649        d.addCallback(cb)
     650        return d
    354651
    355652
     
    363660        @type nodeIdentifier: C{unicode}
    364661        """
    365         request = _PubSubRequest(self.xmlstream, 'delete', NS_PUBSUB_OWNER)
    366         request.command['node'] = nodeIdentifier
    367         return request.send(service)
     662        request = PubSubRequest('delete')
     663        request.recipient = service
     664        request.nodeIdentifier = nodeIdentifier
     665        return request.send(self.xmlstream)
    368666
    369667
     
    380678        @type subscriber: L{JID}
    381679        """
    382         request = _PubSubRequest(self.xmlstream, 'subscribe')
    383         if nodeIdentifier:
    384             request.command['node'] = nodeIdentifier
    385         request.command['jid'] = subscriber.full()
     680        request = PubSubRequest('subscribe')
     681        request.recipient = service
     682        request.nodeIdentifier = nodeIdentifier
     683        request.subscriber = subscriber
    386684
    387685        def cb(iq):
     
    398696                return None
    399697
    400         return request.send(service).addCallback(cb)
     698        d = request.send(self.xmlstream)
     699        d.addCallback(cb)
     700        return d
    401701
    402702
     
    412712        @type subscriber: L{JID}
    413713        """
    414         request = _PubSubRequest(self.xmlstream, 'unsubscribe')
    415         if nodeIdentifier:
    416             request.command['node'] = nodeIdentifier
    417         request.command['jid'] = subscriber.full()
    418         return request.send(service)
     714        request = PubSubRequest('unsubscribe')
     715        request.recipient = service
     716        request.nodeIdentifier = nodeIdentifier
     717        request.subscriber = subscriber
     718        return request.send(self.xmlstream)
    419719
    420720
     
    430730        @type items: C{list}
    431731        """
    432         request = _PubSubRequest(self.xmlstream, 'publish')
    433         request.command['node'] = nodeIdentifier
    434         if items:
    435             for item in items:
    436                 request.command.addChild(item)
    437 
    438         return request.send(service)
     732        request = PubSubRequest('publish')
     733        request.recipient = service
     734        request.nodeIdentifier = nodeIdentifier
     735        request.items = items
     736        return request.send(self.xmlstream)
    439737
    440738
     
    450748        @type maxItems: C{int}
    451749        """
    452         request = _PubSubRequest(self.xmlstream, 'items', method='get')
    453         if nodeIdentifier:
    454             request.command['node'] = nodeIdentifier
     750        request = PubSubRequest('items')
     751        request.recipient = service
     752        request.nodeIdentifier = nodeIdentifier
    455753        if maxItems:
    456             request.command["max_items"] = str(int(maxItems))
     754            request.maxItems = str(int(maxItems))
    457755
    458756        def cb(iq):
     
    463761            return items
    464762
    465         return request.send(service).addCallback(cb)
     763        d = request.send(self.xmlstream)
     764        d.addCallback(cb)
     765        return d
    466766
    467767
     
    498798
    499799    iqHandlers = {
    500             PUBSUB_PUBLISH: '_onPublish',
    501             PUBSUB_CREATE: '_onCreate',
    502             PUBSUB_SUBSCRIBE: '_onSubscribe',
    503             PUBSUB_OPTIONS_GET: '_onOptionsGet',
    504             PUBSUB_OPTIONS_SET: '_onOptionsSet',
    505             PUBSUB_AFFILIATIONS: '_onAffiliations',
    506             PUBSUB_ITEMS: '_onItems',
    507             PUBSUB_RETRACT: '_onRetract',
    508             PUBSUB_SUBSCRIPTIONS: '_onSubscriptions',
    509             PUBSUB_UNSUBSCRIBE: '_onUnsubscribe',
    510 
    511             PUBSUB_AFFILIATIONS_GET: '_onAffiliationsGet',
    512             PUBSUB_AFFILIATIONS_SET: '_onAffiliationsSet',
    513             PUBSUB_CONFIGURE_GET: '_onConfigureGet',
    514             PUBSUB_CONFIGURE_SET: '_onConfigureSet',
    515             PUBSUB_DEFAULT: '_onDefault',
    516             PUBSUB_PURGE: '_onPurge',
    517             PUBSUB_DELETE: '_onDelete',
    518             PUBSUB_SUBSCRIPTIONS_GET: '_onSubscriptionsGet',
    519             PUBSUB_SUBSCRIPTIONS_SET: '_onSubscriptionsSet',
    520 
     800            '/*': '_onPubSubRequest',
    521801            }
    522802
     
    531811
    532812    def connectionMade(self):
    533         self.xmlstream.addObserver(PUBSUB_GET, self.handleRequest)
    534         self.xmlstream.addObserver(PUBSUB_SET, self.handleRequest)
    535         self.xmlstream.addObserver(PUBSUB_OWNER_GET, self.handleRequest)
    536         self.xmlstream.addObserver(PUBSUB_OWNER_SET, self.handleRequest)
     813        self.xmlstream.addObserver(PUBSUB_REQUEST, self.handleRequest)
    537814
    538815
     
    586863
    587864
    588     def _findForm(self, element, formNamespace):
    589         if not element:
    590             return None
    591 
    592         form = None
    593         for child in element.elements():
    594             try:
    595                 form = data_form.Form.fromElement(child)
    596             except data_form.Error:
    597                 continue
    598 
    599             if form.formNamespace != NS_PUBSUB_NODE_CONFIG:
    600                 continue
    601 
    602         return form
    603 
    604 
    605     def _getParameter_node(self, commandElement):
    606         try:
    607             return commandElement["node"]
    608         except KeyError:
    609             raise BadRequest('nodeid-required')
    610 
    611 
    612     def _getParameter_nodeOrEmpty(self, commandElement):
    613         return commandElement.getAttribute("node", '')
    614 
    615 
    616     def _getParameter_jid(self, commandElement):
    617         try:
    618             return jid.internJID(commandElement["jid"])
    619         except KeyError:
    620             raise BadRequest('jid-required')
    621 
    622 
    623     def _getParameter_max_items(self, commandElement):
    624         value = commandElement.getAttribute('max_items')
    625 
    626         if value:
    627             try:
    628                 return int(value)
    629             except ValueError:
    630                 raise BadRequest(text="Field max_items requires a positive " +
    631                                       "integer value")
    632         else:
    633             return None
    634 
    635 
    636     def _getParameters(self, iq, *names):
    637         requestor = jid.internJID(iq["from"]).userhostJID()
    638         service = jid.internJID(iq["to"])
    639 
    640         params = [requestor, service]
    641 
    642         if names:
    643             command = names[0]
    644             commandElement = getattr(iq.pubsub, command)
    645             if not commandElement:
    646                 raise Exception("Could not find command element %r" % command)
    647 
    648         for name in names[1:]:
    649             try:
    650                 getter = getattr(self, '_getParameter_' + name)
    651             except KeyError:
    652                 raise Exception("No parameter getter for this name")
    653 
    654             params.append(getter(commandElement))
    655 
    656         return params
    657 
    658 
    659     def _onPublish(self, iq):
    660         requestor, service, nodeIdentifier = self._getParameters(
    661                 iq, 'publish', 'node')
    662 
    663         items = []
    664         for element in iq.pubsub.publish.elements():
    665             if element.uri == NS_PUBSUB and element.name == 'item':
    666                 items.append(element)
    667 
    668         return self.publish(requestor, service, nodeIdentifier, items)
    669 
    670 
    671     def _onSubscribe(self, iq):
    672         requestor, service, nodeIdentifier, subscriber = self._getParameters(
    673                 iq, 'subscribe', 'nodeOrEmpty', 'jid')
     865    def _onPubSubRequest(self, iq):
     866        request = PubSubRequest.fromElement(iq)
     867        handler = getattr(self, '_on_%s' % request.verb)
     868        return handler(request)
     869
     870
     871    def _on_publish(self, request):
     872        return self.publish(request.sender, request.recipient,
     873                            request.nodeIdentifier, request.items)
     874
     875
     876    def _on_subscribe(self, request):
    674877
    675878        def toResponse(result):
     
    682885            return response
    683886
    684         d = self.subscribe(requestor, service, nodeIdentifier, subscriber)
     887        d = self.subscribe(request.sender, request.recipient,
     888                           request.nodeIdentifier, request.subscriber)
    685889        d.addCallback(toResponse)
    686890        return d
    687891
    688892
    689     def _onUnsubscribe(self, iq):
    690         requestor, service, nodeIdentifier, subscriber = self._getParameters(
    691                 iq, 'unsubscribe', 'nodeOrEmpty', 'jid')
    692 
    693         return self.unsubscribe(requestor, service, nodeIdentifier, subscriber)
    694 
    695 
    696     def _onOptionsGet(self, iq):
     893    def _on_unsubscribe(self, request):
     894        return self.unsubscribe(request.sender, request.recipient,
     895                                request.nodeIdentifier, request.subscriber)
     896
     897
     898    def _on_optionsGet(self, request):
    697899        raise Unsupported('subscription-options')
    698900
    699901
    700     def _onOptionsSet(self, iq):
     902    def _on_optionsSet(self, request):
    701903        raise Unsupported('subscription-options')
    702904
    703905
    704     def _onSubscriptions(self, iq):
    705         requestor, service = self._getParameters(iq)
     906    def _on_subscriptions(self, request):
    706907
    707908        def toResponse(result):
     
    715916            return response
    716917
    717         d = self.subscriptions(requestor, service)
     918        d = self.subscriptions(request.sender, request.recipient)
    718919        d.addCallback(toResponse)
    719920        return d
    720921
    721922
    722     def _onAffiliations(self, iq):
    723         requestor, service = self._getParameters(iq)
     923    def _on_affiliations(self, request):
    724924
    725925        def toResponse(result):
     
    734934            return response
    735935
    736         d = self.affiliations(requestor, service)
     936        d = self.affiliations(request.sender, request.recipient)
    737937        d.addCallback(toResponse)
    738938        return d
    739939
    740940
    741     def _onCreate(self, iq):
    742         requestor, service = self._getParameters(iq)
    743         nodeIdentifier = iq.pubsub.create.getAttribute("node")
     941    def _on_create(self, request):
    744942
    745943        def toResponse(result):
    746             if not nodeIdentifier or nodeIdentifier != result:
     944            if not request.nodeIdentifier or request.nodeIdentifier != result:
    747945                response = domish.Element((NS_PUBSUB, 'pubsub'))
    748946                create = response.addElement('create')
     
    752950                return None
    753951
    754         d = self.create(requestor, service, nodeIdentifier)
     952        d = self.create(request.sender, request.recipient,
     953                        request.nodeIdentifier)
    755954        d.addCallback(toResponse)
    756955        return d
     
    772971        return fields
    773972
     973
    774974    def _formFromConfiguration(self, values):
    775975        options = self.getConfigurationOptions()
     
    780980
    781981        return form
     982
    782983
    783984    def _checkConfiguration(self, values):
     
    8061007
    8071008
    808     def _onDefault(self, iq):
    809         requestor, service = self._getParameters(iq)
     1009    def _on_default(self, request):
    8101010
    8111011        def toResponse(options):
     
    8151015            return response
    8161016
    817         form = self._findForm(iq.pubsub.config, NS_PUBSUB_NODE_CONFIG)
    818         values = form and form.formType == 'result' and form.getValues() or {}
    819         nodeType = values.get('pubsub#node_type', 'leaf')
    820 
    821         if nodeType not in ('leaf', 'collections'):
     1017        if request.nodeType not in ('leaf', 'collection'):
    8221018            return defer.fail(error.StanzaError('not-acceptable'))
    8231019
    824         d = self.getDefaultConfiguration(requestor, service, nodeType)
     1020        d = self.getDefaultConfiguration(request.sender, request.recipient,
     1021                                         request.nodeType)
    8251022        d.addCallback(toResponse)
    8261023        return d
    8271024
    8281025
    829     def _onConfigureGet(self, iq):
    830         requestor, service, nodeIdentifier = self._getParameters(
    831                 iq, 'configure', 'nodeOrEmpty')
    832 
     1026    def _on_configureGet(self, request):
    8331027        def toResponse(options):
    8341028            response = domish.Element((NS_PUBSUB_OWNER, "pubsub"))
    8351029            configure = response.addElement("configure")
    836             configure.addChild(self._formFromConfiguration(options).toElement())
    837 
    838             if nodeIdentifier:
    839                 configure["node"] = nodeIdentifier
     1030            form = self._formFromConfiguration(options)
     1031            configure.addChild(form.toElement())
     1032
     1033            if request.nodeIdentifier:
     1034                configure["node"] = request.nodeIdentifier
    8401035
    8411036            return response
    8421037
    843         d = self.getConfiguration(requestor, service, nodeIdentifier)
     1038        d = self.getConfiguration(request.sender, request.recipient,
     1039                                  request.nodeIdentifier)
    8441040        d.addCallback(toResponse)
    8451041        return d
    8461042
    8471043
    848     def _onConfigureSet(self, iq):
    849         requestor, service, nodeIdentifier = self._getParameters(
    850                 iq, 'configure', 'nodeOrEmpty')
    851 
    852         # Search configuration form with correct FORM_TYPE and process it
    853 
    854         form = self._findForm(iq.pubsub.configure, NS_PUBSUB_NODE_CONFIG)
    855 
    856         if form:
    857             if form.formType == 'submit':
    858                 options = self._checkConfiguration(form.getValues())
    859 
    860                 return self.setConfiguration(requestor, service,
    861                                              nodeIdentifier, options)
    862             elif form.formType == 'cancel':
    863                 return None
    864 
    865         raise BadRequest()
    866 
    867 
    868     def _onItems(self, iq):
    869         requestor, service, nodeIdentifier, maxItems = self._getParameters(
    870                 iq, 'items', 'nodeOrEmpty', 'max_items')
    871 
    872         itemIdentifiers = []
    873         for child in iq.pubsub.items.elements():
    874             if child.name == 'item' and child.uri == NS_PUBSUB:
    875                 try:
    876                     itemIdentifiers.append(child["id"])
    877                 except KeyError:
    878                     raise BadRequest()
     1044    def _on_configureSet(self, request):
     1045        if request.options:
     1046            request.options = self._checkConfiguration(request.options)
     1047            return self.setConfiguration(request.sender, request.recipient,
     1048                                         request.nodeIdentifier,
     1049                                         request.options)
     1050        else:
     1051            return None
     1052
     1053
     1054
     1055    def _on_items(self, request):
    8791056
    8801057        def toResponse(result):
    8811058            response = domish.Element((NS_PUBSUB, 'pubsub'))
    8821059            items = response.addElement('items')
    883             if nodeIdentifier:
    884                 items["node"] = nodeIdentifier
     1060            items["node"] = request.nodeIdentifier
    8851061
    8861062            for item in result:
     
    8891065            return response
    8901066
    891         d = self.items(requestor, service, nodeIdentifier, maxItems,
    892                        itemIdentifiers)
     1067        d = self.items(request.sender, request.recipient,
     1068                       request.nodeIdentifier, request.maxItems,
     1069                       request.itemIdentifiers)
    8931070        d.addCallback(toResponse)
    8941071        return d
    8951072
    8961073
    897     def _onRetract(self, iq):
    898         requestor, service, nodeIdentifier = self._getParameters(
    899                 iq, 'retract', 'node')
    900 
    901         itemIdentifiers = []
    902         for child in iq.pubsub.retract.elements():
    903             if child.uri == NS_PUBSUB and child.name == 'item':
    904                 try:
    905                     itemIdentifiers.append(child["id"])
    906                 except KeyError:
    907                     raise BadRequest()
    908 
    909         return self.retract(requestor, service, nodeIdentifier,
    910                             itemIdentifiers)
    911 
    912 
    913     def _onPurge(self, iq):
    914         requestor, service, nodeIdentifier = self._getParameters(
    915                 iq, 'purge', 'node')
    916         return self.purge(requestor, service, nodeIdentifier)
    917 
    918 
    919     def _onDelete(self, iq):
    920         requestor, service, nodeIdentifier = self._getParameters(
    921                 iq, 'delete', 'node')
    922         return self.delete(requestor, service, nodeIdentifier)
    923 
    924 
    925     def _onAffiliationsGet(self, iq):
     1074    def _on_retract(self, request):
     1075        return self.retract(request.sender, request.recipient,
     1076                            request.nodeIdentifier, request.itemIdentifiers)
     1077
     1078
     1079    def _on_purge(self, request):
     1080        return self.purge(request.sender, request.recipient,
     1081                          request.nodeIdentifier)
     1082
     1083
     1084    def _on_delete(self, request):
     1085        return self.delete(request.sender, request.recipient,
     1086                           request.nodeIdentifier)
     1087
     1088
     1089    def _on_affiliationsGet(self, iq):
    9261090        raise Unsupported('modify-affiliations')
    9271091
    9281092
    929     def _onAffiliationsSet(self, iq):
     1093    def _on_affiliationsSet(self, iq):
    9301094        raise Unsupported('modify-affiliations')
    9311095
    9321096
    933     def _onSubscriptionsGet(self, iq):
     1097    def _on_subscriptionsGet(self, iq):
    9341098        raise Unsupported('manage-subscriptions')
    9351099
    9361100
    937     def _onSubscriptionsSet(self, iq):
     1101    def _on_subscriptionsSet(self, iq):
    9381102        raise Unsupported('manage-subscriptions')
    9391103
  • wokkel/test/test_pubsub.py

    r56 r57  
    1616
    1717from wokkel import data_form, disco, iwokkel, pubsub, shim
     18from wokkel.generic import parseXml
    1819from wokkel.test.helpers import TestableRequestHandlerMixin, XmlStreamStub
    1920
     
    488489
    489490
     491class PubSubRequestTest(unittest.TestCase):
     492
     493    def test_fromElementPublish(self):
     494        """
     495        Test parsing a publish request.
     496        """
     497
     498        xml = """
     499        <iq type='set' to='pubsub.example.org'
     500                       from='user@example.org'>
     501          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     502            <publish node='test'/>
     503          </pubsub>
     504        </iq>
     505        """
     506
     507        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
     508        self.assertEqual('publish', request.verb)
     509        self.assertEqual(JID('user@example.org'), request.sender)
     510        self.assertEqual(JID('pubsub.example.org'), request.recipient)
     511        self.assertEqual('test', request.nodeIdentifier)
     512        self.assertEqual([], request.items)
     513
     514
     515    def test_fromElementPublishItems(self):
     516        """
     517        Test parsing a publish request with items.
     518        """
     519
     520        xml = """
     521        <iq type='set' to='pubsub.example.org'
     522                       from='user@example.org'>
     523          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     524            <publish node='test'>
     525              <item id="item1"/>
     526              <item id="item2"/>
     527            </publish>
     528          </pubsub>
     529        </iq>
     530        """
     531
     532        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
     533        self.assertEqual(2, len(request.items))
     534        self.assertEqual(u'item1', request.items[0]["id"])
     535        self.assertEqual(u'item2', request.items[1]["id"])
     536
     537
     538    def test_fromElementPublishNoNode(self):
     539        """
     540        A publish request to the root node should raise an exception.
     541        """
     542        xml = """
     543        <iq type='set' to='pubsub.example.org'
     544                       from='user@example.org'>
     545          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     546            <publish/>
     547          </pubsub>
     548        </iq>
     549        """
     550
     551        err = self.assertRaises(error.StanzaError,
     552                                pubsub.PubSubRequest.fromElement,
     553                                parseXml(xml))
     554        self.assertEqual('bad-request', err.condition)
     555        self.assertEqual(NS_PUBSUB_ERRORS, err.appCondition.uri)
     556        self.assertEqual('nodeid-required', err.appCondition.name)
     557
     558
     559    def test_fromElementSubscribe(self):
     560        """
     561        Test parsing a subscription request.
     562        """
     563
     564        xml = """
     565        <iq type='set' to='pubsub.example.org'
     566                       from='user@example.org'>
     567          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     568            <subscribe node='test' jid='user@example.org/Home'/>
     569          </pubsub>
     570        </iq>
     571        """
     572
     573        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
     574        self.assertEqual('subscribe', request.verb)
     575        self.assertEqual(JID('user@example.org'), request.sender)
     576        self.assertEqual(JID('pubsub.example.org'), request.recipient)
     577        self.assertEqual('test', request.nodeIdentifier)
     578        self.assertEqual(JID('user@example.org/Home'), request.subscriber)
     579
     580
     581    def test_fromElementSubscribeEmptyNode(self):
     582        """
     583        Test parsing a subscription request to the root node.
     584        """
     585
     586        xml = """
     587        <iq type='set' to='pubsub.example.org'
     588                       from='user@example.org'>
     589          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     590            <subscribe jid='user@example.org/Home'/>
     591          </pubsub>
     592        </iq>
     593        """
     594
     595        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
     596        self.assertEqual('', request.nodeIdentifier)
     597
     598
     599    def test_fromElementSubscribeNoJID(self):
     600        """
     601        Subscribe requests without a JID should raise a bad-request exception.
     602        """
     603        xml = """
     604        <iq type='set' to='pubsub.example.org'
     605                       from='user@example.org'>
     606          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     607            <subscribe node='test'/>
     608          </pubsub>
     609        </iq>
     610        """
     611        err = self.assertRaises(error.StanzaError,
     612                                pubsub.PubSubRequest.fromElement,
     613                                parseXml(xml))
     614        self.assertEqual('bad-request', err.condition)
     615        self.assertEqual(NS_PUBSUB_ERRORS, err.appCondition.uri)
     616        self.assertEqual('jid-required', err.appCondition.name)
     617
     618    def test_fromElementUnsubscribe(self):
     619        """
     620        Test parsing an unsubscription request.
     621        """
     622
     623        xml = """
     624        <iq type='set' to='pubsub.example.org'
     625                       from='user@example.org'>
     626          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     627            <unsubscribe node='test' jid='user@example.org/Home'/>
     628          </pubsub>
     629        </iq>
     630        """
     631
     632        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
     633        self.assertEqual('unsubscribe', request.verb)
     634        self.assertEqual(JID('user@example.org'), request.sender)
     635        self.assertEqual(JID('pubsub.example.org'), request.recipient)
     636        self.assertEqual('test', request.nodeIdentifier)
     637        self.assertEqual(JID('user@example.org/Home'), request.subscriber)
     638
     639
     640    def test_fromElementUnsubscribeNoJID(self):
     641        """
     642        Unsubscribe requests without a JID should raise a bad-request exception.
     643        """
     644        xml = """
     645        <iq type='set' to='pubsub.example.org'
     646                       from='user@example.org'>
     647          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     648            <unsubscribe node='test'/>
     649          </pubsub>
     650        </iq>
     651        """
     652        err = self.assertRaises(error.StanzaError,
     653                                pubsub.PubSubRequest.fromElement,
     654                                parseXml(xml))
     655        self.assertEqual('bad-request', err.condition)
     656        self.assertEqual(NS_PUBSUB_ERRORS, err.appCondition.uri)
     657        self.assertEqual('jid-required', err.appCondition.name)
     658
     659
     660    def test_fromElementOptionsGet(self):
     661        """
     662        Test parsing a request for getting subscription options.
     663        """
     664
     665        xml = """
     666        <iq type='get' to='pubsub.example.org'
     667                       from='user@example.org'>
     668          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     669            <options node='test' jid='user@example.org/Home'/>
     670          </pubsub>
     671        </iq>
     672        """
     673
     674        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
     675        self.assertEqual('optionsGet', request.verb)
     676
     677
     678    def test_fromElementOptionsSet(self):
     679        """
     680        Test parsing a request for setting subscription options.
     681        """
     682
     683        xml = """
     684        <iq type='set' to='pubsub.example.org'
     685                       from='user@example.org'>
     686          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     687            <options node='test' jid='user@example.org/Home'>
     688              <x xmlns='jabber:x:data' type='submit'>
     689                <field var='FORM_TYPE' type='hidden'>
     690                  <value>http://jabber.org/protocol/pubsub#subscribe_options</value>
     691                </field>
     692                <field var='pubsub#deliver'><value>1</value></field>
     693              </x>
     694            </options>
     695          </pubsub>
     696        </iq>
     697        """
     698
     699        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
     700        self.assertEqual('optionsSet', request.verb)
     701        self.assertEqual(JID('user@example.org'), request.sender)
     702        self.assertEqual(JID('pubsub.example.org'), request.recipient)
     703        self.assertEqual('test', request.nodeIdentifier)
     704        self.assertEqual(JID('user@example.org/Home'), request.subscriber)
     705        self.assertEqual({'pubsub#deliver': '1'}, request.options)
     706
     707
     708    def test_fromElementOptionsSetCancel(self):
     709        """
     710        Test parsing a request for cancelling setting subscription options.
     711        """
     712
     713        xml = """
     714        <iq type='set' to='pubsub.example.org'
     715                       from='user@example.org'>
     716          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     717            <options node='test' jid='user@example.org/Home'>
     718              <x xmlns='jabber:x:data' type='cancel'/>
     719            </options>
     720          </pubsub>
     721        </iq>
     722        """
     723
     724        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
     725        self.assertEqual({}, request.options)
     726
     727
     728    def test_fromElementOptionsSetBadFormType(self):
     729        """
     730        On a options set request unknown fields should be ignored.
     731        """
     732
     733        xml = """
     734        <iq type='set' to='pubsub.example.org'
     735                       from='user@example.org'>
     736          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     737            <options node='test' jid='user@example.org/Home'>
     738              <x xmlns='jabber:x:data' type='result'>
     739                <field var='FORM_TYPE' type='hidden'>
     740                  <value>http://jabber.org/protocol/pubsub#node_config</value>
     741                </field>
     742                <field var='pubsub#deliver'><value>1</value></field>
     743              </x>
     744            </options>
     745          </pubsub>
     746        </iq>
     747        """
     748
     749        err = self.assertRaises(error.StanzaError,
     750                                pubsub.PubSubRequest.fromElement,
     751                                parseXml(xml))
     752        self.assertEqual('bad-request', err.condition)
     753        self.assertEqual(None, err.appCondition)
     754
     755
     756    def test_fromElementOptionsSetNoForm(self):
     757        """
     758        On a options set request a form is required.
     759        """
     760
     761        xml = """
     762        <iq type='set' to='pubsub.example.org'
     763                       from='user@example.org'>
     764          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     765            <options node='test' jid='user@example.org/Home'/>
     766          </pubsub>
     767        </iq>
     768        """
     769        err = self.assertRaises(error.StanzaError,
     770                                pubsub.PubSubRequest.fromElement,
     771                                parseXml(xml))
     772        self.assertEqual('bad-request', err.condition)
     773        self.assertEqual(None, err.appCondition)
     774
     775
     776    def test_fromElementSubscriptions(self):
     777        """
     778        Test parsing a request for all subscriptions.
     779        """
     780
     781        xml = """
     782        <iq type='get' to='pubsub.example.org'
     783                       from='user@example.org'>
     784          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     785            <subscriptions/>
     786          </pubsub>
     787        </iq>
     788        """
     789
     790        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
     791        self.assertEqual('subscriptions', request.verb)
     792        self.assertEqual(JID('user@example.org'), request.sender)
     793        self.assertEqual(JID('pubsub.example.org'), request.recipient)
     794
     795
     796    def test_fromElementAffiliations(self):
     797        """
     798        Test parsing a request for all affiliations.
     799        """
     800
     801        xml = """
     802        <iq type='get' to='pubsub.example.org'
     803                       from='user@example.org'>
     804          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     805            <affiliations/>
     806          </pubsub>
     807        </iq>
     808        """
     809
     810        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
     811        self.assertEqual('affiliations', request.verb)
     812        self.assertEqual(JID('user@example.org'), request.sender)
     813        self.assertEqual(JID('pubsub.example.org'), request.recipient)
     814
     815
     816    def test_fromElementCreate(self):
     817        """
     818        Test parsing a request to create a node.
     819        """
     820
     821        xml = """
     822        <iq type='set' to='pubsub.example.org'
     823                       from='user@example.org'>
     824          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     825            <create node='mynode'/>
     826          </pubsub>
     827        </iq>
     828        """
     829
     830        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
     831        self.assertEqual('create', request.verb)
     832        self.assertEqual(JID('user@example.org'), request.sender)
     833        self.assertEqual(JID('pubsub.example.org'), request.recipient)
     834        self.assertEqual('mynode', request.nodeIdentifier)
     835
     836
     837    def test_fromElementCreateInstant(self):
     838        """
     839        Test parsing a request to create an instant node.
     840        """
     841
     842        xml = """
     843        <iq type='set' to='pubsub.example.org'
     844                       from='user@example.org'>
     845          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     846            <create/>
     847          </pubsub>
     848        </iq>
     849        """
     850
     851        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
     852        self.assertIdentical(None, request.nodeIdentifier)
     853
     854
     855    def test_fromElementDefault(self):
     856        """
     857        Test parsing a request for the default node configuration.
     858        """
     859
     860        xml = """
     861        <iq type='get' to='pubsub.example.org'
     862                       from='user@example.org'>
     863          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
     864            <default/>
     865          </pubsub>
     866        </iq>
     867        """
     868
     869        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
     870        self.assertEqual('default', request.verb)
     871        self.assertEqual(JID('user@example.org'), request.sender)
     872        self.assertEqual(JID('pubsub.example.org'), request.recipient)
     873        self.assertEqual('leaf', request.nodeType)
     874
     875
     876    def test_fromElementDefaultCollection(self):
     877        """
     878        Parsing a request for the default configuration extracts the node type.
     879        """
     880
     881        xml = """
     882        <iq type='get' to='pubsub.example.org'
     883                       from='user@example.org'>
     884          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
     885            <default>
     886              <x xmlns='jabber:x:data' type='submit'>
     887                <field var='FORM_TYPE' type='hidden'>
     888                  <value>http://jabber.org/protocol/pubsub#node_config</value>
     889                </field>
     890                <field var='pubsub#node_type'>
     891                  <value>collection</value>
     892                </field>
     893              </x>
     894            </default>
     895
     896          </pubsub>
     897        </iq>
     898        """
     899
     900        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
     901        self.assertEqual('collection', request.nodeType)
     902
     903
     904    def test_fromElementConfigureGet(self):
     905        """
     906        Test parsing a node configuration get request.
     907        """
     908
     909        xml = """
     910        <iq type='get' to='pubsub.example.org'
     911                       from='user@example.org'>
     912          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
     913            <configure node='test'/>
     914          </pubsub>
     915        </iq>
     916        """
     917
     918        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
     919        self.assertEqual('configureGet', request.verb)
     920        self.assertEqual(JID('user@example.org'), request.sender)
     921        self.assertEqual(JID('pubsub.example.org'), request.recipient)
     922        self.assertEqual('test', request.nodeIdentifier)
     923
     924
     925    def test_fromElementConfigureSet(self):
     926        """
     927        On a node configuration set request the Data Form is parsed.
     928        """
     929
     930        xml = """
     931        <iq type='set' to='pubsub.example.org'
     932                       from='user@example.org'>
     933          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
     934            <configure node='test'>
     935              <x xmlns='jabber:x:data' type='submit'>
     936                <field var='FORM_TYPE' type='hidden'>
     937                  <value>http://jabber.org/protocol/pubsub#node_config</value>
     938                </field>
     939                <field var='pubsub#deliver_payloads'><value>0</value></field>
     940                <field var='pubsub#persist_items'><value>1</value></field>
     941              </x>
     942            </configure>
     943          </pubsub>
     944        </iq>
     945        """
     946
     947        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
     948        self.assertEqual('configureSet', request.verb)
     949        self.assertEqual(JID('user@example.org'), request.sender)
     950        self.assertEqual(JID('pubsub.example.org'), request.recipient)
     951        self.assertEqual('test', request.nodeIdentifier)
     952        self.assertEqual({'pubsub#deliver_payloads': '0',
     953                          'pubsub#persist_items': '1'}, request.options)
     954
     955
     956    def test_fromElementConfigureSetCancel(self):
     957        """
     958        The node configuration is cancelled, so no options.
     959        """
     960
     961        xml = """
     962        <iq type='set' to='pubsub.example.org'
     963                       from='user@example.org'>
     964          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
     965            <configure node='test'>
     966              <x xmlns='jabber:x:data' type='cancel'/>
     967            </configure>
     968          </pubsub>
     969        </iq>
     970        """
     971
     972        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
     973        self.assertEqual({}, request.options)
     974
     975
     976    def test_fromElementConfigureSetBadFormType(self):
     977        """
     978        On a node configuration set request unknown fields should be ignored.
     979        """
     980
     981        xml = """
     982        <iq type='set' to='pubsub.example.org'
     983                       from='user@example.org'>
     984          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
     985            <configure node='test'>
     986              <x xmlns='jabber:x:data' type='result'>
     987                <field var='FORM_TYPE' type='hidden'>
     988                  <value>http://jabber.org/protocol/pubsub#node_config</value>
     989                </field>
     990                <field var='pubsub#deliver_payloads'><value>0</value></field>
     991                <field var='x-myfield'><value>1</value></field>
     992              </x>
     993            </configure>
     994          </pubsub>
     995        </iq>
     996        """
     997
     998        err = self.assertRaises(error.StanzaError,
     999                                pubsub.PubSubRequest.fromElement,
     1000                                parseXml(xml))
     1001        self.assertEqual('bad-request', err.condition)
     1002        self.assertEqual(None, err.appCondition)
     1003
     1004
     1005    def test_fromElementConfigureSetNoForm(self):
     1006        """
     1007        On a node configuration set request a form is required.
     1008        """
     1009
     1010        xml = """
     1011        <iq type='set' to='pubsub.example.org'
     1012                       from='user@example.org'>
     1013          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
     1014            <configure node='test'/>
     1015          </pubsub>
     1016        </iq>
     1017        """
     1018        err = self.assertRaises(error.StanzaError,
     1019                                pubsub.PubSubRequest.fromElement,
     1020                                parseXml(xml))
     1021        self.assertEqual('bad-request', err.condition)
     1022        self.assertEqual(None, err.appCondition)
     1023
     1024
     1025    def test_fromElementItems(self):
     1026        """
     1027        Test parsing an items request.
     1028        """
     1029        xml = """
     1030        <iq type='get' to='pubsub.example.org'
     1031                       from='user@example.org'>
     1032          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     1033            <items node='test'/>
     1034          </pubsub>
     1035        </iq>
     1036        """
     1037
     1038        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
     1039        self.assertEqual('items', request.verb)
     1040        self.assertEqual(JID('user@example.org'), request.sender)
     1041        self.assertEqual(JID('pubsub.example.org'), request.recipient)
     1042        self.assertEqual('test', request.nodeIdentifier)
     1043        self.assertIdentical(None, request.maxItems)
     1044        self.assertEqual([], request.itemIdentifiers)
     1045
     1046
     1047    def test_fromElementRetract(self):
     1048        """
     1049        Test parsing a retract request.
     1050        """
     1051
     1052        xml = """
     1053        <iq type='set' to='pubsub.example.org'
     1054                       from='user@example.org'>
     1055          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     1056            <retract node='test'>
     1057              <item id='item1'/>
     1058              <item id='item2'/>
     1059            </retract>
     1060          </pubsub>
     1061        </iq>
     1062        """
     1063
     1064        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
     1065        self.assertEqual('retract', request.verb)
     1066        self.assertEqual(JID('user@example.org'), request.sender)
     1067        self.assertEqual(JID('pubsub.example.org'), request.recipient)
     1068        self.assertEqual('test', request.nodeIdentifier)
     1069        self.assertEqual(['item1', 'item2'], request.itemIdentifiers)
     1070
     1071
     1072    def test_fromElementPurge(self):
     1073        """
     1074        Test parsing a purge request.
     1075        """
     1076
     1077        xml = """
     1078        <iq type='set' to='pubsub.example.org'
     1079                       from='user@example.org'>
     1080          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
     1081            <purge node='test'/>
     1082          </pubsub>
     1083        </iq>
     1084        """
     1085
     1086        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
     1087        self.assertEqual('purge', request.verb)
     1088        self.assertEqual(JID('user@example.org'), request.sender)
     1089        self.assertEqual(JID('pubsub.example.org'), request.recipient)
     1090        self.assertEqual('test', request.nodeIdentifier)
     1091
     1092
     1093    def test_fromElementDelete(self):
     1094        """
     1095        Test parsing a delete request.
     1096        """
     1097
     1098        xml = """
     1099        <iq type='set' to='pubsub.example.org'
     1100                       from='user@example.org'>
     1101          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
     1102            <delete node='test'/>
     1103          </pubsub>
     1104        </iq>
     1105        """
     1106
     1107        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
     1108        self.assertEqual('delete', request.verb)
     1109        self.assertEqual(JID('user@example.org'), request.sender)
     1110        self.assertEqual(JID('pubsub.example.org'), request.recipient)
     1111        self.assertEqual('test', request.nodeIdentifier)
     1112
     1113
     1114
    4901115class PubSubServiceTest(unittest.TestCase, TestableRequestHandlerMixin):
    4911116    """
     
    5031128        """
    5041129        verify.verifyObject(iwokkel.IPubSubService, self.service)
     1130
     1131
     1132    def test_connectionMade(self):
     1133        """
     1134        Verify setup of observers in L{pubsub.connectionMade}.
     1135        """
     1136        requests = []
     1137
     1138        def handleRequest(iq):
     1139            requests.append(iq)
     1140
     1141        self.service.xmlstream = self.stub.xmlstream
     1142        self.service.handleRequest = handleRequest
     1143        self.service.connectionMade()
     1144
     1145        for namespace in (NS_PUBSUB, NS_PUBSUB_OWNER):
     1146            for stanzaType in ('get', 'set'):
     1147                iq = domish.Element((None, 'iq'))
     1148                iq['type'] = stanzaType
     1149                iq.addElement((namespace, 'pubsub'))
     1150                self.stub.xmlstream.dispatch(iq)
     1151
     1152        self.assertEqual(4, len(requests))
    5051153
    5061154
     
    5701218
    5711219
    572     def test_onPublishNoNode(self):
    573         """
    574         The root node is always a collection, publishing is a bad request.
    575         """
    576         xml = """
    577         <iq type='set' to='pubsub.example.org'
    578                        from='user@example.org'>
    579           <pubsub xmlns='http://jabber.org/protocol/pubsub'>
    580             <publish/>
    581           </pubsub>
    582         </iq>
    583         """
    584 
    585         def cb(result):
    586             self.assertEquals('bad-request', result.condition)
    587 
    588         d = self.handleRequest(xml)
    589         self.assertFailure(d, error.StanzaError)
    590         d.addCallback(cb)
    591         return d
    592 
    593 
    5941220    def test_onPublish(self):
    5951221        """
     
    6081234
    6091235        def publish(requestor, service, nodeIdentifier, items):
    610             self.assertEqual(JID('user@example.org'), requestor)
    611             self.assertEqual(JID('pubsub.example.org'), service)
    612             self.assertEqual('test', nodeIdentifier)
    613             self.assertEqual([], items)
    6141236            return defer.succeed(None)
    6151237
    6161238        self.service.publish = publish
     1239        verify.verifyObject(iwokkel.IPubSubService, self.service)
    6171240        return self.handleRequest(xml)
    6181241
    6191242
     1243    def test_onSubscribe(self):
     1244        """
     1245        A successful subscription should return the current subscription.
     1246        """
     1247
     1248        xml = """
     1249        <iq type='set' to='pubsub.example.org'
     1250                       from='user@example.org'>
     1251          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     1252            <subscribe node='test' jid='user@example.org/Home'/>
     1253          </pubsub>
     1254        </iq>
     1255        """
     1256
     1257        def subscribe(requestor, service, nodeIdentifier, subscriber):
     1258            return defer.succeed(pubsub.Subscription(nodeIdentifier,
     1259                                                     subscriber,
     1260                                                     'subscribed'))
     1261
     1262        def cb(element):
     1263            self.assertEqual('pubsub', element.name)
     1264            self.assertEqual(NS_PUBSUB, element.uri)
     1265            subscription = element.subscription
     1266            self.assertEqual(NS_PUBSUB, subscription.uri)
     1267            self.assertEqual('test', subscription['node'])
     1268            self.assertEqual('user@example.org/Home', subscription['jid'])
     1269            self.assertEqual('subscribed', subscription['subscription'])
     1270
     1271        self.service.subscribe = subscribe
     1272        verify.verifyObject(iwokkel.IPubSubService, self.service)
     1273        d = self.handleRequest(xml)
     1274        d.addCallback(cb)
     1275        return d
     1276
     1277
     1278    def test_onSubscribeEmptyNode(self):
     1279        """
     1280        A successful subscription on root node should return no node attribute.
     1281        """
     1282
     1283        xml = """
     1284        <iq type='set' to='pubsub.example.org'
     1285                       from='user@example.org'>
     1286          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     1287            <subscribe jid='user@example.org/Home'/>
     1288          </pubsub>
     1289        </iq>
     1290        """
     1291
     1292        def subscribe(requestor, service, nodeIdentifier, subscriber):
     1293            return defer.succeed(pubsub.Subscription(nodeIdentifier,
     1294                                                     subscriber,
     1295                                                     'subscribed'))
     1296
     1297        def cb(element):
     1298            self.assertFalse(element.subscription.hasAttribute('node'))
     1299
     1300        self.service.subscribe = subscribe
     1301        verify.verifyObject(iwokkel.IPubSubService, self.service)
     1302        d = self.handleRequest(xml)
     1303        d.addCallback(cb)
     1304        return d
     1305
     1306
     1307    def test_onUnsubscribe(self):
     1308        """
     1309        A successful unsubscription should return an empty response.
     1310        """
     1311
     1312        xml = """
     1313        <iq type='set' to='pubsub.example.org'
     1314                       from='user@example.org'>
     1315          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     1316            <unsubscribe node='test' jid='user@example.org/Home'/>
     1317          </pubsub>
     1318        </iq>
     1319        """
     1320
     1321        def unsubscribe(requestor, service, nodeIdentifier, subscriber):
     1322            return defer.succeed(None)
     1323
     1324        def cb(element):
     1325            self.assertIdentical(None, element)
     1326
     1327        self.service.unsubscribe = unsubscribe
     1328        verify.verifyObject(iwokkel.IPubSubService, self.service)
     1329        d = self.handleRequest(xml)
     1330        d.addCallback(cb)
     1331        return d
     1332
     1333
    6201334    def test_onOptionsGet(self):
    6211335        """
    622         Subscription options are not supported.
     1336        Getting subscription options is not supported.
    6231337        """
    6241338
     
    6271341                       from='user@example.org'>
    6281342          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
    629             <options/>
     1343            <options node='test' jid='user@example.org/Home'/>
     1344          </pubsub>
     1345        </iq>
     1346        """
     1347
     1348        def cb(result):
     1349            self.assertEquals('feature-not-implemented', result.condition)
     1350            self.assertEquals('unsupported', result.appCondition.name)
     1351            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     1352
     1353        d = self.handleRequest(xml)
     1354        self.assertFailure(d, error.StanzaError)
     1355        d.addCallback(cb)
     1356        return d
     1357
     1358
     1359    def test_onOptionsSet(self):
     1360        """
     1361        Setting subscription options is not supported.
     1362        """
     1363
     1364        xml = """
     1365        <iq type='set' to='pubsub.example.org'
     1366                       from='user@example.org'>
     1367          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     1368            <options node='test' jid='user@example.org/Home'>
     1369              <x xmlns='jabber:x:data' type='submit'>
     1370                <field var='FORM_TYPE' type='hidden'>
     1371                  <value>http://jabber.org/protocol/pubsub#subscribe_options</value>
     1372                </field>
     1373                <field var='pubsub#deliver'><value>1</value></field>
     1374              </x>
     1375            </options>
    6301376          </pubsub>
    6311377        </iq>
     
    6741420
    6751421        def subscriptions(requestor, service):
    676             self.assertEqual(JID('user@example.org'), requestor)
    677             self.assertEqual(JID('pubsub.example.org'), service)
    6781422            subscription = pubsub.Subscription('test', JID('user@example.org'),
    6791423                                               'subscribed')
     
    6811425
    6821426        self.service.subscriptions = subscriptions
     1427        verify.verifyObject(iwokkel.IPubSubService, self.service)
     1428        d = self.handleRequest(xml)
     1429        d.addCallback(cb)
     1430        return d
     1431
     1432
     1433    def test_onAffiliations(self):
     1434        """
     1435        A subscriptions request should result in
     1436        L{PubSubService.affiliations} being called and the result prepared
     1437        for the response.
     1438        """
     1439
     1440        xml = """
     1441        <iq type='get' to='pubsub.example.org'
     1442                       from='user@example.org'>
     1443          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     1444            <affiliations/>
     1445          </pubsub>
     1446        </iq>
     1447        """
     1448
     1449        def cb(element):
     1450            self.assertEqual('pubsub', element.name)
     1451            self.assertEqual(NS_PUBSUB, element.uri)
     1452            self.assertEqual(NS_PUBSUB, element.affiliations.uri)
     1453            children = list(element.affiliations.elements())
     1454            self.assertEqual(1, len(children))
     1455            affiliation = children[0]
     1456            self.assertEqual('affiliation', affiliation.name)
     1457            self.assertEqual(NS_PUBSUB, affiliation.uri)
     1458            self.assertEqual('test', affiliation['node'])
     1459            self.assertEqual('owner', affiliation['affiliation'])
     1460
     1461
     1462        def affiliations(requestor, service):
     1463            affiliation = ('test', 'owner')
     1464            return defer.succeed([affiliation])
     1465
     1466        self.service.affiliations = affiliations
     1467        verify.verifyObject(iwokkel.IPubSubService, self.service)
     1468        d = self.handleRequest(xml)
     1469        d.addCallback(cb)
     1470        return d
     1471
     1472
     1473    def test_onCreate(self):
     1474        """
     1475        Replies to create node requests don't return the created node.
     1476        """
     1477
     1478        xml = """
     1479        <iq type='set' to='pubsub.example.org'
     1480                       from='user@example.org'>
     1481          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     1482            <create node='mynode'/>
     1483          </pubsub>
     1484        </iq>
     1485        """
     1486
     1487        def create(requestor, service, nodeIdentifier):
     1488            return defer.succeed(nodeIdentifier)
     1489
     1490        def cb(element):
     1491            self.assertIdentical(None, element)
     1492
     1493        self.service.create = create
     1494        verify.verifyObject(iwokkel.IPubSubService, self.service)
     1495        d = self.handleRequest(xml)
     1496        d.addCallback(cb)
     1497        return d
     1498
     1499
     1500    def test_onCreateChanged(self):
     1501        """
     1502        Replies to create node requests return the created node if changed.
     1503        """
     1504
     1505        xml = """
     1506        <iq type='set' to='pubsub.example.org'
     1507                       from='user@example.org'>
     1508          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     1509            <create node='mynode'/>
     1510          </pubsub>
     1511        </iq>
     1512        """
     1513
     1514        def create(requestor, service, nodeIdentifier):
     1515            return defer.succeed(u'myrenamednode')
     1516
     1517        def cb(element):
     1518            self.assertEqual('pubsub', element.name)
     1519            self.assertEqual(NS_PUBSUB, element.uri)
     1520            self.assertEqual(NS_PUBSUB, element.create.uri)
     1521            self.assertEqual(u'myrenamednode',
     1522                             element.create.getAttribute('node'))
     1523
     1524        self.service.create = create
     1525        verify.verifyObject(iwokkel.IPubSubService, self.service)
     1526        d = self.handleRequest(xml)
     1527        d.addCallback(cb)
     1528        return d
     1529
     1530
     1531    def test_onCreateInstant(self):
     1532        """
     1533        Replies to create instant node requests return the created node.
     1534        """
     1535
     1536        xml = """
     1537        <iq type='set' to='pubsub.example.org'
     1538                       from='user@example.org'>
     1539          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     1540            <create/>
     1541          </pubsub>
     1542        </iq>
     1543        """
     1544
     1545        def create(requestor, service, nodeIdentifier):
     1546            return defer.succeed(u'random')
     1547
     1548        def cb(element):
     1549            self.assertEqual('pubsub', element.name)
     1550            self.assertEqual(NS_PUBSUB, element.uri)
     1551            self.assertEqual(NS_PUBSUB, element.create.uri)
     1552            self.assertEqual(u'random', element.create.getAttribute('node'))
     1553
     1554        self.service.create = create
     1555        verify.verifyObject(iwokkel.IPubSubService, self.service)
    6831556        d = self.handleRequest(xml)
    6841557        d.addCallback(cb)
     
    7121585
    7131586        def getDefaultConfiguration(requestor, service, nodeType):
    714             self.assertEqual(JID('user@example.org'), requestor)
    715             self.assertEqual(JID('pubsub.example.org'), service)
    716             self.assertEqual('leaf', nodeType)
    7171587            return defer.succeed({})
    7181588
     
    7281598        verify.verifyObject(iwokkel.IPubSubService, self.service)
    7291599        d = self.handleRequest(xml)
     1600        d.addCallback(cb)
     1601        return d
     1602
     1603
     1604    def test_onDefaultCollection(self):
     1605        """
     1606        Responses to default requests should depend on passed node type.
     1607        """
     1608
     1609        xml = """
     1610        <iq type='get' to='pubsub.example.org'
     1611                       from='user@example.org'>
     1612          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
     1613            <default>
     1614              <x xmlns='jabber:x:data' type='submit'>
     1615                <field var='FORM_TYPE' type='hidden'>
     1616                  <value>http://jabber.org/protocol/pubsub#node_config</value>
     1617                </field>
     1618                <field var='pubsub#node_type'>
     1619                  <value>collection</value>
     1620                </field>
     1621              </x>
     1622            </default>
     1623
     1624          </pubsub>
     1625        </iq>
     1626        """
     1627
     1628        def getConfigurationOptions():
     1629            return {
     1630                "pubsub#deliver_payloads":
     1631                    {"type": "boolean",
     1632                     "label": "Deliver payloads with event notifications"}
     1633                }
     1634
     1635        def getDefaultConfiguration(requestor, service, nodeType):
     1636            return defer.succeed({})
     1637
     1638        self.service.getConfigurationOptions = getConfigurationOptions
     1639        self.service.getDefaultConfiguration = getDefaultConfiguration
     1640        verify.verifyObject(iwokkel.IPubSubService, self.service)
     1641        return self.handleRequest(xml)
     1642
     1643
     1644    def test_onDefaultUnknownNodeType(self):
     1645        """
     1646        A default request should result in
     1647        L{PubSubService.getDefaultConfiguration} being called.
     1648        """
     1649
     1650        xml = """
     1651        <iq type='get' to='pubsub.example.org'
     1652                       from='user@example.org'>
     1653          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
     1654            <default>
     1655              <x xmlns='jabber:x:data' type='submit'>
     1656                <field var='FORM_TYPE' type='hidden'>
     1657                  <value>http://jabber.org/protocol/pubsub#node_config</value>
     1658                </field>
     1659                <field var='pubsub#node_type'>
     1660                  <value>unknown</value>
     1661                </field>
     1662              </x>
     1663            </default>
     1664
     1665          </pubsub>
     1666        </iq>
     1667        """
     1668
     1669        def getDefaultConfiguration(requestor, service, nodeType):
     1670            self.fail("Unexpected call to getConfiguration")
     1671
     1672        def cb(result):
     1673            self.assertEquals('not-acceptable', result.condition)
     1674
     1675        self.service.getDefaultConfiguration = getDefaultConfiguration
     1676        verify.verifyObject(iwokkel.IPubSubService, self.service)
     1677        d = self.handleRequest(xml)
     1678        self.assertFailure(d, error.StanzaError)
    7301679        d.addCallback(cb)
    7311680        return d
     
    7611710
    7621711        def getConfiguration(requestor, service, nodeIdentifier):
    763             self.assertEqual(JID('user@example.org'), requestor)
    764             self.assertEqual(JID('pubsub.example.org'), service)
    765             self.assertEqual('test', nodeIdentifier)
    766 
    7671712            return defer.succeed({'pubsub#deliver_payloads': '0',
    7681713                                  'pubsub#persist_items': '1',
    769                                   'pubsub#owner': JID('user@example.org')})
     1714                                  'pubsub#owner': JID('user@example.org'),
     1715                                  'x-myfield': ['a', 'b']})
    7701716
    7711717        def cb(element):
     
    7951741            self.assertEqual(JID('user@example.org'), field.value)
    7961742
     1743            self.assertNotIn('x-myfield', fields)
     1744
     1745
    7971746        self.service.getConfigurationOptions = getConfigurationOptions
    7981747        self.service.getConfiguration = getConfiguration
     1748        verify.verifyObject(iwokkel.IPubSubService, self.service)
    7991749        d = self.handleRequest(xml)
    8001750        d.addCallback(cb)
     
    8361786
    8371787        def setConfiguration(requestor, service, nodeIdentifier, options):
    838             self.assertEqual(JID('user@example.org'), requestor)
    839             self.assertEqual(JID('pubsub.example.org'), service)
    840             self.assertEqual('test', nodeIdentifier)
    8411788            self.assertEqual({'pubsub#deliver_payloads': False,
    8421789                              'pubsub#persist_items': True}, options)
     
    8451792        self.service.getConfigurationOptions = getConfigurationOptions
    8461793        self.service.setConfiguration = setConfiguration
     1794        verify.verifyObject(iwokkel.IPubSubService, self.service)
    8471795        return self.handleRequest(xml)
    8481796
     
    8731821
    8741822        self.service.setConfiguration = setConfiguration
     1823        verify.verifyObject(iwokkel.IPubSubService, self.service)
    8751824        return self.handleRequest(xml)
    8761825
     
    9131862        self.service.getConfigurationOptions = getConfigurationOptions
    9141863        self.service.setConfiguration = setConfiguration
     1864        verify.verifyObject(iwokkel.IPubSubService, self.service)
    9151865        return self.handleRequest(xml)
    9161866
    9171867
     1868    def test_onConfigureSetBadFormType(self):
     1869        """
     1870        On a node configuration set request unknown fields should be ignored.
     1871        """
     1872
     1873        xml = """
     1874        <iq type='set' to='pubsub.example.org'
     1875                       from='user@example.org'>
     1876          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
     1877            <configure node='test'>
     1878              <x xmlns='jabber:x:data' type='result'>
     1879                <field var='FORM_TYPE' type='hidden'>
     1880                  <value>http://jabber.org/protocol/pubsub#node_config</value>
     1881                </field>
     1882                <field var='pubsub#deliver_payloads'><value>0</value></field>
     1883                <field var='x-myfield'><value>1</value></field>
     1884              </x>
     1885            </configure>
     1886          </pubsub>
     1887        </iq>
     1888        """
     1889
     1890        def cb(result):
     1891            self.assertEquals('bad-request', result.condition)
     1892
     1893        d = self.handleRequest(xml)
     1894        self.assertFailure(d, error.StanzaError)
     1895        d.addCallback(cb)
     1896        return d
     1897
     1898
    9181899    def test_onItems(self):
    9191900        """
     
    9291910        """
    9301911
    931         def items(requestor, service, nodeIdentifier, maxItems, items):
    932             self.assertEqual(JID('user@example.org'), requestor)
    933             self.assertEqual(JID('pubsub.example.org'), service)
    934             self.assertEqual('test', nodeIdentifier)
    935             self.assertIdentical(None, maxItems)
    936             self.assertEqual([], items)
     1912        def items(requestor, service, nodeIdentifier, maxItems,
     1913                  itemIdentifiers):
    9371914            return defer.succeed([pubsub.Item('current')])
    9381915
     
    9721949
    9731950        def retract(requestor, service, nodeIdentifier, itemIdentifiers):
    974             self.assertEqual(JID('user@example.org'), requestor)
    975             self.assertEqual(JID('pubsub.example.org'), service)
    976             self.assertEqual('test', nodeIdentifier)
    977             self.assertEqual(['item1', 'item2'], itemIdentifiers)
    9781951            return defer.succeed(None)
    9791952
     
    9981971
    9991972        def purge(requestor, service, nodeIdentifier):
    1000             self.assertEqual(JID('user@example.org'), requestor)
    1001             self.assertEqual(JID('pubsub.example.org'), service)
    1002             self.assertEqual('test', nodeIdentifier)
    10031973            return defer.succeed(None)
    10041974
     
    10231993
    10241994        def delete(requestor, service, nodeIdentifier):
    1025             self.assertEqual(JID('user@example.org'), requestor)
    1026             self.assertEqual(JID('pubsub.example.org'), service)
    1027             self.assertEqual('test', nodeIdentifier)
    10281995            return defer.succeed(None)
    10291996
     
    10772044        self.assertTrue(message.event.delete.redirect.hasAttribute('uri'))
    10782045        self.assertEqual(redirectURI, message.event.delete.redirect['uri'])
     2046
     2047
     2048    def test_onSubscriptionsGet(self):
     2049        """
     2050        Getting subscription options is not supported.
     2051        """
     2052
     2053        xml = """
     2054        <iq type='get' to='pubsub.example.org'
     2055                       from='user@example.org'>
     2056          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
     2057            <subscriptions/>
     2058          </pubsub>
     2059        </iq>
     2060        """
     2061
     2062        def cb(result):
     2063            self.assertEquals('feature-not-implemented', result.condition)
     2064            self.assertEquals('unsupported', result.appCondition.name)
     2065            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     2066            self.assertEquals('manage-subscriptions',
     2067                              result.appCondition['feature'])
     2068
     2069        d = self.handleRequest(xml)
     2070        self.assertFailure(d, error.StanzaError)
     2071        d.addCallback(cb)
     2072        return d
     2073
     2074
     2075    def test_onSubscriptionsSet(self):
     2076        """
     2077        Setting subscription options is not supported.
     2078        """
     2079
     2080        xml = """
     2081        <iq type='set' to='pubsub.example.org'
     2082                       from='user@example.org'>
     2083          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
     2084            <subscriptions/>
     2085          </pubsub>
     2086        </iq>
     2087        """
     2088
     2089        def cb(result):
     2090            self.assertEquals('feature-not-implemented', result.condition)
     2091            self.assertEquals('unsupported', result.appCondition.name)
     2092            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     2093            self.assertEquals('manage-subscriptions',
     2094                              result.appCondition['feature'])
     2095
     2096        d = self.handleRequest(xml)
     2097        self.assertFailure(d, error.StanzaError)
     2098        d.addCallback(cb)
     2099        return d
     2100
     2101
     2102    def test_onAffiliationsGet(self):
     2103        """
     2104        Getting subscription options is not supported.
     2105        """
     2106
     2107        xml = """
     2108        <iq type='get' to='pubsub.example.org'
     2109                       from='user@example.org'>
     2110          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
     2111            <affiliations/>
     2112          </pubsub>
     2113        </iq>
     2114        """
     2115
     2116        def cb(result):
     2117            self.assertEquals('feature-not-implemented', result.condition)
     2118            self.assertEquals('unsupported', result.appCondition.name)
     2119            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     2120            self.assertEquals('modify-affiliations',
     2121                              result.appCondition['feature'])
     2122
     2123        d = self.handleRequest(xml)
     2124        self.assertFailure(d, error.StanzaError)
     2125        d.addCallback(cb)
     2126        return d
     2127
     2128
     2129    def test_onAffiliationsSet(self):
     2130        """
     2131        Setting subscription options is not supported.
     2132        """
     2133
     2134        xml = """
     2135        <iq type='set' to='pubsub.example.org'
     2136                       from='user@example.org'>
     2137          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
     2138            <affiliations/>
     2139          </pubsub>
     2140        </iq>
     2141        """
     2142
     2143        def cb(result):
     2144            self.assertEquals('feature-not-implemented', result.condition)
     2145            self.assertEquals('unsupported', result.appCondition.name)
     2146            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     2147            self.assertEquals('modify-affiliations',
     2148                              result.appCondition['feature'])
     2149
     2150        d = self.handleRequest(xml)
     2151        self.assertFailure(d, error.StanzaError)
     2152        d.addCallback(cb)
     2153        return d
     2154
     2155
     2156    def test_publish(self):
     2157        """
     2158        Non-overridden L{PubSubService.publish} yields unsupported error.
     2159        """
     2160
     2161        xml = """
     2162        <iq type='set' to='pubsub.example.org'
     2163                       from='user@example.org'>
     2164          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     2165            <publish node='mynode'/>
     2166          </pubsub>
     2167        </iq>
     2168        """
     2169
     2170        def cb(result):
     2171            self.assertEquals('feature-not-implemented', result.condition)
     2172            self.assertEquals('unsupported', result.appCondition.name)
     2173            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     2174            self.assertEquals('publish', result.appCondition['feature'])
     2175
     2176        d = self.handleRequest(xml)
     2177        self.assertFailure(d, error.StanzaError)
     2178        d.addCallback(cb)
     2179        return d
     2180
     2181
     2182    def test_subscribe(self):
     2183        """
     2184        Non-overridden L{PubSubService.subscribe} yields unsupported error.
     2185        """
     2186
     2187        xml = """
     2188        <iq type='set' to='pubsub.example.org'
     2189                       from='user@example.org'>
     2190          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     2191            <subscribe node='test' jid='user@example.org/Home'/>
     2192          </pubsub>
     2193        </iq>
     2194        """
     2195
     2196        def cb(result):
     2197            self.assertEquals('feature-not-implemented', result.condition)
     2198            self.assertEquals('unsupported', result.appCondition.name)
     2199            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     2200            self.assertEquals('subscribe', result.appCondition['feature'])
     2201
     2202        d = self.handleRequest(xml)
     2203        self.assertFailure(d, error.StanzaError)
     2204        d.addCallback(cb)
     2205        return d
     2206
     2207
     2208    def test_unsubscribe(self):
     2209        """
     2210        Non-overridden L{PubSubService.unsubscribe} yields unsupported error.
     2211        """
     2212
     2213        xml = """
     2214        <iq type='set' to='pubsub.example.org'
     2215                       from='user@example.org'>
     2216          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     2217            <unsubscribe node='test' jid='user@example.org/Home'/>
     2218          </pubsub>
     2219        </iq>
     2220        """
     2221
     2222        def cb(result):
     2223            self.assertEquals('feature-not-implemented', result.condition)
     2224            self.assertEquals('unsupported', result.appCondition.name)
     2225            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     2226            self.assertEquals('subscribe', result.appCondition['feature'])
     2227
     2228        d = self.handleRequest(xml)
     2229        self.assertFailure(d, error.StanzaError)
     2230        d.addCallback(cb)
     2231        return d
     2232
     2233
     2234    def test_subscriptions(self):
     2235        """
     2236        Non-overridden L{PubSubService.subscriptions} yields unsupported error.
     2237        """
     2238
     2239        xml = """
     2240        <iq type='get' to='pubsub.example.org'
     2241                       from='user@example.org'>
     2242          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     2243            <subscriptions/>
     2244          </pubsub>
     2245        </iq>
     2246        """
     2247
     2248        def cb(result):
     2249            self.assertEquals('feature-not-implemented', result.condition)
     2250            self.assertEquals('unsupported', result.appCondition.name)
     2251            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     2252            self.assertEquals('retrieve-subscriptions',
     2253                              result.appCondition['feature'])
     2254
     2255        d = self.handleRequest(xml)
     2256        self.assertFailure(d, error.StanzaError)
     2257        d.addCallback(cb)
     2258        return d
     2259
     2260
     2261    def test_affiliations(self):
     2262        """
     2263        Non-overridden L{PubSubService.affiliations} yields unsupported error.
     2264        """
     2265
     2266        xml = """
     2267        <iq type='get' to='pubsub.example.org'
     2268                       from='user@example.org'>
     2269          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     2270            <affiliations/>
     2271          </pubsub>
     2272        </iq>
     2273        """
     2274
     2275        def cb(result):
     2276            self.assertEquals('feature-not-implemented', result.condition)
     2277            self.assertEquals('unsupported', result.appCondition.name)
     2278            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     2279            self.assertEquals('retrieve-affiliations',
     2280                              result.appCondition['feature'])
     2281
     2282        d = self.handleRequest(xml)
     2283        self.assertFailure(d, error.StanzaError)
     2284        d.addCallback(cb)
     2285        return d
     2286
     2287
     2288    def test_create(self):
     2289        """
     2290        Non-overridden L{PubSubService.create} yields unsupported error.
     2291        """
     2292
     2293        xml = """
     2294        <iq type='set' to='pubsub.example.org'
     2295                       from='user@example.org'>
     2296          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     2297            <create node='mynode'/>
     2298          </pubsub>
     2299        </iq>
     2300        """
     2301
     2302        def cb(result):
     2303            self.assertEquals('feature-not-implemented', result.condition)
     2304            self.assertEquals('unsupported', result.appCondition.name)
     2305            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     2306            self.assertEquals('create-nodes', result.appCondition['feature'])
     2307
     2308        d = self.handleRequest(xml)
     2309        self.assertFailure(d, error.StanzaError)
     2310        d.addCallback(cb)
     2311        return d
     2312
     2313
     2314    def test_getDefaultConfiguration(self):
     2315        """
     2316        Non-overridden L{PubSubService.getDefaultConfiguration} yields
     2317        unsupported error.
     2318        """
     2319
     2320        xml = """
     2321        <iq type='get' to='pubsub.example.org'
     2322                       from='user@example.org'>
     2323          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
     2324            <default/>
     2325          </pubsub>
     2326        </iq>
     2327        """
     2328
     2329        def cb(result):
     2330            self.assertEquals('feature-not-implemented', result.condition)
     2331            self.assertEquals('unsupported', result.appCondition.name)
     2332            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     2333            self.assertEquals('retrieve-default', result.appCondition['feature'])
     2334
     2335        d = self.handleRequest(xml)
     2336        self.assertFailure(d, error.StanzaError)
     2337        d.addCallback(cb)
     2338        return d
     2339
     2340
     2341    def test_getConfiguration(self):
     2342        """
     2343        Non-overridden L{PubSubService.getConfiguration} yields unsupported
     2344        error.
     2345        """
     2346
     2347        xml = """
     2348        <iq type='get' to='pubsub.example.org'
     2349                       from='user@example.org'>
     2350          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
     2351            <configure/>
     2352          </pubsub>
     2353        </iq>
     2354        """
     2355
     2356        def cb(result):
     2357            self.assertEquals('feature-not-implemented', result.condition)
     2358            self.assertEquals('unsupported', result.appCondition.name)
     2359            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     2360            self.assertEquals('config-node', result.appCondition['feature'])
     2361
     2362        d = self.handleRequest(xml)
     2363        self.assertFailure(d, error.StanzaError)
     2364        d.addCallback(cb)
     2365        return d
     2366
     2367
     2368    def test_setConfiguration(self):
     2369        """
     2370        Non-overridden L{PubSubService.setConfiguration} yields unsupported
     2371        error.
     2372        """
     2373
     2374        xml = """
     2375        <iq type='set' to='pubsub.example.org'
     2376                       from='user@example.org'>
     2377          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
     2378            <configure node='test'>
     2379              <x xmlns='jabber:x:data' type='submit'>
     2380                <field var='FORM_TYPE' type='hidden'>
     2381                  <value>http://jabber.org/protocol/pubsub#node_config</value>
     2382                </field>
     2383                <field var='pubsub#deliver_payloads'><value>0</value></field>
     2384                <field var='pubsub#persist_items'><value>1</value></field>
     2385              </x>
     2386            </configure>
     2387          </pubsub>
     2388        </iq>
     2389        """
     2390
     2391        def cb(result):
     2392            self.assertEquals('feature-not-implemented', result.condition)
     2393            self.assertEquals('unsupported', result.appCondition.name)
     2394            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     2395            self.assertEquals('config-node', result.appCondition['feature'])
     2396
     2397        d = self.handleRequest(xml)
     2398        self.assertFailure(d, error.StanzaError)
     2399        d.addCallback(cb)
     2400        return d
     2401
     2402
     2403    def test_items(self):
     2404        """
     2405        Non-overridden L{PubSubService.items} yields unsupported error.
     2406        """
     2407        xml = """
     2408        <iq type='get' to='pubsub.example.org'
     2409                       from='user@example.org'>
     2410          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     2411            <items node='test'/>
     2412          </pubsub>
     2413        </iq>
     2414        """
     2415
     2416        def cb(result):
     2417            self.assertEquals('feature-not-implemented', result.condition)
     2418            self.assertEquals('unsupported', result.appCondition.name)
     2419            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     2420            self.assertEquals('retrieve-items', result.appCondition['feature'])
     2421
     2422        d = self.handleRequest(xml)
     2423        self.assertFailure(d, error.StanzaError)
     2424        d.addCallback(cb)
     2425        return d
     2426
     2427
     2428    def test_retract(self):
     2429        """
     2430        Non-overridden L{PubSubService.retract} yields unsupported error.
     2431        """
     2432        xml = """
     2433        <iq type='set' to='pubsub.example.org'
     2434                       from='user@example.org'>
     2435          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     2436            <retract node='test'>
     2437              <item id='item1'/>
     2438              <item id='item2'/>
     2439            </retract>
     2440          </pubsub>
     2441        </iq>
     2442        """
     2443
     2444        def cb(result):
     2445            self.assertEquals('feature-not-implemented', result.condition)
     2446            self.assertEquals('unsupported', result.appCondition.name)
     2447            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     2448            self.assertEquals('retract-items', result.appCondition['feature'])
     2449
     2450        d = self.handleRequest(xml)
     2451        self.assertFailure(d, error.StanzaError)
     2452        d.addCallback(cb)
     2453        return d
     2454
     2455
     2456    def test_purge(self):
     2457        """
     2458        Non-overridden L{PubSubService.purge} yields unsupported error.
     2459        """
     2460        xml = """
     2461        <iq type='set' to='pubsub.example.org'
     2462                       from='user@example.org'>
     2463          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
     2464            <purge node='test'/>
     2465          </pubsub>
     2466        </iq>
     2467        """
     2468
     2469        def cb(result):
     2470            self.assertEquals('feature-not-implemented', result.condition)
     2471            self.assertEquals('unsupported', result.appCondition.name)
     2472            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     2473            self.assertEquals('purge-nodes', result.appCondition['feature'])
     2474
     2475        d = self.handleRequest(xml)
     2476        self.assertFailure(d, error.StanzaError)
     2477        d.addCallback(cb)
     2478        return d
     2479
     2480
     2481    def test_delete(self):
     2482        """
     2483        Non-overridden L{PubSubService.delete} yields unsupported error.
     2484        """
     2485        xml = """
     2486        <iq type='set' to='pubsub.example.org'
     2487                       from='user@example.org'>
     2488          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
     2489            <delete node='test'/>
     2490          </pubsub>
     2491        </iq>
     2492        """
     2493
     2494        def cb(result):
     2495            self.assertEquals('feature-not-implemented', result.condition)
     2496            self.assertEquals('unsupported', result.appCondition.name)
     2497            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     2498            self.assertEquals('delete-nodes', result.appCondition['feature'])
     2499
     2500        d = self.handleRequest(xml)
     2501        self.assertFailure(d, error.StanzaError)
     2502        d.addCallback(cb)
     2503        return d
Note: See TracChangeset for help on using the changeset viewer.