~landscape/zope3/ztk-1.1.3

« back to all changes in this revision

Viewing changes to src/twisted/words/protocols/jabber/error.py

  • Committer: Andreas Hasenack
  • Date: 2009-07-20 17:49:16 UTC
  • Revision ID: andreas@canonical.com-20090720174916-g2tn6qmietz2hn0u
Revert twisted removal, it breaks several dozen tests [trivial]

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- test-case-name: twisted.words.test.test_jabbererror -*-
 
2
#
 
3
# Copyright (c) 2001-2006 Twisted Matrix Laboratories.
 
4
# See LICENSE for details.
 
5
 
 
6
"""
 
7
XMPP Error support.
 
8
"""
 
9
 
 
10
from twisted.words.xish import domish
 
11
 
 
12
NS_XML = "http://www.w3.org/XML/1998/namespace"
 
13
NS_XMPP_STANZAS = "urn:ietf:params:xml:ns:xmpp-stanzas"
 
14
 
 
15
STANZA_CONDITIONS = {
 
16
    'bad-request':              {'code': '400', 'type': 'modify'},
 
17
    'conflict':                 {'code': '409', 'type': 'cancel'},
 
18
    'feature-not-implemented':  {'code': '501', 'type': 'cancel'},
 
19
    'forbidden':                {'code': '403', 'type': 'auth'},
 
20
    'gone':                     {'code': '302', 'type': 'modify'},
 
21
    'internal-server-error':    {'code': '500', 'type': 'wait'},
 
22
    'item-not-found':           {'code': '404', 'type': 'cancel'},
 
23
    'jid-malformed':            {'code': '400', 'type': 'modify'},
 
24
    'not-acceptable':           {'code': '406', 'type': 'modify'},
 
25
    'not-allowed':              {'code': '405', 'type': 'cancel'},
 
26
    'not-authorized':           {'code': '401', 'type': 'auth'},
 
27
    'payment-required':         {'code': '402', 'type': 'auth'},
 
28
    'recipient-unavailable':    {'code': '404', 'type': 'wait'},
 
29
    'redirect':                 {'code': '302', 'type': 'modify'},
 
30
    'registration-required':    {'code': '407', 'type': 'auth'},
 
31
    'remote-server-not-found':  {'code': '404', 'type': 'cancel'},
 
32
    'remove-server-timeout':    {'code': '504', 'type': 'wait'},
 
33
    'resource-constraint':      {'code': '500', 'type': 'wait'},
 
34
    'service-unavailable':      {'code': '503', 'type': 'cancel'},
 
35
    'subscription-required':    {'code': '407', 'type': 'auth'},
 
36
    'undefined-condition':      {'code': '500', 'type': None},
 
37
    'unexpected-request':       {'code': '400', 'type': 'wait'},
 
38
}
 
39
 
 
40
CODES_TO_CONDITIONS = {
 
41
    '302': ('gone', 'modify'),
 
42
    '400': ('bad-request', 'modify'),
 
43
    '401': ('not-authorized', 'auth'),
 
44
    '402': ('payment-required', 'auth'),
 
45
    '403': ('forbidden', 'auth'),
 
46
    '404': ('item-not-found', 'cancel'),
 
47
    '405': ('not-allowed', 'cancel'),
 
48
    '406': ('not-acceptable', 'modify'),
 
49
    '407': ('registration-required', 'auth'),
 
50
    '408': ('remote-server-timeout', 'wait'),
 
51
    '409': ('conflict', 'cancel'),
 
52
    '500': ('internal-server-error', 'wait'),
 
53
    '501': ('feature-not-implemented', 'cancel'),
 
54
    '502': ('service-unavailable', 'wait'),
 
55
    '503': ('service-unavailable', 'cancel'),
 
56
    '504': ('remote-server-timeout', 'wait'),
 
57
    '510': ('service-unavailable', 'cancel'),
 
58
}
 
59
 
 
60
class Error(Exception):
 
61
    """
 
62
    Generic XMPP error exception.
 
63
    """
 
64
 
 
65
    def __init__(self, condition, text=None, textLang=None, appCondition=None):
 
66
        Exception.__init__(self)
 
67
        self.condition = condition
 
68
        self.text = text
 
69
        self.textLang = textLang
 
70
        self.appCondition = appCondition
 
71
 
 
72
 
 
73
    def __str__(self):
 
74
        message = "%s with condition %r" % (self.__class__.__name__,
 
75
                                            self.condition)
 
76
 
 
77
        if self.text:
 
78
            message += ': ' + self.text
 
79
 
 
80
        return message
 
81
 
 
82
 
 
83
    def getElement(self):
 
84
        """
 
85
        Get XML representation from self.
 
86
 
 
87
        The method creates an L{domish} representation of the
 
88
        error data contained in this exception.
 
89
 
 
90
        @rtype: L{domish.Element}
 
91
        """
 
92
        error = domish.Element((None, 'error'))
 
93
        error.addElement((NS_XMPP_STANZAS, self.condition))
 
94
        if self.text:
 
95
            text = error.addElement((NS_XMPP_STANZAS, 'text'),
 
96
                                    content=self.text)
 
97
            if self.textLang:
 
98
                text[(NS_XML, 'lang')] = self.textLang
 
99
        if self.appCondition:
 
100
            error.addChild(self.appCondition)
 
101
        return error
 
102
 
 
103
 
 
104
 
 
105
class StreamError(Error):
 
106
    """
 
107
    Stream Error exception.
 
108
    """
 
109
 
 
110
    def getElement(self):
 
111
        """
 
112
        Get XML representation from self.
 
113
 
 
114
        Overrides the base L{Error.getElement} to make sure the returned
 
115
        element is in the XML Stream namespace.
 
116
 
 
117
        @rtype: L{domish.Element}
 
118
        """
 
