~certify-web-dev/twisted/certify-trunk

« back to all changes in this revision

Viewing changes to twisted/web2/dav/method/proppatch.py

  • Committer: Bazaar Package Importer
  • Author(s): Matthias Klose
  • Date: 2007-01-17 14:52:35 UTC
  • mfrom: (1.1.5 upstream) (2.1.2 etch)
  • Revision ID: james.westby@ubuntu.com-20070117145235-btmig6qfmqfen0om
Tags: 2.5.0-0ubuntu1
New upstream version, compatible with python2.5.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- test-case-name: twisted.web2.dav.test.test_prop.PROP.test_PROPPATCH -*-
 
2
##
 
3
# Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
 
4
#
 
5
# Permission is hereby granted, free of charge, to any person obtaining a copy
 
6
# of this software and associated documentation files (the "Software"), to deal
 
7
# in the Software without restriction, including without limitation the rights
 
8
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 
9
# copies of the Software, and to permit persons to whom the Software is
 
10
# furnished to do so, subject to the following conditions:
 
11
 
12
# The above copyright notice and this permission notice shall be included in all
 
13
# copies or substantial portions of the Software.
 
14
 
15
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 
16
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 
17
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 
18
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 
19
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 
20
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
21
# SOFTWARE.
 
22
#
 
23
# DRI: Wilfredo Sanchez, wsanchez@apple.com
 
24
##
 
25
 
 
26
"""
 
27
WebDAV-aware static resources.
 
28
"""
 
29
 
 
30
__all__ = ["http_PROPPATCH"]
 
31
 
 
32
from twisted.python import log
 
33
from twisted.python.failure import Failure
 
34
from twisted.internet.defer import deferredGenerator, waitForDeferred
 
35
from twisted.web2 import responsecode
 
36
from twisted.web2.http import HTTPError, StatusResponse
 
37
from twisted.web2.dav import davxml
 
38
from twisted.web2.dav.http import MultiStatusResponse, PropertyStatusResponseQueue
 
39
from twisted.web2.dav.util import davXMLFromStream
 
40
 
 
41
def http_PROPPATCH(self, request):
 
42
    """
 
43
    Respond to a PROPPATCH request. (RFC 2518, section 8.2)
 
44
    """
 
45
    if not self.fp.exists():
 
46
        log.err("File not found: %s" % (self.fp.path,))
 
47
        raise HTTPError(responsecode.NOT_FOUND)
 
48
 
 
49
    #
 
50
    # Read request body
 
51
    #
 
52
    try:
 
53
        doc = waitForDeferred(davXMLFromStream(request.stream))
 
54
        yield doc
 
55
        doc = doc.getResult()
 
56
    except ValueError, e:
 
57
        log.err("Error while handling PROPPATCH body: %s" % (e,))
 
58
        raise HTTPError(StatusResponse(responsecode.BAD_REQUEST, str(e)))
 
59
 
 
60
    if doc is None:
 
61
        error = "Request XML body is required."
 
62
        log.err(error)
 
63
        raise HTTPError(StatusResponse(responsecode.BAD_REQUEST, error))
 
64
 
 
65
    #
 
66
    # Parse request
 
67
    #
 
68
    update = doc.root_element
 
69
    if not isinstance(update, davxml.PropertyUpdate):
 
70
        error = ("Request XML body must be a propertyupdate element."
 
71
                 % (davxml.PropertyUpdate.sname(),))
 
72
        log.err(error)
 
73
        raise HTTPError(StatusResponse(responsecode.BAD_REQUEST, error))
 
74
 
 
75
    responses = PropertyStatusResponseQueue("PROPPATCH", request.uri, responsecode.NO_CONTENT)
 
76
    undoActions = []
 
77
    gotError = False
 
78
 
 
79
    try:
 
80
        #
 
81
        # Update properties
 
82
        #
 
83
        for setOrRemove in update.children:
 
84
            assert len(setOrRemove.children) == 1
 
85
 
 
86
            container = setOrRemove.children[0]
 
87
 
 
88
            assert isinstance(container, davxml.PropertyContainer)
 
89
 
 
90
            properties = container.children
 
91
 
 
92
            def do(action, property):
 
93
                """
 
94
                Perform action(property, request) while maintaining an
 
95
                undo queue.
 
96
                """
 
97
                has = waitForDeferred(self.hasProperty(property, request))
 
98
                yield has
 
99
                has = has.getResult()
 
100
 
 
101
                if has:
 
102
                    oldProperty = waitForDeferred(self.readProperty(property, request))
 
103
                    yield oldProperty
 
104
                    oldProperty.getResult()
 
105
 
 
106
                    def undo():
 
107
                        return self.writeProperty(oldProperty, request)
 
108
                else:
 
109
                    def undo():
 
110
                        return self.removeProperty(property, request)
 
111
 
 
112
                try:
 
113
                    x = waitForDeferred(action(property, request))
 
114
                    yield x
 
115
                    x.getResult()
 
116
                except ValueError, e:
 
117
                    # Convert ValueError exception into HTTPError
 
118
                    responses.add(
 
119
                        Failure(exc_value=HTTPError(StatusResponse(responsecode.FORBIDDEN, str(e)))),
 
120
                        property
 
121
                    )
 
122
                    yield False
 
123
                    return
 
124
                except:
 
125
                    responses.add(Failure(), property)
 
126
                    yield False
 
127
                    return
 
128
                else:
 
129
                    responses.add(responsecode.OK, property)
 
130
 
 
131
                    # Only add undo action for those that succeed because those that fail will not have changed               
 
132
                    undoActions.append(undo)
 
133
 
 
134
                    yield True
 
135
                    return
 
136
 
 
137
            do = deferredGenerator(do)
 
138
 
 
139
            if isinstance(setOrRemove, davxml.Set):
 
140
                for property in properties:
 
141
                    ok = waitForDeferred(do(self.writeProperty, property))
 
142
                    yield ok
 
143
                    ok = ok.getResult()
 
144
                    if not ok:
 
145
                        gotError = True
 
146
            elif isinstance(setOrRemove, davxml.Remove):
 
147
                for property in properties:
 
148
                    ok = waitForDeferred(do(self.removeProperty, property))
 
149
                    yield ok
 
150
                    ok = ok.getResult()
 
151
                    if not ok:
 
152
                        gotError = True
 
153
            else:
 
154
                raise AssertionError("Unknown child of PropertyUpdate: %s" % (setOrRemove,))
 
155
    except:
 
156
        #
 
157
        # If there is an error, we have to back out whatever we have
 
158
        # operations we have done because PROPPATCH is an
 
159
        # all-or-nothing request.
 
160
        # We handle the first one here, and then re-raise to handle the
 
161
        # rest in the containing scope.
 
162
        #
 
163
        for action in undoActions:
 
164
            x = waitForDeferred(action())
 
165
            yield x
 
166
            x.getResult()
 
167
        raise
 
168
 
 
169
    #
 
170
    # If we had an error we need to undo any changes that did succeed and change status of
 
171
    # those to 424 Failed Dependency.
 
172
    #
 
173
    if gotError:
 
174
        for action in undoActions:
 
175
            x = waitForDeferred(action())
 
176
            yield x
 
177
            x.getResult()
 
178
        responses.error()
 
179
 
 
180
    #
 
181
    # Return response
 
182
    #
 
183
    yield MultiStatusResponse([responses.response()])
 
184
 
 
185
http_PROPPATCH = deferredGenerator(http_PROPPATCH)