1
# -*- test-case-name: twisted.words.test.test_jabbererror -*-
3
# Copyright (c) 2001-2006 Twisted Matrix Laboratories.
4
# See LICENSE for details.
10
from twisted.words.xish import domish
12
NS_XML = "http://www.w3.org/XML/1998/namespace"
13
NS_XMPP_STANZAS = "urn:ietf:params:xml:ns:xmpp-stanzas"
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'},
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'),
60
class Error(Exception):
62
Generic XMPP error exception.
65
def __init__(self, condition, text=None, textLang=None, appCondition=None):
66
Exception.__init__(self)
67
self.condition = condition
69
self.textLang = textLang
70
self.appCondition = appCondition
74
message = "%s with condition %r" % (self.__class__.__name__,
78
message += ': ' + self.text
85
Get XML representation from self.
87
The method creates an L{domish} representation of the
88
error data contained in this exception.
90
@rtype: L{domish.Element}
92
error = domish.Element((None, 'error'))
93
error.addElement((NS_XMPP_STANZAS, self.condition))
95
text = error.addElement((NS_XMPP_STANZAS, 'text'),
98
text[(NS_XML, 'lang')] = self.textLang
100
error.addChild(self.appCondition)
105
class StreamError(Error):
107
Stream Error exception.
110
def getElement(self):
112
Get XML representation from self.
114
Overrides the base L{Error.getElement} to make sure the returned
115
element is in the XML Stream namespace.
117
@rtype: L{domish.Element}
119
from twisted.words.protocols.jabber.xmlstream import NS_STREAMS
121
error = Error.getElement(self)
122
error.uri = NS_STREAMS
127
class StanzaError(Error):
129
Stanza Error exception.
132
def __init__(self, condition, type=None, text=None, textLang=None,
134
Error.__init__(self, condition, text, textLang, appCondition)
138
type = STANZA_CONDITIONS[condition]['type']
144
self.code = STANZA_CONDITIONS[condition]['code']
152
def getElement(self):
154
Get XML representation from self.
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}
160
@rtype: L{domish.Element}
162
error = Error.getElement(self)
163
error['type'] = self.type
165
error['code'] = self.code
169
def toResponse(self, stanza):
171
Construct error response stanza.
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
177
@param stanza: the stanza to respond to
178
@type stanza: L{domish.Element}
180
if stanza.getAttribute('to'):
181
stanza.swapAttributeValues('to', 'from')
182
stanza['type'] = 'error'
183
stanza.addChild(self.getElement())
188
def _getText(element):
189
for child in element.children:
190
if isinstance(child, basestring):
191
return unicode(child)
197
def _parseError(error):
199
Parses an error element.
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.
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'))
219
condition = element.name
221
appCondition = element
224
'condition': condition,
226
'textLang': textLang,
227
'appCondition': appCondition,
232
def exceptionFromStreamError(element):
234
Build an exception object from a stream error.
236
@param element: the stream error
237
@type element: L{domish.Element}
238
@return: the generated exception object
239
@rtype: L{StreamError}
241
error = _parseError(element)
243
exception = StreamError(error['condition'],
246
error['appCondition'])
252
def exceptionFromStanza(stanza):
254
Build an exception object from an error stanza.
256
@param stanza: the error stanza
257
@type stanza: L{domish.Element}
258
@return: the generated exception object
259
@rtype: L{StanzaError}
262
condition = text = textLang = appCondition = type = code = None
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']
271
textLang = error['textLang']
272
appCondition = error['appCondition']
274
if not condition and code:
275
condition, type = CODES_TO_CONDITIONS[code]
276
text = _getText(stanza.error)
278
children.append(element)
280
if condition is None:
281
# TODO: raise exception instead?
282
return StanzaError(None)
284
exception = StanzaError(condition, type, text, textLang, appCondition)
286
exception.children = children
287
exception.stanza = stanza