119
        from twisted.words.protocols.jabber.xmlstream import NS_STREAMS
 
120
 
 
121
        error = Error.getElement(self)
 
122
        error.uri = NS_STREAMS
 
123
        return error
 
124
 
 
125
 
 
126
 
 
127
class StanzaError(Error):
 
128
    """
 
129
    Stanza Error exception.
 
130
    """
 
131
 
 
132
    def __init__(self, condition, type=None, text=None, textLang=None,
 
133
                       appCondition=None):
 
134
        Error.__init__(self, condition, text, textLang, appCondition)
 
135
 
 
136
        if type is None:
 
137
            try:
 
138
                type = STANZA_CONDITIONS[condition]['type']
 
139
            except KeyError:
 
140
                pass
 
141
        self.type = type
 
142
 
 
143
        try:
 
144
            self.code = STANZA_CONDITIONS[condition]['code']
 
145
        except KeyError:
 
146
            self.code = None
 
147
 
 
148
        self.children = []
 
149
        self.iq = None
 
150
 
 
151
 
 
152
    def getElement(self):
 
153
        """
 
154
        Get XML representation from self.
 
155
 
 
156
        Overrides the base L{Error.getElement} to make sure the returned
 
157
        element has a C{type} attribute and optionally a legacy C{code}
 
158
        attribute.
 
159
 
 
160
        @rtype: L{domish.Element}
 
161
        """
 
162
        error = Error.getElement(self)
 
163
        error['type'] = self.type
 
164
        if self.code:
 
165
            error['code'] = self.code
 
166
        return error
 
167
 
 
168
 
 
169
    def toResponse(self, stanza):
 
170
        """
 
171
        Construct error response stanza.
 
172
 
 
173
        The C{stanza} is transformed into an error response stanza by
 
174
        swapping the C{to} and C{from} addresses and inserting an error
 
175
        element.
 
176
 
 
177
        @param stanza: the stanza to respond to
 
178
        @type stanza: L{domish.Element}
 
179
        """
 
180
        if stanza.getAttribute('to'):
 
181
            stanza.swapAttributeValues('to', 'from')
 
182
        stanza['type'] = 'error'
 
183
        stanza.addChild(self.getElement())
 
184
        return stanza
 
185
 
 
186
 
 
187
 
 
188
def _getText(element):
 
189
    for child in element.children:
 
190
        if isinstance(child, basestring):
 
191
            return unicode(child)
 
192
 
 
193
    return None
 
194
 
 
195
 
 
196
 
 
197
def _parseError(error):
 
198
    """
 
199
    Parses an error element.
 
200
 
 
201
    @param error: the error element to be parsed
 
202
    @type error: L{domish.Element}
 
203
    @return: dictionary with extracted error information. If present, keys
 
204
             C{condition}, C{text}, C{textLang} have a string value,
 
205
             and C{appCondition} has an L{domish.Element} value.
 
206
    @rtype: L{dict}
 
207
    """
 
208
    condition = None
 
209
    text = None
 
210
    textLang = None
 
211
    appCondition = None
 
212
 
 
213
    for element in error.elements():
 
214
        if element.uri == NS_XMPP_STANZAS:
 
215
            if element.name == 'text':
 
216
                text = _getText(element)
 
217
                textLang = element.getAttribute((NS_XML, 'lang'))
 
218
            else:
 
219
                condition = element.name
 
220
        else:
 
221
            appCondition = element
 
222
 
 
223
    return {
 
224
        'condition': condition,
 
225
        'text': text,
 
226
        'textLang': textLang,
 
227
        'appCondition': appCondition,
 
228
    }
 
229
 
 
230
 
 
231
 
 
232
def exceptionFromStreamError(element):
 
233
    """
 
234
    Build an exception object from a stream error.
 
235
 
 
236
    @param element: the stream error
 
237
    @type element: L{domish.Element}
 
238
    @return: the generated exception object
 
239
    @rtype: L{StreamError}
 
240
    """
 
241
    error = _parseError(element)
 
242
 
 
243
    exception = StreamError(error['condition'],
 
244
                            error['text'],
 
245
                            error['textLang'],
 
246
                            error['appCondition'])
 
247
 
 
248
    return exception
 
249
 
 
250
 
 
251
 
 
252
def exceptionFromStanza(stanza):
 
253
    """
 
254
    Build an exception object from an error stanza.
 
255
 
 
256
    @param stanza: the error stanza
 
257
    @type stanza: L{domish.Element}
 
258
    @return: the generated exception object
 
259
    @rtype: L{StanzaError}
 
260
    """
 
261
    children = []
 
262
    condition = text = textLang = appCondition = type = code = None
 
263
 
 
264
    for element in stanza.elements():
 
265
        if element.name == 'error' and element.uri == stanza.uri:
 
266
            code = element.getAttribute('code')
 
267
            type = element.getAttribute('type')
 
268
            error = _parseError(element)
 
269
            condition = error['condition']
 
270
            text = error['text']
 
271
            textLang = error['textLang']
 
272
            appCondition = error['appCondition']
 
273
 
 
274
            if not condition and code:
 
275
               condition, type = CODES_TO_CONDITIONS[code]
 
276
               text = _getText(stanza.error)
 
277
        else:
 
278
            children.append(element)
 
279
 
 
280
    if condition is None:
 
281
        # TODO: raise exception instead?
 
282
        return StanzaError(None)
 
283
 
 
284
    exception = StanzaError(condition, type, text, textLang, appCondition)
 
285
 
 
286
    exception.children = children
 
287
    exception.stanza = stanza
 
288
 
 
289
    return exception