diff idavoll/pubsub.py @ 159:6fe78048baf9

Rework error handling, depend on Twisted Words 0.4.0. Twisted Words 0.4.0 introduced support for stanza error handling, much better than the custom error handling in Idavoll. Also, all protocol-level errors were examined and brought up to date with version 1.8 of JEP-0060. As a result of the error examination, the retrieval of default configuration options using <default/> is now supported properly.
author Ralph Meijer <ralphm@ik.nu>
date Wed, 06 Sep 2006 12:38:47 +0000
parents 5191ba7c4df8
children 40d931ed15b9
line wrap: on
line diff
--- a/idavoll/pubsub.py	Thu Jun 18 11:52:01 2009 +0000
+++ b/idavoll/pubsub.py	Wed Sep 06 12:38:47 2006 +0000
@@ -1,7 +1,7 @@
 # Copyright (c) 2003-2006 Ralph Meijer
 # See LICENSE for details.
 
-from twisted.words.protocols.jabber import component,jid
+from twisted.words.protocols.jabber import component, jid, error
 from twisted.words.xish import utility, domish
 from twisted.python import components
 from twisted.internet import defer
@@ -9,7 +9,6 @@
 
 import backend
 import storage
-import xmpp_error
 import disco
 import data_form
 
@@ -40,6 +39,7 @@
 PUBSUB_UNSUBSCRIBE = PUBSUB_SET + '/unsubscribe'
 PUBSUB_OPTIONS_GET = PUBSUB_GET + '/options'
 PUBSUB_OPTIONS_SET = PUBSUB_SET + '/options'
+PUBSUB_DEFAULT = PUBSUB_OWNER_GET + '/default'
 PUBSUB_CONFIGURE_GET = PUBSUB_OWNER_GET + '/configure'
 PUBSUB_CONFIGURE_SET = PUBSUB_OWNER_SET + '/configure'
 PUBSUB_SUBSCRIPTIONS = PUBSUB_GET + '/subscriptions'
@@ -49,37 +49,39 @@
 PUBSUB_PURGE = PUBSUB_OWNER_SET + '/purge'
 PUBSUB_DELETE = PUBSUB_OWNER_SET + '/delete'
 
-class Error(Exception):
-    pubsub_error = None
-    stanza_error = None
-    msg = ''
-
-class NotImplemented(Error):
-    stanza_error = 'feature-not-implemented'
+class BadRequest(error.StanzaError):
+    def __init__(self):
+        error.StanzaError.__init__(self, 'bad-request')
 
-class BadRequest(Error):
-    stanza_error = 'bad-request'
+class PubSubError(error.StanzaError):
+    def __init__(self, condition, pubsubCondition, feature=None, text=None):
+        appCondition = domish.Element((NS_PUBSUB_ERRORS, pubsubCondition))
+        if feature:
+            appCondition['feature'] = feature
+        error.StanzaError.__init__(self, condition,
+                                         text=text, 
+                                         appCondition=appCondition)
 
-class OptionsUnavailable(Error):
-    stanza_error = 'feature-not-implemented'
-    pubsub_error = 'subscription-options-unavailable'
-
-class NodeNotConfigurable(Error):
-    stanza_error = 'feature-not-implemented'
-    pubsub_error = 'node-not-configurable'
+class OptionsUnavailable(PubSubError):
+    def __init__(self):
+        PubSubError.__init__(self, 'feature-not-implemented',
+                                   'unsupported',
+                                   'subscription-options-unavailable')
 
 error_map = {
-    storage.NodeNotFound: ('item-not-found', None),
-    storage.NodeExists: ('conflict', None),
-    storage.SubscriptionNotFound: ('not-authorized',
-                                   'not-subscribed'),
-    backend.NotAuthorized: ('not-authorized', None),
-    backend.NoPayloadAllowed: ('bad-request', None),
-    backend.PayloadExpected: ('bad-request', None),
-    backend.NoInstantNodes: ('not-acceptable', None),
-    backend.NotImplemented: ('feature-not-implemented', None),
-    backend.InvalidConfigurationOption: ('not-acceptable', None),
-    backend.InvalidConfigurationValue: ('not-acceptable', None),
+    storage.NodeNotFound: ('item-not-found', None, None),
+    storage.NodeExists: ('conflict', None, None),
+    storage.SubscriptionNotFound: ('not-authorized', 'not-subscribed', None),
+    backend.Forbidden: ('forbidden', None, None),
+    backend.ItemForbidden: ('bad-request', 'item-forbidden', None),
+    backend.ItemRequired: ('bad-request', 'item-required', None),
+    backend.NoInstantNodes: ('not-acceptable', 'unsupported', 'instant-nodes'),
+    backend.NotSubscribed: ('not-authorized', 'not-subscribed', None),
+    backend.InvalidConfigurationOption: ('not-acceptable', None, None),
+    backend.InvalidConfigurationValue: ('not-acceptable', None, None),
+    backend.NodeNotPersistent: ('feature-not-implemented', 'unsupported',
+                                                           'persistent-node'),
+    backend.NoRootNode: ('bad-request', None, None),
 }
 
 class Service(component.Service):
