source: ralphm-patches/pubsub_request.patch @ 5:231bc3e8b810

Last change on this file since 5:231bc3e8b810 was 5:231bc3e8b810, checked in by Ralph Meijer <ralphm@…>, 11 years ago

A gazillion changes.

File size: 106.8 KB
  • wokkel/generic.py

    Add PubSubRequest that parses requests and replaces separate parameters.
    
    The publish-subscribe protocol has a large number of optional features. Adding
    support for a feature often means more information needs to be passed, and
    required changing the signature of the methods that need to be overridden by
    adaptors.
    
    This change adds the new PubSubRequest class that features a fromElement
    method that creates a new instance, parses a request and then fills the
    instance attributes where appropriate. This instance is then the only argument
    that is passed to the corresponding handler methods.
    
    diff -r bcbcbc4aa868 wokkel/generic.py
    a b  
    1010from zope.interface import implements
    1111
    1212from twisted.internet import defer
    13 from twisted.words.protocols.jabber import error
     13from twisted.words.protocols.jabber import error, jid
    1414from twisted.words.protocols.jabber.xmlstream import toResponse
    1515from twisted.words.xish import domish, utility
    1616
     
    155155        self.sink = utility.EventDispatcher()
    156156        self.source.send = lambda obj: self.sink.dispatch(obj)
    157157        self.sink.send = lambda obj: self.source.dispatch(obj)
     158
     159
     160
     161class Stanza(object):
     162    """
     163    Abstract representation of a stanza.
     164
     165    @ivar sender: The sending entity.
     166    @type sender: L{jid.JID}
     167    @ivar recipient: The receiving entity.
     168    @type recipient: L{jid.JID}
     169    """
     170
     171    sender = None
     172    recipient = None
     173    stanzaType = None
     174
     175    @classmethod
     176    def fromElement(Class, element):
     177        stanza = Class()
     178        stanza.parseElement(element)
     179        return stanza
     180
     181
     182    def parseElement(self, element):
     183        self.sender = jid.internJID(element['from'])
     184        if element.hasAttribute('from'):
     185            self.sender = jid.internJID(element['from'])
     186        if element.hasAttribute('to'):
     187            self.recipient = jid.internJID(element['to'])
     188        self.stanzaType = element.getAttribute('type')
  • wokkel/iwokkel.py

    diff -r bcbcbc4aa868 wokkel/iwokkel.py
    a b  
    278278                             C{list} of L{domish.Element})
    279279        """
    280280
     281
    281282    def notifyDelete(service, nodeIdentifier, subscribers,
    282283                     redirectURI=None):
    283284        """
     
    295296        @type redirectURI: C{str}
    296297        """
    297298
    298     def publish(requestor, service, nodeIdentifier, items):
     299
     300    def publish(request):
    299301        """
    300302        Called when a publish request has been received.
    301303
    302         @param requestor: The entity the request originated from.
    303         @type requestor: L{jid.JID}
    304         @param service: The entity the request was addressed to.
    305         @type service: L{jid.JID}
    306         @param nodeIdentifier: The identifier of the node to publish to.
    307         @type nodeIdentifier: C{unicode}
    308         @param items: The items to be published as L{domish} elements.
    309         @type items: C{list} of C{domish.Element}
     304        @param request: The publish-subscribe request.
     305        @type request: L{wokkel.pubsub.PubSubRequest}
    310306        @return: deferred that fires on success.
    311307        @rtype: L{defer.Deferred}
    312308        """
    313309
    314     def subscribe(requestor, service, nodeIdentifier, subscriber):
     310
     311    def subscribe(request):
    315312        """
    316313        Called when a subscribe request has been received.
    317314
    318         @param requestor: The entity the request originated from.
    319         @type requestor: L{jid.JID}
    320         @param service: The entity the request was addressed to.
    321         @type service: L{jid.JID}
    322         @param nodeIdentifier: The identifier of the node to subscribe to.
    323         @type nodeIdentifier: C{unicode}
    324         @param subscriber: The entity to be subscribed.
    325         @type subscriber: L{jid.JID}
     315        @param request: The publish-subscribe request.
     316        @type request: L{wokkel.pubsub.PubSubRequest}
    326317        @return: A deferred that fires with a
    327318                 L{Subscription<wokkel.pubsub.Subscription>}.
    328319        @rtype: L{defer.Deferred}
    329320        """
    330321
    331     def unsubscribe(requestor, service, nodeIdentifier, subscriber):
     322
     323    def unsubscribe(request):
    332324        """
    333325        Called when a subscribe request has been received.
    334326
    335         @param requestor: The entity the request originated from.
    336         @type requestor: L{jid.JID}
    337         @param service: The entity the request was addressed to.
    338         @type service: L{jid.JID}
    339         @param nodeIdentifier: The identifier of the node to unsubscribe from.
    340         @type nodeIdentifier: C{unicode}
    341         @param subscriber: The entity to be unsubscribed.
    342         @type subscriber: L{jid.JID}
     327        @param request: The publish-subscribe request.
     328        @type request: L{wokkel.pubsub.PubSubRequest}
    343329        @return: A deferred that fires with C{None} when unsubscription has
    344330                 succeeded.
    345331        @rtype: L{defer.Deferred}
    346332        """
    347333
    348     def subscriptions(requestor, service):
     334
     335    def subscriptions(request):
    349336        """
    350337        Called when a subscriptions retrieval request has been received.
    351338
    352         @param requestor: The entity the request originated from.
    353         @type requestor: L{jid.JID}
    354         @param service: The entity the request was addressed to.
    355         @type service: L{jid.JID}
     339        @param request: The publish-subscribe request.
     340        @type request: L{wokkel.pubsub.PubSubRequest}
    356341        @return: A deferred that fires with a C{list} of subscriptions as
    357342                 L{Subscription<wokkel.pubsub.Subscription>}.
    358343        @rtype: L{defer.Deferred}
    359344        """
    360345
    361     def affiliations(requestor, service):
     346
     347    def affiliations(request):
    362348        """
    363349        Called when a affiliations retrieval request has been received.
    364350
    365         @param requestor: The entity the request originated from.
    366         @type requestor: L{jid.JID}
    367         @param service: The entity the request was addressed to.
    368         @type service: L{jid.JID}
     351        @param request: The publish-subscribe request.
     352        @type request: L{wokkel.pubsub.PubSubRequest}
    369353        @return: A deferred that fires with a C{list} of affiliations as
    370354                 C{tuple}s of (node identifier as C{unicode}, affiliation state
    371355                 as C{str}). The affiliation can be C{'owner'}, C{'publisher'},
     
    373357        @rtype: L{defer.Deferred}
    374358        """
    375359
    376     def create(requestor, service, nodeIdentifier):
     360
     361    def create(request):
    377362        """
    378363        Called when a node creation request has been received.
    379364
    380         @param requestor: The entity the request originated from.
    381         @type requestor: L{jid.JID}
    382         @param service: The entity the request was addressed to.
    383         @type service: L{jid.JID}
    384         @param nodeIdentifier: The suggestion for the identifier of the node to
    385                                be created. If the request did not include a
    386                                suggestion for the node identifier, the value
    387                                is C{None}.
    388         @type nodeIdentifier: C{unicode} or C{NoneType}
     365        @param request: The publish-subscribe request.
     366        @type request: L{wokkel.pubsub.PubSubRequest}
    389367        @return: A deferred that fires with a C{unicode} that represents
    390368                 the identifier of the new node.
    391369        @rtype: L{defer.Deferred}
    392370        """
    393371
     372
    394373    def getConfigurationOptions():
    395374        """
    396375        Retrieve all known node configuration options.
     
    426405        @rtype: C{dict}.
    427406        """
    428407
    429     def getDefaultConfiguration(requestor, service, nodeType):
     408
     409    def getDefaultConfiguration(request):
    430410        """
    431411        Called when a default node configuration request has been received.
    432412
    433         @param requestor: The entity the request originated from.
    434         @type requestor: L{jid.JID}
    435         @param service: The entity the request was addressed to.
    436         @type service: L{jid.JID}
    437         @param nodeType: The type of node for which the configuration is
    438                          retrieved, C{'leaf'} or C{'collection'}.
    439         @type nodeType: C{str}
     413        @param request: The publish-subscribe request.
     414        @type request: L{wokkel.pubsub.PubSubRequest}
    440415        @return: A deferred that fires with a C{dict} representing the default
    441416                 node configuration. Keys are C{str}s that represent the
    442417                 field name. Values can be of types C{unicode}, C{int} or
     
    444419        @rtype: L{defer.Deferred}
    445420        """
    446421
    447     def getConfiguration(requestor, service, nodeIdentifier):
     422
     423    def getConfiguration(request):
    448424        """
    449425        Called when a node configuration retrieval request has been received.
    450426
    451         @param requestor: The entity the request originated from.
    452         @type requestor: L{jid.JID}
    453         @param service: The entity the request was addressed to.
    454         @type service: L{jid.JID}
    455         @param nodeIdentifier: The identifier of the node to retrieve the
    456                                configuration from.
    457         @type nodeIdentifier: C{unicode}
     427        @param request: The publish-subscribe request.
     428        @type request: L{wokkel.pubsub.PubSubRequest}
    458429        @return: A deferred that fires with a C{dict} representing the node
    459430                 configuration. Keys are C{str}s that represent the field name.
    460431                 Values can be of types C{unicode}, C{int} or C{bool}.
    461432        @rtype: L{defer.Deferred}
    462433        """
    463434
    464     def setConfiguration(requestor, service, nodeIdentifier, options):
     435
     436    def setConfiguration(request):
    465437        """
    466438        Called when a node configuration change request has been received.
    467439
    468         @param requestor: The entity the request originated from.
    469         @type requestor: L{jid.JID}
    470         @param service: The entity the request was addressed to.
    471         @type service: L{jid.JID}
    472         @param nodeIdentifier: The identifier of the node to change the
    473                                configuration of.
    474         @type nodeIdentifier: C{unicode}
     440        @param request: The publish-subscribe request.
     441        @type request: L{wokkel.pubsub.PubSubRequest}
    475442        @return: A deferred that fires with C{None} when the node's
    476443                 configuration has been changed.
    477444        @rtype: L{defer.Deferred}
    478445        """
    479446
    480     def items(requestor, service, nodeIdentifier, maxItems, itemIdentifiers):
     447
     448    def items(request):
    481449        """
    482450        Called when a items retrieval request has been received.
    483451
    484         @param requestor: The entity the request originated from.
    485         @type requestor: L{jid.JID}
    486         @param service: The entity the request was addressed to.
    487         @type service: L{jid.JID}
    488         @param nodeIdentifier: The identifier of the node to retrieve items
    489                                from.
    490         @type nodeIdentifier: C{unicode}
     452        @param request: The publish-subscribe request.
     453        @type request: L{wokkel.pubsub.PubSubRequest}
     454        @return: A deferred that fires with a C{list} of L{pubsub.Item}.
     455        @rtype: L{defer.Deferred}
    491456        """
    492457
    493     def retract(requestor, service, nodeIdentifier, itemIdentifiers):
     458
     459    def retract(request):
    494460        """
    495461        Called when a item retraction request has been received.
    496462
    497         @param requestor: The entity the request originated from.
    498         @type requestor: L{jid.JID}
    499         @param service: The entity the request was addressed to.
    500         @type service: L{jid.JID}
    501         @param nodeIdentifier: The identifier of the node to retract items
    502                                from.
    503         @type nodeIdentifier: C{unicode}
     463        @param request: The publish-subscribe request.
     464        @type request: L{wokkel.pubsub.PubSubRequest}
     465        @return: A deferred that fires with C{None} when the given items have
     466                 been retracted.
     467        @rtype: L{defer.Deferred}
    504468        """
    505469
    506     def purge(requestor, service, nodeIdentifier):
     470
     471    def purge(request):
    507472        """
    508473        Called when a node purge request has been received.
    509474
    510         @param requestor: The entity the request originated from.
    511         @type requestor: L{jid.JID}
    512         @param service: The entity the request was addressed to.
    513         @type service: L{jid.JID}
    514         @param nodeIdentifier: The identifier of the node to be purged.
    515         @type nodeIdentifier: C{unicode}
     475        @param request: The publish-subscribe request.
     476        @type request: L{wokkel.pubsub.PubSubRequest}
     477        @return: A deferred that fires with C{None} when the node has been
     478                 purged.
     479        @rtype: L{defer.Deferred}
    516480        """
    517481
    518     def delete(requestor, service, nodeIdentifier):
     482
     483    def delete(request):
    519484        """
    520485        Called when a node deletion request has been received.
    521486
    522         @param requestor: The entity the request originated from.
    523         @type requestor: L{jid.JID}
    524         @param service: The entity the request was addressed to.
    525         @type service: L{jid.JID}
    526         @param nodeIdentifier: The identifier of the node to be delete.
    527         @type nodeIdentifier: C{unicode}
     487        @param request: The publish-subscribe request.
     488        @type request: L{wokkel.pubsub.PubSubRequest}
     489        @return: A deferred that fires with C{None} when the node has been
     490                 deleted.
     491        @rtype: L{defer.Deferred}
    528492        """
  • wokkel/pubsub.py

    diff -r bcbcbc4aa868 wokkel/pubsub.py
    a b  
    1616from twisted.words.protocols.jabber import jid, error, xmlstream
    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
    2222
     
    3131NS_PUBSUB_OWNER = NS_PUBSUB + "#owner"
    3232NS_PUBSUB_NODE_CONFIG = NS_PUBSUB + "#node_config"
    3333NS_PUBSUB_META_DATA = NS_PUBSUB + "#meta-data"
     34NS_PUBSUB_SUBSCRIBE_OPTIONS = NS_PUBSUB + "#subscribe_options"
    3435
    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
     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):
    7342    """
     
    9867
    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
    10984
     
    167142
    168143
    169144
    170 class _PubSubRequest(xmlstream.IQ):
     145class PubSubRequest(generic.Stanza):
    171146    """
    172     Publish subscribe request.
     147    A publish-subscribe request.
    173148
    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}.
     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}
    183180    """
    184181
    185     def __init__(self, xs, verb, namespace=NS_PUBSUB, method='set'):
    186         xmlstream.IQ.__init__(self, xs, method)
    187         self.addElement((namespace, 'pubsub'))
     182    verb = None
    188183
    189         self.command = self.pubsub.addElement(verb)
     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
    190194
     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    }
    191217
    192     def send(self, to):
     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):
    193250        """
    194         Send out request.
     251        Find a Data Form.
    195252
    196         Extends L{xmlstream.IQ.send} by requiring the C{to} parameter to be
    197         a L{JID} instance.
     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
    198258
    199         @param to: Entity to send the request to.
    200         @type to: L{JID}
     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):
    201273        """
    202         destination = to.full()
    203         return xmlstream.IQ.send(self, destination)
     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
    206503
     
    336633        @param nodeIdentifier: Optional suggestion for the id of the node.
    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):
    346641            try:
     
    350645                new_node = nodeIdentifier
    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
    356653    def deleteNode(self, service, nodeIdentifier):
     
    362659        @param nodeIdentifier: The identifier of the node.
    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
    370668    def subscribe(self, service, nodeIdentifier, subscriber):
     
    379677                           will get notifications of new published items.
    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):
    388686            subscription = iq.pubsub.subscription["subscription"]
     
    397695                # yielded a stanza error.
    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
    403703    def unsubscribe(self, service, nodeIdentifier, subscriber):
     
    411711        @param subscriber: The entity to unsubscribe from the node.
    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
    421721    def publish(self, service, nodeIdentifier, items=None):
     
    429729        @param items: Optional list of L{Item}s to publish.
    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
    441739    def items(self, service, nodeIdentifier, maxItems=None):
     
    449747        @param maxItems: Optional limit on the number of retrieved items.
    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):
    459757            items = []
     
    462760                    items.append(element)
    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
    468768
     
    497797    implements(IPubSubService)
    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
    523803
     
    530810
    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
    539816    def getDiscoInfo(self, requestor, target, nodeIdentifier):
     
    585862        return d
    586863
    587864
    588     def _findForm(self, element, formNamespace):
    589         if not element:
    590             return None
     865    def _onPubSubRequest(self, iq):
     866        request = PubSubRequest.fromElement(iq)
     867        handler = getattr(self, '_on_%s' % request.verb)
     868        return handler(request)
    591869
    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
    598870
    599             if form.formNamespace != NS_PUBSUB_NODE_CONFIG:
    600                 continue
     871    def _on_publish(self, request):
     872        return self.publish(request)
    601873
    602         return form
    603874
    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')
     875    def _on_subscribe(self, request):
    674876
    675877        def toResponse(result):
    676878            response = domish.Element((NS_PUBSUB, "pubsub"))
     
    681883            subscription["subscription"] = result.state
    682884            return response
    683885
    684         d = self.subscribe(requestor, service, nodeIdentifier, subscriber)
     886        d = self.subscribe(request)
    685887        d.addCallback(toResponse)
    686888        return d
    687889
    688890
    689     def _onUnsubscribe(self, iq):
    690         requestor, service, nodeIdentifier, subscriber = self._getParameters(
    691                 iq, 'unsubscribe', 'nodeOrEmpty', 'jid')
     891    def _on_unsubscribe(self, request):
     892        return self.unsubscribe(request)
    692893
    693         return self.unsubscribe(requestor, service, nodeIdentifier, subscriber)
    694894
    695 
    696     def _onOptionsGet(self, iq):
     895    def _on_optionsGet(self, request):
    697896        raise Unsupported('subscription-options')
    698897
    699898
    700     def _onOptionsSet(self, iq):
     899    def _on_optionsSet(self, request):
    701900        raise Unsupported('subscription-options')
    702901
    703902
    704     def _onSubscriptions(self, iq):
    705         requestor, service = self._getParameters(iq)
     903    def _on_subscriptions(self, request):
    706904
    707905        def toResponse(result):
    708906            response = domish.Element((NS_PUBSUB, 'pubsub'))
     
    714912                item['subscription'] = subscription.state
    715913            return response
    716914
    717         d = self.subscriptions(requestor, service)
     915        d = self.subscriptions(request)
    718916        d.addCallback(toResponse)
    719917        return d
    720918
    721919
    722     def _onAffiliations(self, iq):
    723         requestor, service = self._getParameters(iq)
     920    def _on_affiliations(self, request):
    724921
    725922        def toResponse(result):
    726923            response = domish.Element((NS_PUBSUB, 'pubsub'))
     
    733930
    734931            return response
    735932
    736         d = self.affiliations(requestor, service)
     933        d = self.affiliations(request)
    737934        d.addCallback(toResponse)
    738935        return d
    739936
    740937
    741     def _onCreate(self, iq):
    742         requestor, service = self._getParameters(iq)
    743         nodeIdentifier = iq.pubsub.create.getAttribute("node")
     938    def _on_create(self, request):
    744939
    745940        def toResponse(result):
    746             if not nodeIdentifier or nodeIdentifier != result:
     941            if not request.nodeIdentifier or request.nodeIdentifier != result:
    747942                response = domish.Element((NS_PUBSUB, 'pubsub'))
    748943                create = response.addElement('create')
    749944                create['node'] = result
     
    751946            else:
    752947                return None
    753948
    754         d = self.create(requestor, service, nodeIdentifier)
     949        d = self.create(request)
    755950        d.addCallback(toResponse)
    756951        return d
    757952
     
    771966            fields.append(data_form.Field.fromDict(option))
    772967        return fields
    773968
     969
    774970    def _formFromConfiguration(self, values):
    775971        options = self.getConfigurationOptions()
    776972        fields = self._makeFields(options, values)
     
    780976
    781977        return form
    782978
     979
    783980    def _checkConfiguration(self, values):
    784981        options = self.getConfigurationOptions()
    785982        processedValues = {}
     
    8051002        return processedValues
    8061003
    8071004
    808     def _onDefault(self, iq):
    809         requestor, service = self._getParameters(iq)
     1005    def _on_default(self, request):
    8101006
    8111007        def toResponse(options):
    8121008            response = domish.Element((NS_PUBSUB_OWNER, "pubsub"))
     
    8141010            default.addChild(self._formFromConfiguration(options).toElement())
    8151011            return response
    8161012
    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'):
     1013        if request.nodeType not in ('leaf', 'collection'):
    8221014            return defer.fail(error.StanzaError('not-acceptable'))
    8231015
    824         d = self.getDefaultConfiguration(requestor, service, nodeType)
     1016        d = self.getDefaultConfiguration(request)
    8251017        d.addCallback(toResponse)
    8261018        return d
    8271019
    8281020
    829     def _onConfigureGet(self, iq):
    830         requestor, service, nodeIdentifier = self._getParameters(
    831                 iq, 'configure', 'nodeOrEmpty')
    832 
     1021    def _on_configureGet(self, request):
    8331022        def toResponse(options):
    8341023            response = domish.Element((NS_PUBSUB_OWNER, "pubsub"))
    8351024            configure = response.addElement("configure")
    836             configure.addChild(self._formFromConfiguration(options).toElement())
     1025            form = self._formFromConfiguration(options)
     1026            configure.addChild(form.toElement())
    8371027
    838             if nodeIdentifier:
    839                 configure["node"] = nodeIdentifier
     1028            if request.nodeIdentifier:
     1029                configure["node"] = request.nodeIdentifier
    8401030
    8411031            return response
    8421032
    843         d = self.getConfiguration(requestor, service, nodeIdentifier)
     1033        d = self.getConfiguration(request)
    8441034        d.addCallback(toResponse)
    8451035        return d
    8461036
    8471037
    848     def _onConfigureSet(self, iq):
    849         requestor, service, nodeIdentifier = self._getParameters(
    850                 iq, 'configure', 'nodeOrEmpty')
     1038    def _on_configureSet(self, request):
     1039        if request.options:
     1040            request.options = self._checkConfiguration(request.options)
     1041            return self.setConfiguration(request)
     1042        else:
     1043            return None
    8511044
    852         # Search configuration form with correct FORM_TYPE and process it
    8531045
    854         form = self._findForm(iq.pubsub.configure, NS_PUBSUB_NODE_CONFIG)
    8551046
    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()
     1047    def _on_items(self, request):
    8791048
    8801049        def toResponse(result):
    8811050            response = domish.Element((NS_PUBSUB, 'pubsub'))
    8821051            items = response.addElement('items')
    883             if nodeIdentifier:
    884                 items["node"] = nodeIdentifier
     1052            items["node"] = request.nodeIdentifier
    8851053
    8861054            for item in result:
    8871055                items.addChild(item)
    8881056
    8891057            return response
    8901058
    891         d = self.items(requestor, service, nodeIdentifier, maxItems,
    892                        itemIdentifiers)
     1059        d = self.items(request)
    8931060        d.addCallback(toResponse)
    8941061        return d
    8951062
    8961063
    897     def _onRetract(self, iq):
    898         requestor, service, nodeIdentifier = self._getParameters(
    899                 iq, 'retract', 'node')
     1064    def _on_retract(self, request):
     1065        return self.retract(request)
    9001066
    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()
    9081067
    909         return self.retract(requestor, service, nodeIdentifier,
    910                             itemIdentifiers)
     1068    def _on_purge(self, request):
     1069        return self.purge(request)
    9111070
    9121071
    913     def _onPurge(self, iq):
    914         requestor, service, nodeIdentifier = self._getParameters(
    915                 iq, 'purge', 'node')
    916         return self.purge(requestor, service, nodeIdentifier)
     1072    def _on_delete(self, request):
     1073        return self.delete(request)
    9171074
    9181075
    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):
     1076    def _on_affiliationsGet(self, iq):
    9261077        raise Unsupported('modify-affiliations')
    9271078
    9281079
    929     def _onAffiliationsSet(self, iq):
     1080    def _on_affiliationsSet(self, iq):
    9301081        raise Unsupported('modify-affiliations')
    9311082
    9321083
    933     def _onSubscriptionsGet(self, iq):
     1084    def _on_subscriptionsGet(self, iq):
    9341085        raise Unsupported('manage-subscriptions')
    9351086
    9361087
    937     def _onSubscriptionsSet(self, iq):
     1088    def _on_subscriptionsSet(self, iq):
    9381089        raise Unsupported('manage-subscriptions')
    9391090
    9401091    # public methods
     
    9901141        return []
    9911142
    9921143
    993     def publish(self, requestor, service, nodeIdentifier, items):
     1144    def publish(self, request):
    9941145        raise Unsupported('publish')
    9951146
    9961147
    997     def subscribe(self, requestor, service, nodeIdentifier, subscriber):
     1148    def subscribe(self, request):
    9981149        raise Unsupported('subscribe')
    9991150
    10001151
    1001     def unsubscribe(self, requestor, service, nodeIdentifier, subscriber):
     1152    def unsubscribe(self, request):
    10021153        raise Unsupported('subscribe')
    10031154
    10041155
    1005     def subscriptions(self, requestor, service):
     1156    def subscriptions(self, request):
    10061157        raise Unsupported('retrieve-subscriptions')
    10071158
    10081159
    1009     def affiliations(self, requestor, service):
     1160    def affiliations(self, request):
    10101161        raise Unsupported('retrieve-affiliations')
    10111162
    10121163
    1013     def create(self, requestor, service, nodeIdentifier):
     1164    def create(self, request):
    10141165        raise Unsupported('create-nodes')
    10151166
    10161167
     
    10181169        return {}
    10191170
    10201171
    1021     def getDefaultConfiguration(self, requestor, service, nodeType):
     1172    def getDefaultConfiguration(self, request):
    10221173        raise Unsupported('retrieve-default')
    10231174
    10241175
    1025     def getConfiguration(self, requestor, service, nodeIdentifier):
     1176    def getConfiguration(self, request):
    10261177        raise Unsupported('config-node')
    10271178
    10281179
    1029     def setConfiguration(self, requestor, service, nodeIdentifier, options):
     1180    def setConfiguration(self, request):
    10301181        raise Unsupported('config-node')
    10311182
    10321183
    1033     def items(self, requestor, service, nodeIdentifier, maxItems,
    1034                     itemIdentifiers):
     1184    def items(self, request):
    10351185        raise Unsupported('retrieve-items')
    10361186
    10371187
    1038     def retract(self, requestor, service, nodeIdentifier, itemIdentifiers):
     1188    def retract(self, request):
    10391189        raise Unsupported('retract-items')
    10401190
    10411191
    1042     def purge(self, requestor, service, nodeIdentifier):
     1192    def purge(self, request):
    10431193        raise Unsupported('purge-nodes')
    10441194
    10451195
    1046     def delete(self, requestor, service, nodeIdentifier):
     1196    def delete(self, request):
    10471197        raise Unsupported('delete-nodes')
  • wokkel/test/test_pubsub.py

    diff -r bcbcbc4aa868 wokkel/test/test_pubsub.py
    a b  
    1414from twisted.words.protocols.jabber.jid import JID
    1515
    1616from wokkel import data_form, iwokkel, pubsub, shim
     17from wokkel.generic import parseXml
    1718from wokkel.test.helpers import TestableRequestHandlerMixin, XmlStreamStub
    1819
    1920try:
     
    490491
    491492
    492493
     494class PubSubRequestTest(unittest.TestCase):
     495
     496    def test_fromElementPublish(self):
     497        """
     498        Test parsing a publish request.
     499        """
     500
     501        xml = """
     502        <iq type='set' to='pubsub.example.org'
     503                       from='user@example.org'>
     504          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     505            <publish node='test'/>
     506          </pubsub>
     507        </iq>
     508        """
     509
     510        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
     511        self.assertEqual('publish', request.verb)
     512        self.assertEqual(JID('user@example.org'), request.sender)
     513        self.assertEqual(JID('pubsub.example.org'), request.recipient)
     514        self.assertEqual('test', request.nodeIdentifier)
     515        self.assertEqual([], request.items)
     516
     517
     518    def test_fromElementPublishItems(self):
     519        """
     520        Test parsing a publish request with items.
     521        """
     522
     523        xml = """
     524        <iq type='set' to='pubsub.example.org'
     525                       from='user@example.org'>
     526          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     527            <publish node='test'>
     528              <item id="item1"/>
     529              <item id="item2"/>
     530            </publish>
     531          </pubsub>
     532        </iq>
     533        """
     534
     535        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
     536        self.assertEqual(2, len(request.items))
     537        self.assertEqual(u'item1', request.items[0]["id"])
     538        self.assertEqual(u'item2', request.items[1]["id"])
     539
     540
     541    def test_fromElementPublishNoNode(self):
     542        """
     543        A publish request to the root node should raise an exception.
     544        """
     545        xml = """
     546        <iq type='set' to='pubsub.example.org'
     547                       from='user@example.org'>
     548          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     549            <publish/>
     550          </pubsub>
     551        </iq>
     552        """
     553
     554        err = self.assertRaises(error.StanzaError,
     555                                pubsub.PubSubRequest.fromElement,
     556                                parseXml(xml))
     557        self.assertEqual('bad-request', err.condition)
     558        self.assertEqual(NS_PUBSUB_ERRORS, err.appCondition.uri)
     559        self.assertEqual('nodeid-required', err.appCondition.name)
     560
     561
     562    def test_fromElementSubscribe(self):
     563        """
     564        Test parsing a subscription request.
     565        """
     566
     567        xml = """
     568        <iq type='set' to='pubsub.example.org'
     569                       from='user@example.org'>
     570          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     571            <subscribe node='test' jid='user@example.org/Home'/>
     572          </pubsub>
     573        </iq>
     574        """
     575
     576        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
     577        self.assertEqual('subscribe', request.verb)
     578        self.assertEqual(JID('user@example.org'), request.sender)
     579        self.assertEqual(JID('pubsub.example.org'), request.recipient)
     580        self.assertEqual('test', request.nodeIdentifier)
     581        self.assertEqual(JID('user@example.org/Home'), request.subscriber)
     582
     583
     584    def test_fromElementSubscribeEmptyNode(self):
     585        """
     586        Test parsing a subscription request to the root node.
     587        """
     588
     589        xml = """
     590        <iq type='set' to='pubsub.example.org'
     591                       from='user@example.org'>
     592          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     593            <subscribe jid='user@example.org/Home'/>
     594          </pubsub>
     595        </iq>
     596        """
     597
     598        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
     599        self.assertEqual('', request.nodeIdentifier)
     600
     601
     602    def test_fromElementSubscribeNoJID(self):
     603        """
     604        Subscribe requests without a JID should raise a bad-request exception.
     605        """
     606        xml = """
     607        <iq type='set' to='pubsub.example.org'
     608                       from='user@example.org'>
     609          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     610            <subscribe node='test'/>
     611          </pubsub>
     612        </iq>
     613        """
     614        err = self.assertRaises(error.StanzaError,
     615                                pubsub.PubSubRequest.fromElement,
     616                                parseXml(xml))
     617        self.assertEqual('bad-request', err.condition)
     618        self.assertEqual(NS_PUBSUB_ERRORS, err.appCondition.uri)
     619        self.assertEqual('jid-required', err.appCondition.name)
     620
     621    def test_fromElementUnsubscribe(self):
     622        """
     623        Test parsing an unsubscription request.
     624        """
     625
     626        xml = """
     627        <iq type='set' to='pubsub.example.org'
     628                       from='user@example.org'>
     629          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     630            <unsubscribe node='test' jid='user@example.org/Home'/>
     631          </pubsub>
     632        </iq>
     633        """
     634
     635        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
     636        self.assertEqual('unsubscribe', request.verb)
     637        self.assertEqual(JID('user@example.org'), request.sender)
     638        self.assertEqual(JID('pubsub.example.org'), request.recipient)
     639        self.assertEqual('test', request.nodeIdentifier)
     640        self.assertEqual(JID('user@example.org/Home'), request.subscriber)
     641
     642
     643    def test_fromElementUnsubscribeNoJID(self):
     644        """
     645        Unsubscribe requests without a JID should raise a bad-request exception.
     646        """
     647        xml = """
     648        <iq type='set' to='pubsub.example.org'
     649                       from='user@example.org'>
     650          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     651            <unsubscribe node='test'/>
     652          </pubsub>
     653        </iq>
     654        """
     655        err = self.assertRaises(error.StanzaError,
     656                                pubsub.PubSubRequest.fromElement,
     657                                parseXml(xml))
     658        self.assertEqual('bad-request', err.condition)
     659        self.assertEqual(NS_PUBSUB_ERRORS, err.appCondition.uri)
     660        self.assertEqual('jid-required', err.appCondition.name)
     661
     662
     663    def test_fromElementOptionsGet(self):
     664        """
     665        Test parsing a request for getting subscription options.
     666        """
     667
     668        xml = """
     669        <iq type='get' to='pubsub.example.org'
     670                       from='user@example.org'>
     671          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     672            <options node='test' jid='user@example.org/Home'/>
     673          </pubsub>
     674        </iq>
     675        """
     676
     677        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
     678        self.assertEqual('optionsGet', request.verb)
     679
     680
     681    def test_fromElementOptionsSet(self):
     682        """
     683        Test parsing a request for setting subscription options.
     684        """
     685
     686        xml = """
     687        <iq type='set' to='pubsub.example.org'
     688                       from='user@example.org'>
     689          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     690            <options node='test' jid='user@example.org/Home'>
     691              <x xmlns='jabber:x:data' type='submit'>
     692                <field var='FORM_TYPE' type='hidden'>
     693                  <value>http://jabber.org/protocol/pubsub#subscribe_options</value>
     694                </field>
     695                <field var='pubsub#deliver'><value>1</value></field>
     696              </x>
     697            </options>
     698          </pubsub>
     699        </iq>
     700        """
     701
     702        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
     703        self.assertEqual('optionsSet', request.verb)
     704        self.assertEqual(JID('user@example.org'), request.sender)
     705        self.assertEqual(JID('pubsub.example.org'), request.recipient)
     706        self.assertEqual('test', request.nodeIdentifier)
     707        self.assertEqual(JID('user@example.org/Home'), request.subscriber)
     708        self.assertEqual({'pubsub#deliver': '1'}, request.options)
     709
     710
     711    def test_fromElementOptionsSetCancel(self):
     712        """
     713        Test parsing a request for cancelling setting subscription options.
     714        """
     715
     716        xml = """
     717        <iq type='set' to='pubsub.example.org'
     718                       from='user@example.org'>
     719          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     720            <options node='test' jid='user@example.org/Home'>
     721              <x xmlns='jabber:x:data' type='cancel'/>
     722            </options>
     723          </pubsub>
     724        </iq>
     725        """
     726
     727        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
     728        self.assertEqual({}, request.options)
     729
     730
     731    def test_fromElementOptionsSetBadFormType(self):
     732        """
     733        On a options set request unknown fields should be ignored.
     734        """
     735
     736        xml = """
     737        <iq type='set' to='pubsub.example.org'
     738                       from='user@example.org'>
     739          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     740            <options node='test' jid='user@example.org/Home'>
     741              <x xmlns='jabber:x:data' type='result'>
     742                <field var='FORM_TYPE' type='hidden'>
     743                  <value>http://jabber.org/protocol/pubsub#node_config</value>
     744                </field>
     745                <field var='pubsub#deliver'><value>1</value></field>
     746              </x>
     747            </options>
     748          </pubsub>
     749        </iq>
     750        """
     751
     752        err = self.assertRaises(error.StanzaError,
     753                                pubsub.PubSubRequest.fromElement,
     754                                parseXml(xml))
     755        self.assertEqual('bad-request', err.condition)
     756        self.assertEqual(None, err.appCondition)
     757
     758
     759    def test_fromElementOptionsSetNoForm(self):
     760        """
     761        On a options set request a form is required.
     762        """
     763
     764        xml = """
     765        <iq type='set' to='pubsub.example.org'
     766                       from='user@example.org'>
     767          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     768            <options node='test' jid='user@example.org/Home'/>
     769          </pubsub>
     770        </iq>
     771        """
     772        err = self.assertRaises(error.StanzaError,
     773                                pubsub.PubSubRequest.fromElement,
     774                                parseXml(xml))
     775        self.assertEqual('bad-request', err.condition)
     776        self.assertEqual(None, err.appCondition)
     777
     778
     779    def test_fromElementSubscriptions(self):
     780        """
     781        Test parsing a request for all subscriptions.
     782        """
     783
     784        xml = """
     785        <iq type='get' to='pubsub.example.org'
     786                       from='user@example.org'>
     787          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     788            <subscriptions/>
     789          </pubsub>
     790        </iq>
     791        """
     792
     793        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
     794        self.assertEqual('subscriptions', request.verb)
     795        self.assertEqual(JID('user@example.org'), request.sender)
     796        self.assertEqual(JID('pubsub.example.org'), request.recipient)
     797
     798
     799    def test_fromElementAffiliations(self):
     800        """
     801        Test parsing a request for all affiliations.
     802        """
     803
     804        xml = """
     805        <iq type='get' to='pubsub.example.org'
     806                       from='user@example.org'>
     807          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     808            <affiliations/>
     809          </pubsub>
     810        </iq>
     811        """
     812
     813        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
     814        self.assertEqual('affiliations', request.verb)
     815        self.assertEqual(JID('user@example.org'), request.sender)
     816        self.assertEqual(JID('pubsub.example.org'), request.recipient)
     817
     818
     819    def test_fromElementCreate(self):
     820        """
     821        Test parsing a request to create a node.
     822        """
     823
     824        xml = """
     825        <iq type='set' to='pubsub.example.org'
     826                       from='user@example.org'>
     827          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     828            <create node='mynode'/>
     829          </pubsub>
     830        </iq>
     831        """
     832
     833        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
     834        self.assertEqual('create', request.verb)
     835        self.assertEqual(JID('user@example.org'), request.sender)
     836        self.assertEqual(JID('pubsub.example.org'), request.recipient)
     837        self.assertEqual('mynode', request.nodeIdentifier)
     838
     839
     840    def test_fromElementCreateInstant(self):
     841        """
     842        Test parsing a request to create an instant node.
     843        """
     844
     845        xml = """
     846        <iq type='set' to='pubsub.example.org'
     847                       from='user@example.org'>
     848          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     849            <create/>
     850          </pubsub>
     851        </iq>
     852        """
     853
     854        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
     855        self.assertIdentical(None, request.nodeIdentifier)
     856
     857
     858    def test_fromElementDefault(self):
     859        """
     860        Test parsing a request for the default node configuration.
     861        """
     862
     863        xml = """
     864        <iq type='get' to='pubsub.example.org'
     865                       from='user@example.org'>
     866          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
     867            <default/>
     868          </pubsub>
     869        </iq>
     870        """
     871
     872        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
     873        self.assertEqual('default', request.verb)
     874        self.assertEqual(JID('user@example.org'), request.sender)
     875        self.assertEqual(JID('pubsub.example.org'), request.recipient)
     876        self.assertEqual('leaf', request.nodeType)
     877
     878
     879    def test_fromElementDefaultCollection(self):
     880        """
     881        Parsing a request for the default configuration extracts the node type.
     882        """
     883
     884        xml = """
     885        <iq type='get' to='pubsub.example.org'
     886                       from='user@example.org'>
     887          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
     888            <default>
     889              <x xmlns='jabber:x:data' type='submit'>
     890                <field var='FORM_TYPE' type='hidden'>
     891                  <value>http://jabber.org/protocol/pubsub#node_config</value>
     892                </field>
     893                <field var='pubsub#node_type'>
     894                  <value>collection</value>
     895                </field>
     896              </x>
     897            </default>
     898
     899          </pubsub>
     900        </iq>
     901        """
     902
     903        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
     904        self.assertEqual('collection', request.nodeType)
     905
     906
     907    def test_fromElementConfigureGet(self):
     908        """
     909        Test parsing a node configuration get request.
     910        """
     911
     912        xml = """
     913        <iq type='get' to='pubsub.example.org'
     914                       from='user@example.org'>
     915          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
     916            <configure node='test'/>
     917          </pubsub>
     918        </iq>
     919        """
     920
     921        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
     922        self.assertEqual('configureGet', request.verb)
     923        self.assertEqual(JID('user@example.org'), request.sender)
     924        self.assertEqual(JID('pubsub.example.org'), request.recipient)
     925        self.assertEqual('test', request.nodeIdentifier)
     926
     927
     928    def test_fromElementConfigureSet(self):
     929        """
     930        On a node configuration set request the Data Form is parsed.
     931        """
     932
     933        xml = """
     934        <iq type='set' to='pubsub.example.org'
     935                       from='user@example.org'>
     936          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
     937            <configure node='test'>
     938              <x xmlns='jabber:x:data' type='submit'>
     939                <field var='FORM_TYPE' type='hidden'>
     940                  <value>http://jabber.org/protocol/pubsub#node_config</value>
     941                </field>
     942                <field var='pubsub#deliver_payloads'><value>0</value></field>
     943                <field var='pubsub#persist_items'><value>1</value></field>
     944              </x>
     945            </configure>
     946          </pubsub>
     947        </iq>
     948        """
     949
     950        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
     951        self.assertEqual('configureSet', request.verb)
     952        self.assertEqual(JID('user@example.org'), request.sender)
     953        self.assertEqual(JID('pubsub.example.org'), request.recipient)
     954        self.assertEqual('test', request.nodeIdentifier)
     955        self.assertEqual({'pubsub#deliver_payloads': '0',
     956                          'pubsub#persist_items': '1'}, request.options)
     957
     958
     959    def test_fromElementConfigureSetCancel(self):
     960        """
     961        The node configuration is cancelled, so no options.
     962        """
     963
     964        xml = """
     965        <iq type='set' to='pubsub.example.org'
     966                       from='user@example.org'>
     967          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
     968            <configure node='test'>
     969              <x xmlns='jabber:x:data' type='cancel'/>
     970            </configure>
     971          </pubsub>
     972        </iq>
     973        """
     974
     975        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
     976        self.assertEqual({}, request.options)
     977
     978
     979    def test_fromElementConfigureSetBadFormType(self):
     980        """
     981        On a node configuration set request unknown fields should be ignored.
     982        """
     983
     984        xml = """
     985        <iq type='set' to='pubsub.example.org'
     986                       from='user@example.org'>
     987          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
     988            <configure node='test'>
     989              <x xmlns='jabber:x:data' type='result'>
     990                <field var='FORM_TYPE' type='hidden'>
     991                  <value>http://jabber.org/protocol/pubsub#node_config</value>
     992                </field>
     993                <field var='pubsub#deliver_payloads'><value>0</value></field>
     994                <field var='x-myfield'><value>1</value></field>
     995              </x>
     996            </configure>
     997          </pubsub>
     998        </iq>
     999        """
     1000
     1001        err = self.assertRaises(error.StanzaError,
     1002                                pubsub.PubSubRequest.fromElement,
     1003                                parseXml(xml))
     1004        self.assertEqual('bad-request', err.condition)
     1005        self.assertEqual(None, err.appCondition)
     1006
     1007
     1008    def test_fromElementConfigureSetNoForm(self):
     1009        """
     1010        On a node configuration set request a form is required.
     1011        """
     1012
     1013        xml = """
     1014        <iq type='set' to='pubsub.example.org'
     1015                       from='user@example.org'>
     1016          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
     1017            <configure node='test'/>
     1018          </pubsub>
     1019        </iq>
     1020        """
     1021        err = self.assertRaises(error.StanzaError,
     1022                                pubsub.PubSubRequest.fromElement,
     1023                                parseXml(xml))
     1024        self.assertEqual('bad-request', err.condition)
     1025        self.assertEqual(None, err.appCondition)
     1026
     1027
     1028    def test_fromElementItems(self):
     1029        """
     1030        Test parsing an items request.
     1031        """
     1032        xml = """
     1033        <iq type='get' to='pubsub.example.org'
     1034                       from='user@example.org'>
     1035          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     1036            <items node='test'/>
     1037          </pubsub>
     1038        </iq>
     1039        """
     1040
     1041        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
     1042        self.assertEqual('items', request.verb)
     1043        self.assertEqual(JID('user@example.org'), request.sender)
     1044        self.assertEqual(JID('pubsub.example.org'), request.recipient)
     1045        self.assertEqual('test', request.nodeIdentifier)
     1046        self.assertIdentical(None, request.maxItems)
     1047        self.assertEqual([], request.itemIdentifiers)
     1048
     1049
     1050    def test_fromElementRetract(self):
     1051        """
     1052        Test parsing a retract request.
     1053        """
     1054
     1055        xml = """
     1056        <iq type='set' to='pubsub.example.org'
     1057                       from='user@example.org'>
     1058          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     1059            <retract node='test'>
     1060              <item id='item1'/>
     1061              <item id='item2'/>
     1062            </retract>
     1063          </pubsub>
     1064        </iq>
     1065        """
     1066
     1067        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
     1068        self.assertEqual('retract', request.verb)
     1069        self.assertEqual(JID('user@example.org'), request.sender)
     1070        self.assertEqual(JID('pubsub.example.org'), request.recipient)
     1071        self.assertEqual('test', request.nodeIdentifier)
     1072        self.assertEqual(['item1', 'item2'], request.itemIdentifiers)
     1073
     1074
     1075    def test_fromElementPurge(self):
     1076        """
     1077        Test parsing a purge request.
     1078        """
     1079
     1080        xml = """
     1081        <iq type='set' to='pubsub.example.org'
     1082                       from='user@example.org'>
     1083          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
     1084            <purge node='test'/>
     1085          </pubsub>
     1086        </iq>
     1087        """
     1088
     1089        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
     1090        self.assertEqual('purge', request.verb)
     1091        self.assertEqual(JID('user@example.org'), request.sender)
     1092        self.assertEqual(JID('pubsub.example.org'), request.recipient)
     1093        self.assertEqual('test', request.nodeIdentifier)
     1094
     1095
     1096    def test_fromElementDelete(self):
     1097        """
     1098        Test parsing a delete request.
     1099        """
     1100
     1101        xml = """
     1102        <iq type='set' to='pubsub.example.org'
     1103                       from='user@example.org'>
     1104          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
     1105            <delete node='test'/>
     1106          </pubsub>
     1107        </iq>
     1108        """
     1109
     1110        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
     1111        self.assertEqual('delete', request.verb)
     1112        self.assertEqual(JID('user@example.org'), request.sender)
     1113        self.assertEqual(JID('pubsub.example.org'), request.recipient)
     1114        self.assertEqual('test', request.nodeIdentifier)
     1115
     1116
     1117
    4931118class PubSubServiceTest(unittest.TestCase, TestableRequestHandlerMixin):
    4941119    """
    4951120    Tests for L{pubsub.PubSubService}.
     
    5071132        verify.verifyObject(iwokkel.IPubSubService, self.service)
    5081133
    5091134
     1135    def test_connectionMade(self):
     1136        """
     1137        Verify setup of observers in L{pubsub.connectionMade}.
     1138        """
     1139        requests = []
     1140
     1141        def handleRequest(iq):
     1142            requests.append(iq)
     1143
     1144        self.service.xmlstream = self.stub.xmlstream
     1145        self.service.handleRequest = handleRequest
     1146        self.service.connectionMade()
     1147
     1148        for namespace in (NS_PUBSUB, NS_PUBSUB_OWNER):
     1149            for stanzaType in ('get', 'set'):
     1150                iq = domish.Element((None, 'iq'))
     1151                iq['type'] = stanzaType
     1152                iq.addElement((namespace, 'pubsub'))
     1153                self.stub.xmlstream.dispatch(iq)
     1154
     1155        self.assertEqual(4, len(requests))
     1156
     1157
    5101158    def test_getDiscoInfo(self):
    5111159        """
    5121160        Test getDiscoInfo calls getNodeInfo and returns some minimal info.
     
    5241172        return d
    5251173
    5261174
    527     def test_onPublishNoNode(self):
    528         """
    529         The root node is always a collection, publishing is a bad request.
    530         """
    531         xml = """
    532         <iq type='set' to='pubsub.example.org'
    533                        from='user@example.org'>
    534           <pubsub xmlns='http://jabber.org/protocol/pubsub'>
    535             <publish/>
    536           </pubsub>
    537         </iq>
    538         """
    539 
    540         def cb(result):
    541             self.assertEquals('bad-request', result.condition)
    542 
    543         d = self.handleRequest(xml)
    544         self.assertFailure(d, error.StanzaError)
    545         d.addCallback(cb)
    546         return d
    547 
    548 
    5491175    def test_onPublish(self):
    5501176        """
    5511177        A publish request should result in L{PubSubService.publish} being
     
    5611187        </iq>
    5621188        """
    5631189
    564         def publish(requestor, service, nodeIdentifier, items):
    565             self.assertEqual(JID('user@example.org'), requestor)
    566             self.assertEqual(JID('pubsub.example.org'), service)
    567             self.assertEqual('test', nodeIdentifier)
    568             self.assertEqual([], items)
     1190        def publish(request):
    5691191            return defer.succeed(None)
    5701192
    5711193        self.service.publish = publish
     1194        verify.verifyObject(iwokkel.IPubSubService, self.service)
    5721195        return self.handleRequest(xml)
    5731196
    5741197
     1198    def test_onSubscribe(self):
     1199        """
     1200        A successful subscription should return the current subscription.
     1201        """
     1202
     1203        xml = """
     1204        <iq type='set' to='pubsub.example.org'
     1205                       from='user@example.org'>
     1206          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     1207            <subscribe node='test' jid='user@example.org/Home'/>
     1208          </pubsub>
     1209        </iq>
     1210        """
     1211
     1212        def subscribe(request):
     1213            return defer.succeed(pubsub.Subscription(request.nodeIdentifier,
     1214                                                     request.subscriber,
     1215                                                     'subscribed'))
     1216
     1217        def cb(element):
     1218            self.assertEqual('pubsub', element.name)
     1219            self.assertEqual(NS_PUBSUB, element.uri)
     1220            subscription = element.subscription
     1221            self.assertEqual(NS_PUBSUB, subscription.uri)
     1222            self.assertEqual('test', subscription['node'])
     1223            self.assertEqual('user@example.org/Home', subscription['jid'])
     1224            self.assertEqual('subscribed', subscription['subscription'])
     1225
     1226        self.service.subscribe = subscribe
     1227        verify.verifyObject(iwokkel.IPubSubService, self.service)
     1228        d = self.handleRequest(xml)
     1229        d.addCallback(cb)
     1230        return d
     1231
     1232
     1233    def test_onSubscribeEmptyNode(self):
     1234        """
     1235        A successful subscription on root node should return no node attribute.
     1236        """
     1237
     1238        xml = """
     1239        <iq type='set' to='pubsub.example.org'
     1240                       from='user@example.org'>
     1241          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     1242            <subscribe jid='user@example.org/Home'/>
     1243          </pubsub>
     1244        </iq>
     1245        """
     1246
     1247        def subscribe(request):
     1248            return defer.succeed(pubsub.Subscription(request.nodeIdentifier,
     1249                                                     request.subscriber,
     1250                                                     'subscribed'))
     1251
     1252        def cb(element):
     1253            self.assertFalse(element.subscription.hasAttribute('node'))
     1254
     1255        self.service.subscribe = subscribe
     1256        verify.verifyObject(iwokkel.IPubSubService, self.service)
     1257        d = self.handleRequest(xml)
     1258        d.addCallback(cb)
     1259        return d
     1260
     1261
     1262    def test_onUnsubscribe(self):
     1263        """
     1264        A successful unsubscription should return an empty response.
     1265        """
     1266
     1267        xml = """
     1268        <iq type='set' to='pubsub.example.org'
     1269                       from='user@example.org'>
     1270          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     1271            <unsubscribe node='test' jid='user@example.org/Home'/>
     1272          </pubsub>
     1273        </iq>
     1274        """
     1275
     1276        def unsubscribe(request):
     1277            return defer.succeed(None)
     1278
     1279        def cb(element):
     1280            self.assertIdentical(None, element)
     1281
     1282        self.service.unsubscribe = unsubscribe
     1283        verify.verifyObject(iwokkel.IPubSubService, self.service)
     1284        d = self.handleRequest(xml)
     1285        d.addCallback(cb)
     1286        return d
     1287
     1288
    5751289    def test_onOptionsGet(self):
    5761290        """
    577         Subscription options are not supported.
     1291        Getting subscription options is not supported.
    5781292        """
    5791293
    5801294        xml = """
    5811295        <iq type='get' to='pubsub.example.org'
    5821296                       from='user@example.org'>
    5831297          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
    584             <options/>
     1298            <options node='test' jid='user@example.org/Home'/>
     1299          </pubsub>
     1300        </iq>
     1301        """
     1302
     1303        def cb(result):
     1304            self.assertEquals('feature-not-implemented', result.condition)
     1305            self.assertEquals('unsupported', result.appCondition.name)
     1306            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     1307
     1308        d = self.handleRequest(xml)
     1309        self.assertFailure(d, error.StanzaError)
     1310        d.addCallback(cb)
     1311        return d
     1312
     1313
     1314    def test_onOptionsSet(self):
     1315        """
     1316        Setting subscription options is not supported.
     1317        """
     1318
     1319        xml = """
     1320        <iq type='set' to='pubsub.example.org'
     1321                       from='user@example.org'>
     1322          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     1323            <options node='test' jid='user@example.org/Home'>
     1324              <x xmlns='jabber:x:data' type='submit'>
     1325                <field var='FORM_TYPE' type='hidden'>
     1326                  <value>http://jabber.org/protocol/pubsub#subscribe_options</value>
     1327                </field>
     1328                <field var='pubsub#deliver'><value>1</value></field>
     1329              </x>
     1330            </options>
    5851331          </pubsub>
    5861332        </iq>
    5871333        """
     
    6271373            self.assertEqual('subscribed', subscription['subscription'])
    6281374
    6291375
    630         def subscriptions(requestor, service):
    631             self.assertEqual(JID('user@example.org'), requestor)
    632             self.assertEqual(JID('pubsub.example.org'), service)
     1376        def subscriptions(request):
    6331377            subscription = pubsub.Subscription('test', JID('user@example.org'),
    6341378                                               'subscribed')
    6351379            return defer.succeed([subscription])
    6361380
    6371381        self.service.subscriptions = subscriptions
     1382        verify.verifyObject(iwokkel.IPubSubService, self.service)
     1383        d = self.handleRequest(xml)
     1384        d.addCallback(cb)
     1385        return d
     1386
     1387
     1388    def test_onAffiliations(self):
     1389        """
     1390        A subscriptions request should result in
     1391        L{PubSubService.affiliations} being called and the result prepared
     1392        for the response.
     1393        """
     1394
     1395        xml = """
     1396        <iq type='get' to='pubsub.example.org'
     1397                       from='user@example.org'>
     1398          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     1399            <affiliations/>
     1400          </pubsub>
     1401        </iq>
     1402        """
     1403
     1404        def cb(element):
     1405            self.assertEqual('pubsub', element.name)
     1406            self.assertEqual(NS_PUBSUB, element.uri)
     1407            self.assertEqual(NS_PUBSUB, element.affiliations.uri)
     1408            children = list(element.affiliations.elements())
     1409            self.assertEqual(1, len(children))
     1410            affiliation = children[0]
     1411            self.assertEqual('affiliation', affiliation.name)
     1412            self.assertEqual(NS_PUBSUB, affiliation.uri)
     1413            self.assertEqual('test', affiliation['node'])
     1414            self.assertEqual('owner', affiliation['affiliation'])
     1415
     1416
     1417        def affiliations(request):
     1418            affiliation = ('test', 'owner')
     1419            return defer.succeed([affiliation])
     1420
     1421        self.service.affiliations = affiliations
     1422        verify.verifyObject(iwokkel.IPubSubService, self.service)
     1423        d = self.handleRequest(xml)
     1424        d.addCallback(cb)
     1425        return d
     1426
     1427
     1428    def test_onCreate(self):
     1429        """
     1430        Replies to create node requests don't return the created node.
     1431        """
     1432
     1433        xml = """
     1434        <iq type='set' to='pubsub.example.org'
     1435                       from='user@example.org'>
     1436          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     1437            <create node='mynode'/>
     1438          </pubsub>
     1439        </iq>
     1440        """
     1441
     1442        def create(request):
     1443            return defer.succeed(request.nodeIdentifier)
     1444
     1445        def cb(element):
     1446            self.assertIdentical(None, element)
     1447
     1448        self.service.create = create
     1449        verify.verifyObject(iwokkel.IPubSubService, self.service)
     1450        d = self.handleRequest(xml)
     1451        d.addCallback(cb)
     1452        return d
     1453
     1454
     1455    def test_onCreateChanged(self):
     1456        """
     1457        Replies to create node requests return the created node if changed.
     1458        """
     1459
     1460        xml = """
     1461        <iq type='set' to='pubsub.example.org'
     1462                       from='user@example.org'>
     1463          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     1464            <create node='mynode'/>
     1465          </pubsub>
     1466        </iq>
     1467        """
     1468
     1469        def create(request):
     1470            return defer.succeed(u'myrenamednode')
     1471
     1472        def cb(element):
     1473            self.assertEqual('pubsub', element.name)
     1474            self.assertEqual(NS_PUBSUB, element.uri)
     1475            self.assertEqual(NS_PUBSUB, element.create.uri)
     1476            self.assertEqual(u'myrenamednode',
     1477                             element.create.getAttribute('node'))
     1478
     1479        self.service.create = create
     1480        verify.verifyObject(iwokkel.IPubSubService, self.service)
     1481        d = self.handleRequest(xml)
     1482        d.addCallback(cb)
     1483        return d
     1484
     1485
     1486    def test_onCreateInstant(self):
     1487        """
     1488        Replies to create instant node requests return the created node.
     1489        """
     1490
     1491        xml = """
     1492        <iq type='set' to='pubsub.example.org'
     1493                       from='user@example.org'>
     1494          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     1495            <create/>
     1496          </pubsub>
     1497        </iq>
     1498        """
     1499
     1500        def create(request):
     1501            return defer.succeed(u'random')
     1502
     1503        def cb(element):
     1504            self.assertEqual('pubsub', element.name)
     1505            self.assertEqual(NS_PUBSUB, element.uri)
     1506            self.assertEqual(NS_PUBSUB, element.create.uri)
     1507            self.assertEqual(u'random', element.create.getAttribute('node'))
     1508
     1509        self.service.create = create
     1510        verify.verifyObject(iwokkel.IPubSubService, self.service)
    6381511        d = self.handleRequest(xml)
    6391512        d.addCallback(cb)
    6401513        return d
     
    6651538                     "label": "Deliver payloads with event notifications"}
    6661539                }
    6671540
    668         def getDefaultConfiguration(requestor, service, nodeType):
    669             self.assertEqual(JID('user@example.org'), requestor)
    670             self.assertEqual(JID('pubsub.example.org'), service)
    671             self.assertEqual('leaf', nodeType)
     1541        def getDefaultConfiguration(request):
    6721542            return defer.succeed({})
    6731543
    6741544        def cb(element):
     
    6861556        return d
    6871557
    6881558
     1559    def test_onDefaultCollection(self):
     1560        """
     1561        Responses to default requests should depend on passed node type.
     1562        """
     1563
     1564        xml = """
     1565        <iq type='get' to='pubsub.example.org'
     1566                       from='user@example.org'>
     1567          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
     1568            <default>
     1569              <x xmlns='jabber:x:data' type='submit'>
     1570                <field var='FORM_TYPE' type='hidden'>
     1571                  <value>http://jabber.org/protocol/pubsub#node_config</value>
     1572                </field>
     1573                <field var='pubsub#node_type'>
     1574                  <value>collection</value>
     1575                </field>
     1576              </x>
     1577            </default>
     1578
     1579          </pubsub>
     1580        </iq>
     1581        """
     1582
     1583        def getConfigurationOptions():
     1584            return {
     1585                "pubsub#deliver_payloads":
     1586                    {"type": "boolean",
     1587                     "label": "Deliver payloads with event notifications"}
     1588                }
     1589
     1590        def getDefaultConfiguration(request):
     1591            return defer.succeed({})
     1592
     1593        self.service.getConfigurationOptions = getConfigurationOptions
     1594        self.service.getDefaultConfiguration = getDefaultConfiguration
     1595        verify.verifyObject(iwokkel.IPubSubService, self.service)
     1596        return self.handleRequest(xml)
     1597
     1598
     1599    def test_onDefaultUnknownNodeType(self):
     1600        """
     1601        A default request should result in
     1602        L{PubSubService.getDefaultConfiguration} being called.
     1603        """
     1604
     1605        xml = """
     1606        <iq type='get' to='pubsub.example.org'
     1607                       from='user@example.org'>
     1608          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
     1609            <default>
     1610              <x xmlns='jabber:x:data' type='submit'>
     1611                <field var='FORM_TYPE' type='hidden'>
     1612                  <value>http://jabber.org/protocol/pubsub#node_config</value>
     1613                </field>
     1614                <field var='pubsub#node_type'>
     1615                  <value>unknown</value>
     1616                </field>
     1617              </x>
     1618            </default>
     1619
     1620          </pubsub>
     1621        </iq>
     1622        """
     1623
     1624        def getDefaultConfiguration(request):
     1625            self.fail("Unexpected call to getConfiguration")
     1626
     1627        def cb(result):
     1628            self.assertEquals('not-acceptable', result.condition)
     1629
     1630        self.service.getDefaultConfiguration = getDefaultConfiguration
     1631        verify.verifyObject(iwokkel.IPubSubService, self.service)
     1632        d = self.handleRequest(xml)
     1633        self.assertFailure(d, error.StanzaError)
     1634        d.addCallback(cb)
     1635        return d
     1636
     1637
    6891638    def test_onConfigureGet(self):
    6901639        """
    6911640        On a node configuration get request L{PubSubService.getConfiguration}
     
    7141663                     "label": "Owner of the node"}
    7151664                }
    7161665
    717         def getConfiguration(requestor, service, nodeIdentifier):
    718             self.assertEqual(JID('user@example.org'), requestor)
    719             self.assertEqual(JID('pubsub.example.org'), service)
    720             self.assertEqual('test', nodeIdentifier)
    721 
     1666        def getConfiguration(request):
    7221667            return defer.succeed({'pubsub#deliver_payloads': '0',
    7231668                                  'pubsub#persist_items': '1',
    724                                   'pubsub#owner': JID('user@example.org')})
     1669                                  'pubsub#owner': JID('user@example.org'),
     1670                                  'x-myfield': ['a', 'b']})
    7251671
    7261672        def cb(element):
    7271673            self.assertEqual('pubsub', element.name)
     
    7491695            field.typeCheck()
    7501696            self.assertEqual(JID('user@example.org'), field.value)
    7511697
     1698            self.assertNotIn('x-myfield', fields)
     1699
     1700
    7521701        self.service.getConfigurationOptions = getConfigurationOptions
    7531702        self.service.getConfiguration = getConfiguration
     1703        verify.verifyObject(iwokkel.IPubSubService, self.service)
    7541704        d = self.handleRequest(xml)
    7551705        d.addCallback(cb)
    7561706        return d
     
    7891739                     "label": "Deliver payloads with event notifications"}
    7901740                }
    7911741
    792         def setConfiguration(requestor, service, nodeIdentifier, options):
    793             self.assertEqual(JID('user@example.org'), requestor)
    794             self.assertEqual(JID('pubsub.example.org'), service)
    795             self.assertEqual('test', nodeIdentifier)
     1742        def setConfiguration(request):
    7961743            self.assertEqual({'pubsub#deliver_payloads': False,
    797                               'pubsub#persist_items': True}, options)
     1744                              'pubsub#persist_items': True}, request.options)
    7981745            return defer.succeed(None)
    7991746
    8001747        self.service.getConfigurationOptions = getConfigurationOptions
    8011748        self.service.setConfiguration = setConfiguration
     1749        verify.verifyObject(iwokkel.IPubSubService, self.service)
    8021750        return self.handleRequest(xml)
    8031751
    8041752
     
    8231771        </iq>
    8241772        """
    8251773
    826         def setConfiguration(requestor, service, nodeIdentifier, options):
     1774        def setConfiguration(request):
    8271775            self.fail("Unexpected call to setConfiguration")
    8281776
    8291777        self.service.setConfiguration = setConfiguration
     1778        verify.verifyObject(iwokkel.IPubSubService, self.service)
    8301779        return self.handleRequest(xml)
    8311780
    8321781
     
    8621811                     "label": "Deliver payloads with event notifications"}
    8631812                }
    8641813
    865         def setConfiguration(requestor, service, nodeIdentifier, options):
    866             self.assertEquals(['pubsub#deliver_payloads'], options.keys())
     1814        def setConfiguration(request):
     1815            self.assertEquals(['pubsub#deliver_payloads'],
     1816                              request.options.keys())
    8671817
    8681818        self.service.getConfigurationOptions = getConfigurationOptions
    8691819        self.service.setConfiguration = setConfiguration
     1820        verify.verifyObject(iwokkel.IPubSubService, self.service)
    8701821        return self.handleRequest(xml)
    8711822
    8721823
     1824    def test_onConfigureSetBadFormType(self):
     1825        """
     1826        On a node configuration set request unknown fields should be ignored.
     1827        """
     1828
     1829        xml = """
     1830        <iq type='set' to='pubsub.example.org'
     1831                       from='user@example.org'>
     1832          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
     1833            <configure node='test'>
     1834              <x xmlns='jabber:x:data' type='result'>
     1835                <field var='FORM_TYPE' type='hidden'>
     1836                  <value>http://jabber.org/protocol/pubsub#node_config</value>
     1837                </field>
     1838                <field var='pubsub#deliver_payloads'><value>0</value></field>
     1839                <field var='x-myfield'><value>1</value></field>
     1840              </x>
     1841            </configure>
     1842          </pubsub>
     1843        </iq>
     1844        """
     1845
     1846        def cb(result):
     1847            self.assertEquals('bad-request', result.condition)
     1848
     1849        d = self.handleRequest(xml)
     1850        self.assertFailure(d, error.StanzaError)
     1851        d.addCallback(cb)
     1852        return d
     1853
     1854
    8731855    def test_onItems(self):
    8741856        """
    8751857        On a items request, return all items for the given node.
     
    8831865        </iq>
    8841866        """
    8851867
    886         def items(requestor, service, nodeIdentifier, maxItems, items):
    887             self.assertEqual(JID('user@example.org'), requestor)
    888             self.assertEqual(JID('pubsub.example.org'), service)
    889             self.assertEqual('test', nodeIdentifier)
    890             self.assertIdentical(None, maxItems)
    891             self.assertEqual([], items)
     1868        def items(request):
    8921869            return defer.succeed([pubsub.Item('current')])
    8931870
    8941871        def cb(element):
     
    9251902        </iq>
    9261903        """
    9271904
    928         def retract(requestor, service, nodeIdentifier, itemIdentifiers):
    929             self.assertEqual(JID('user@example.org'), requestor)
    930             self.assertEqual(JID('pubsub.example.org'), service)
    931             self.assertEqual('test', nodeIdentifier)
    932             self.assertEqual(['item1', 'item2'], itemIdentifiers)
     1905        def retract(request):
    9331906            return defer.succeed(None)
    9341907
    9351908        self.service.retract = retract
     
    9511924        </iq>
    9521925        """
    9531926
    954         def purge(requestor, service, nodeIdentifier):
    955             self.assertEqual(JID('user@example.org'), requestor)
    956             self.assertEqual(JID('pubsub.example.org'), service)
    957             self.assertEqual('test', nodeIdentifier)
     1927        def purge(request):
    9581928            return defer.succeed(None)
    9591929
    9601930        self.service.purge = purge
     
    9761946        </iq>
    9771947        """
    9781948
    979         def delete(requestor, service, nodeIdentifier):
    980             self.assertEqual(JID('user@example.org'), requestor)
    981             self.assertEqual(JID('pubsub.example.org'), service)
    982             self.assertEqual('test', nodeIdentifier)
     1949        def delete(request):
    9831950            return defer.succeed(None)
    9841951
    9851952        self.service.delete = delete
     
    10311998        self.assertEqual(NS_PUBSUB_EVENT, message.event.delete.redirect.uri)
    10321999        self.assertTrue(message.event.delete.redirect.hasAttribute('uri'))
    10332000        self.assertEqual(redirectURI, message.event.delete.redirect['uri'])
     2001
     2002
     2003    def test_onSubscriptionsGet(self):
     2004        """
     2005        Getting subscription options is not supported.
     2006        """
     2007
     2008        xml = """
     2009        <iq type='get' to='pubsub.example.org'
     2010                       from='user@example.org'>
     2011          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
     2012            <subscriptions/>
     2013          </pubsub>
     2014        </iq>
     2015        """
     2016
     2017        def cb(result):
     2018            self.assertEquals('feature-not-implemented', result.condition)
     2019            self.assertEquals('unsupported', result.appCondition.name)
     2020            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     2021            self.assertEquals('manage-subscriptions',
     2022                              result.appCondition['feature'])
     2023
     2024        d = self.handleRequest(xml)
     2025        self.assertFailure(d, error.StanzaError)
     2026        d.addCallback(cb)
     2027        return d
     2028
     2029
     2030    def test_onSubscriptionsSet(self):
     2031        """
     2032        Setting subscription options is not supported.
     2033        """
     2034
     2035        xml = """
     2036        <iq type='set' to='pubsub.example.org'
     2037                       from='user@example.org'>
     2038          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
     2039            <subscriptions/>
     2040          </pubsub>
     2041        </iq>
     2042        """
     2043
     2044        def cb(result):
     2045            self.assertEquals('feature-not-implemented', result.condition)
     2046            self.assertEquals('unsupported', result.appCondition.name)
     2047            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     2048            self.assertEquals('manage-subscriptions',
     2049                              result.appCondition['feature'])
     2050
     2051        d = self.handleRequest(xml)
     2052        self.assertFailure(d, error.StanzaError)
     2053        d.addCallback(cb)
     2054        return d
     2055
     2056
     2057    def test_onAffiliationsGet(self):
     2058        """
     2059        Getting subscription options is not supported.
     2060        """
     2061
     2062        xml = """
     2063        <iq type='get' to='pubsub.example.org'
     2064                       from='user@example.org'>
     2065          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
     2066            <affiliations/>
     2067          </pubsub>
     2068        </iq>
     2069        """
     2070
     2071        def cb(result):
     2072            self.assertEquals('feature-not-implemented', result.condition)
     2073            self.assertEquals('unsupported', result.appCondition.name)
     2074            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     2075            self.assertEquals('modify-affiliations',
     2076                              result.appCondition['feature'])
     2077
     2078        d = self.handleRequest(xml)
     2079        self.assertFailure(d, error.StanzaError)
     2080        d.addCallback(cb)
     2081        return d
     2082
     2083
     2084    def test_onAffiliationsSet(self):
     2085        """
     2086        Setting subscription options is not supported.
     2087        """
     2088
     2089        xml = """
     2090        <iq type='set' to='pubsub.example.org'
     2091                       from='user@example.org'>
     2092          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
     2093            <affiliations/>
     2094          </pubsub>
     2095        </iq>
     2096        """
     2097
     2098        def cb(result):
     2099            self.assertEquals('feature-not-implemented', result.condition)
     2100            self.assertEquals('unsupported', result.appCondition.name)
     2101            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     2102            self.assertEquals('modify-affiliations',
     2103                              result.appCondition['feature'])
     2104
     2105        d = self.handleRequest(xml)
     2106        self.assertFailure(d, error.StanzaError)
     2107        d.addCallback(cb)
     2108        return d
     2109
     2110
     2111    def test_publish(self):
     2112        """
     2113        Non-overridden L{PubSubService.publish} yields unsupported error.
     2114        """
     2115
     2116        xml = """
     2117        <iq type='set' to='pubsub.example.org'
     2118                       from='user@example.org'>
     2119          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     2120            <publish node='mynode'/>
     2121          </pubsub>
     2122        </iq>
     2123        """
     2124
     2125        def cb(result):
     2126            self.assertEquals('feature-not-implemented', result.condition)
     2127            self.assertEquals('unsupported', result.appCondition.name)
     2128            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     2129            self.assertEquals('publish', result.appCondition['feature'])
     2130
     2131        d = self.handleRequest(xml)
     2132        self.assertFailure(d, error.StanzaError)
     2133        d.addCallback(cb)
     2134        return d
     2135
     2136
     2137    def test_subscribe(self):
     2138        """
     2139        Non-overridden L{PubSubService.subscribe} yields unsupported error.
     2140        """
     2141
     2142        xml = """
     2143        <iq type='set' to='pubsub.example.org'
     2144                       from='user@example.org'>
     2145          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     2146            <subscribe node='test' jid='user@example.org/Home'/>
     2147          </pubsub>
     2148        </iq>
     2149        """
     2150
     2151        def cb(result):
     2152            self.assertEquals('feature-not-implemented', result.condition)
     2153            self.assertEquals('unsupported', result.appCondition.name)
     2154            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     2155            self.assertEquals('subscribe', result.appCondition['feature'])
     2156
     2157        d = self.handleRequest(xml)
     2158        self.assertFailure(d, error.StanzaError)
     2159        d.addCallback(cb)
     2160        return d
     2161
     2162
     2163    def test_unsubscribe(self):
     2164        """
     2165        Non-overridden L{PubSubService.unsubscribe} yields unsupported error.
     2166        """
     2167
     2168        xml = """
     2169        <iq type='set' to='pubsub.example.org'
     2170                       from='user@example.org'>
     2171          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     2172            <unsubscribe node='test' jid='user@example.org/Home'/>
     2173          </pubsub>
     2174        </iq>
     2175        """
     2176
     2177        def cb(result):
     2178            self.assertEquals('feature-not-implemented', result.condition)
     2179            self.assertEquals('unsupported', result.appCondition.name)
     2180            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     2181            self.assertEquals('subscribe', result.appCondition['feature'])
     2182
     2183        d = self.handleRequest(xml)
     2184        self.assertFailure(d, error.StanzaError)
     2185        d.addCallback(cb)
     2186        return d
     2187
     2188
     2189    def test_subscriptions(self):
     2190        """
     2191        Non-overridden L{PubSubService.subscriptions} yields unsupported error.
     2192        """
     2193
     2194        xml = """
     2195        <iq type='get' to='pubsub.example.org'
     2196                       from='user@example.org'>
     2197          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     2198            <subscriptions/>
     2199          </pubsub>
     2200        </iq>
     2201        """
     2202
     2203        def cb(result):
     2204            self.assertEquals('feature-not-implemented', result.condition)
     2205            self.assertEquals('unsupported', result.appCondition.name)
     2206            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     2207            self.assertEquals('retrieve-subscriptions',
     2208                              result.appCondition['feature'])
     2209
     2210        d = self.handleRequest(xml)
     2211        self.assertFailure(d, error.StanzaError)
     2212        d.addCallback(cb)
     2213        return d
     2214
     2215
     2216    def test_affiliations(self):
     2217        """
     2218        Non-overridden L{PubSubService.affiliations} yields unsupported error.
     2219        """
     2220
     2221        xml = """
     2222        <iq type='get' to='pubsub.example.org'
     2223                       from='user@example.org'>
     2224          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     2225            <affiliations/>
     2226          </pubsub>
     2227        </iq>
     2228        """
     2229
     2230        def cb(result):
     2231            self.assertEquals('feature-not-implemented', result.condition)
     2232            self.assertEquals('unsupported', result.appCondition.name)
     2233            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     2234            self.assertEquals('retrieve-affiliations',
     2235                              result.appCondition['feature'])
     2236
     2237        d = self.handleRequest(xml)
     2238        self.assertFailure(d, error.StanzaError)
     2239        d.addCallback(cb)
     2240        return d
     2241
     2242
     2243    def test_create(self):
     2244        """
     2245        Non-overridden L{PubSubService.create} yields unsupported error.
     2246        """
     2247
     2248        xml = """
     2249        <iq type='set' to='pubsub.example.org'
     2250                       from='user@example.org'>
     2251          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     2252            <create node='mynode'/>
     2253          </pubsub>
     2254        </iq>
     2255        """
     2256
     2257        def cb(result):
     2258            self.assertEquals('feature-not-implemented', result.condition)
     2259            self.assertEquals('unsupported', result.appCondition.name)
     2260            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     2261            self.assertEquals('create-nodes', result.appCondition['feature'])
     2262
     2263        d = self.handleRequest(xml)
     2264        self.assertFailure(d, error.StanzaError)
     2265        d.addCallback(cb)
     2266        return d
     2267
     2268
     2269    def test_getDefaultConfiguration(self):
     2270        """
     2271        Non-overridden L{PubSubService.getDefaultConfiguration} yields
     2272        unsupported error.
     2273        """
     2274
     2275        xml = """
     2276        <iq type='get' to='pubsub.example.org'
     2277                       from='user@example.org'>
     2278          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
     2279            <default/>
     2280          </pubsub>
     2281        </iq>
     2282        """
     2283
     2284        def cb(result):
     2285            self.assertEquals('feature-not-implemented', result.condition)
     2286            self.assertEquals('unsupported', result.appCondition.name)
     2287            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     2288            self.assertEquals('retrieve-default', result.appCondition['feature'])
     2289
     2290        d = self.handleRequest(xml)
     2291        self.assertFailure(d, error.StanzaError)
     2292        d.addCallback(cb)
     2293        return d
     2294
     2295
     2296    def test_getConfiguration(self):
     2297        """
     2298        Non-overridden L{PubSubService.getConfiguration} yields unsupported
     2299        error.
     2300        """
     2301
     2302        xml = """
     2303        <iq type='get' to='pubsub.example.org'
     2304                       from='user@example.org'>
     2305          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
     2306            <configure/>
     2307          </pubsub>
     2308        </iq>
     2309        """
     2310
     2311        def cb(result):
     2312            self.assertEquals('feature-not-implemented', result.condition)
     2313            self.assertEquals('unsupported', result.appCondition.name)
     2314            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     2315            self.assertEquals('config-node', result.appCondition['feature'])
     2316
     2317        d = self.handleRequest(xml)
     2318        self.assertFailure(d, error.StanzaError)
     2319        d.addCallback(cb)
     2320        return d
     2321
     2322
     2323    def test_setConfiguration(self):
     2324        """
     2325        Non-overridden L{PubSubService.setConfiguration} yields unsupported
     2326        error.
     2327        """
     2328
     2329        xml = """
     2330        <iq type='set' to='pubsub.example.org'
     2331                       from='user@example.org'>
     2332          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
     2333            <configure node='test'>
     2334              <x xmlns='jabber:x:data' type='submit'>
     2335                <field var='FORM_TYPE' type='hidden'>
     2336                  <value>http://jabber.org/protocol/pubsub#node_config</value>
     2337                </field>
     2338                <field var='pubsub#deliver_payloads'><value>0</value></field>
     2339                <field var='pubsub#persist_items'><value>1</value></field>
     2340              </x>
     2341            </configure>
     2342          </pubsub>
     2343        </iq>
     2344        """
     2345
     2346        def cb(result):
     2347            self.assertEquals('feature-not-implemented', result.condition)
     2348            self.assertEquals('unsupported', result.appCondition.name)
     2349            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     2350            self.assertEquals('config-node', result.appCondition['feature'])
     2351
     2352        d = self.handleRequest(xml)
     2353        self.assertFailure(d, error.StanzaError)
     2354        d.addCallback(cb)
     2355        return d
     2356
     2357
     2358    def test_items(self):
     2359        """
     2360        Non-overridden L{PubSubService.items} yields unsupported error.
     2361        """
     2362        xml = """
     2363        <iq type='get' to='pubsub.example.org'
     2364                       from='user@example.org'>
     2365          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     2366            <items node='test'/>
     2367          </pubsub>
     2368        </iq>
     2369        """
     2370
     2371        def cb(result):
     2372            self.assertEquals('feature-not-implemented', result.condition)
     2373            self.assertEquals('unsupported', result.appCondition.name)
     2374            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     2375            self.assertEquals('retrieve-items', result.appCondition['feature'])
     2376
     2377        d = self.handleRequest(xml)
     2378        self.assertFailure(d, error.StanzaError)
     2379        d.addCallback(cb)
     2380        return d
     2381
     2382
     2383    def test_retract(self):
     2384        """
     2385        Non-overridden L{PubSubService.retract} yields unsupported error.
     2386        """
     2387        xml = """
     2388        <iq type='set' to='pubsub.example.org'
     2389                       from='user@example.org'>
     2390          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     2391            <retract node='test'>
     2392              <item id='item1'/>
     2393              <item id='item2'/>
     2394            </retract>
     2395          </pubsub>
     2396        </iq>
     2397        """
     2398
     2399        def cb(result):
     2400            self.assertEquals('feature-not-implemented', result.condition)
     2401            self.assertEquals('unsupported', result.appCondition.name)
     2402            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     2403            self.assertEquals('retract-items', result.appCondition['feature'])
     2404
     2405        d = self.handleRequest(xml)
     2406        self.assertFailure(d, error.StanzaError)
     2407        d.addCallback(cb)
     2408        return d
     2409
     2410
     2411    def test_purge(self):
     2412        """
     2413        Non-overridden L{PubSubService.purge} yields unsupported error.
     2414        """
     2415        xml = """
     2416        <iq type='set' to='pubsub.example.org'
     2417                       from='user@example.org'>
     2418          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
     2419            <purge node='test'/>
     2420          </pubsub>
     2421        </iq>
     2422        """
     2423
     2424        def cb(result):
     2425            self.assertEquals('feature-not-implemented', result.condition)
     2426            self.assertEquals('unsupported', result.appCondition.name)
     2427            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     2428            self.assertEquals('purge-nodes', result.appCondition['feature'])
     2429
     2430        d = self.handleRequest(xml)
     2431        self.assertFailure(d, error.StanzaError)
     2432        d.addCallback(cb)
     2433        return d
     2434
     2435
     2436    def test_delete(self):
     2437        """
     2438        Non-overridden L{PubSubService.delete} yields unsupported error.
     2439        """
     2440        xml = """
     2441        <iq type='set' to='pubsub.example.org'
     2442                       from='user@example.org'>
     2443          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
     2444            <delete node='test'/>
     2445          </pubsub>
     2446        </iq>
     2447        """
     2448
     2449        def cb(result):
     2450            self.assertEquals('feature-not-implemented', result.condition)
     2451            self.assertEquals('unsupported', result.appCondition.name)
     2452            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     2453            self.assertEquals('delete-nodes', result.appCondition['feature'])
     2454
     2455        d = self.handleRequest(xml)
     2456        self.assertFailure(d, error.StanzaError)
     2457        d.addCallback(cb)
     2458        return d
Note: See TracBrowser for help on using the repository browser.