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

« back to all changes in this revision

Viewing changes to twisted/web2/dav/method/copymove.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_copy,twisted.web2.dav.test.test_move -*-
 
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 COPY and MOVE methods.
 
28
"""
 
29
 
 
30
__all__ = ["http_COPY", "http_MOVE"]
 
31
 
 
32
from twisted.python import log
 
33
from twisted.web2 import responsecode
 
34
from twisted.web2.http import HTTPError, StatusResponse
 
35
from twisted.web2.filter.location import addLocation
 
36
from twisted.web2.dav.idav import IDAVResource
 
37
from twisted.web2.dav.fileop import copy, move
 
38
 
 
39
# FIXME: This is circular
 
40
import twisted.web2.dav.static
 
41
 
 
42
def http_COPY(self, request):
 
43
    """
 
44
    Respond to a COPY request. (RFC 2518, section 8.8)
 
45
    """
 
46
    def doCopy(r):
 
47
        destination, destination_uri, depth = r
 
48
 
 
49
        # May need to add a location header
 
50
        addLocation(request, destination_uri)
 
51
 
 
52
        return copy(self.fp, destination.fp, destination_uri, depth)
 
53
 
 
54
    d = prepareForCopy(self, request)
 
55
    d.addCallback(doCopy)
 
56
    return d
 
57
 
 
58
def http_MOVE(self, request):
 
59
    """
 
60
    Respond to a MOVE request. (RFC 2518, section 8.9)
 
61
    """
 
62
    def doMove(r):
 
63
        destination, destination_uri, depth = r
 
64
 
 
65
        #
 
66
        # RFC 2518, section 8.9 says that we must act as if the Depth header is set
 
67
        # to infinity, and that the client must omit the Depth header or set it to
 
68
        # infinity.
 
69
        #
 
70
        # This seems somewhat at odds with the notion that a bad request should be
 
71
        # rejected outright; if the client sends a bad depth header, the client is
 
72
        # broken, and section 8 suggests that a bad request should be rejected...
 
73
        #
 
74
        # Let's play it safe for now and ignore broken clients.
 
75
        #
 
76
        if self.fp.isdir() and depth != "infinity":
 
77
            msg = "Client sent illegal depth header value for MOVE: %s" % (depth,)
 
78
            log.err(msg)
 
79
            raise HTTPError(StatusResponse(responsecode.BAD_REQUEST, msg))
 
80
 
 
81
        # May need to add a location header
 
82
        addLocation(request, destination_uri)
 
83
 
 
84
        return move(self.fp, request.uri, destination.fp, destination_uri, depth)
 
85
 
 
86
    d = prepareForCopy(self, request)
 
87
    d.addCallback(doMove)
 
88
    return d
 
89
 
 
90
def prepareForCopy(self, request):
 
91
    #
 
92
    # Get the depth
 
93
    #
 
94
 
 
95
    depth = request.headers.getHeader("depth", "infinity")
 
96
 
 
97
    if depth not in ("0", "infinity"):
 
98
        msg = ("Client sent illegal depth header value: %s" % (depth,))
 
99
        log.err(msg)
 
100
        raise HTTPError(StatusResponse(responsecode.BAD_REQUEST, msg))
 
101
 
 
102
    #
 
103
    # Verify this resource exists
 
104
    #
 
105
 
 
106
    if not self.exists():
 
107
        log.err("File not found: %s" % (self.fp.path,))
 
108
        raise HTTPError(StatusResponse(
 
109
            responsecode.NOT_FOUND,
 
110
            "Source resource %s not found." % (request.uri,)
 
111
        ))
 
112
 
 
113
    #
 
114
    # Get the destination
 
115
    #
 
116
 
 
117
    destination_uri = request.headers.getHeader("destination")
 
118
 
 
119
    if not destination_uri:
 
120
        msg = "No destination header in %s request." % (request.method,)
 
121
        log.err(msg)
 
122
        raise HTTPError(StatusResponse(responsecode.BAD_REQUEST, msg))
 
123
 
 
124
    d = request.locateResource(destination_uri)
 
125
    d.addCallback(_prepareForCopy, destination_uri, request, depth)
 
126
 
 
127
    return d
 
128
 
 
129
def _prepareForCopy(destination, destination_uri, request, depth):
 
130
    #
 
131
    # Destination must be a DAV resource
 
132
    #
 
133
 
 
134
    try:
 
135
        destination = IDAVResource(destination)
 
136
    except TypeError:
 
137
        log.err("Attempt to %s to a non-DAV resource: (%s) %s"
 
138
                % (request.method, destination.__class__, destination_uri))
 
139
        raise HTTPError(StatusResponse(
 
140
            responsecode.FORBIDDEN,
 
141
            "Destination %s is not a WebDAV resource." % (destination_uri,)
 
142
        ))
 
143
 
 
144
    #
 
145
    # FIXME: Right now we don't know how to copy to a non-DAVFile resource.
 
146
    # We may need some more API in IDAVResource.
 
147
    # So far, we need: .exists(), .fp.parent()
 
148
    #
 
149
 
 
150
    if not isinstance(destination, twisted.web2.dav.static.DAVFile):
 
151
        log.err("DAV copy between non-DAVFile DAV resources isn't implemented")
 
152
        raise HTTPError(StatusResponse(
 
153
            responsecode.NOT_IMPLEMENTED,
 
154
            "Destination %s is not a DAVFile resource." % (destination_uri,)
 
155
        ))
 
156
 
 
157
    #
 
158
    # Check for existing destination resource
 
159
    #
 
160
 
 
161
    overwrite = request.headers.getHeader("overwrite", True)
 
162
 
 
163
    if destination.exists() and not overwrite:
 
164
        log.err("Attempt to %s onto existing file without overwrite flag enabled: %s"
 
165
                % (request.method, destination.fp.path))
 
166
        raise HTTPError(StatusResponse(
 
167
            responsecode.PRECONDITION_FAILED,
 
168
            "Destination %s already exists." % (destination_uri,)
 
169
        ))
 
170
 
 
171
    #
 
172
    # Make sure destination's parent exists
 
173
    #
 
174
 
 
175
    if not destination.fp.parent().isdir():
 
176
        log.err("Attempt to %s to a resource with no parent: %s"
 
177
                % (request.method, destination.fp.path))
 
178
        raise HTTPError(StatusResponse(responsecode.CONFLICT, "No parent collection."))
 
179
 
 
180
    return destination, destination_uri, depth