@@ -91,24 +93,23 @@
 
     def error(self, failure, iq):
         try: 
-            e = failure.trap(Error, *error_map.keys())
+            e = failure.trap(error.StanzaError, *error_map.keys())
         except:
             failure.printBriefTraceback()
-            xmpp_error.error_from_iq(iq, 'internal-server-error')
-            return iq
+            return error.StanzaError('internal-server-error').toResponse(iq)
         else:
-            if e == Error:
-                stanza_error = failure.value.stanza_error
-                pubsub_error = failure.value.pubsub_error
-                msg = ''
+            if e == error.StanzaError:
+                exc = failure.value
             else:
-                stanza_error, pubsub_error = error_map[e]
+                condition, pubsubCondition, feature = error_map[e]
                 msg = failure.value.msg
 
-            xmpp_error.error_from_iq(iq, stanza_error, msg)
-            if pubsub_error:
-                iq.error.addElement((NS_PUBSUB_ERRORS, pubsub_error))
-            return iq
+                if pubsubCondition:
+                    exc = PubSubError(condition, pubsubCondition, feature, msg)
+                else:
+                    exc = error.StanzaError(condition, text=msg)
+
+            return exc.toResponse(iq)
     
     def success(self, result, iq):
         iq.swapAttributeValues("to", "from")
@@ -362,6 +363,7 @@
 
     def componentConnected(self, xmlstream):
         xmlstream.addObserver(PUBSUB_CREATE, self.onCreate)
+        xmlstream.addObserver(PUBSUB_DEFAULT, self.onDefault)
         xmlstream.addObserver(PUBSUB_CONFIGURE_GET, self.onConfigureGet)
         xmlstream.addObserver(PUBSUB_CONFIGURE_SET, self.onConfigureSet)
 
@@ -371,6 +373,7 @@
         if not node:
             info.append(disco.Feature(NS_PUBSUB + "#create-nodes"))
             info.append(disco.Feature(NS_PUBSUB + "#config-node"))
+            info.append(disco.Feature(NS_PUBSUB + "#retrieve-default"))
 
             if self.backend.supports_instant_nodes():
                 info.append(disco.Feature(NS_PUBSUB + "#instant-nodes"))
@@ -378,6 +381,7 @@
         return defer.succeed(info)
 
     def onCreate(self, iq):
+        print "onCreate"
         self.handler_wrapper(self._onCreate, iq)
 
     def _onCreate(self, iq):
@@ -397,14 +401,26 @@
             entity['node'] = result
             return [reply]
 
+    def onDefault(self, iq):
+        self.handler_wrapper(self._onDefault, iq)
+
+    def _onDefault(self, iq):
+        d = self.backend.get_default_configuration()
+        d.addCallback(self._return_default_response)
+        return d
+        
+    def _return_default_response(self, options):
+        reply = domish.Element((NS_PUBSUB_OWNER, "pubsub"))
+        default = reply.addElement("default")
+        default.addChild(self._form_from_configuration(options))
+
+        return [reply]
+
     def onConfigureGet(self, iq):
         self.handler_wrapper(self._onConfigureGet, iq)
 
     def _onConfigureGet(self, iq):
-        try:
-            node_id = iq.pubsub.configure["node"]
-        except KeyError:
-            raise NodeNotConfigurable
+        node_id = iq.pubsub.configure.getAttribute("node")
 
         d = self.backend.get_node_configuration(node_id)
         d.addCallback(self._return_configuration_response, node_id)
@@ -415,26 +431,25 @@
         configure = reply.addElement("configure")
         if node_id:
             configure["node"] = node_id
+        configure.addChild(self._form_from_configuration(options))
+
+        return [reply]
+
+    def _form_from_configuration(self, options):
         form = data_form.Form(type="form",
                               form_type=NS_PUBSUB + "#node_config")
 
         for option in options:
             form.add_field(**option)
 
-        form.parent = configure
-        configure.addChild(form)
-
-        return [reply]
+        return form
 
     def onConfigureSet(self, iq):
+        print "onConfigureSet"
         self.handler_wrapper(self._onConfigureSet, iq)
 
     def _onConfigureSet(self, iq):
-        try:
-            node_id = iq.pubsub.configure["node"]
-        except KeyError:
-            raise BadRequest
-
+        node_id = iq.pubsub.configure["node"]
         requestor = jid.internJID(iq["from"]).userhostJID()
 
         for element in iq.pubsub.configure.elements():