Statistics
| Branch: | Tag: | Revision:

root / pithos / api / functions.py @ 6749c3bd

History | View | Annotate | Download (33.4 kB)

1 5635f9ef Antony Chazapis
# Copyright 2011 GRNET S.A. All rights reserved.
2 5635f9ef Antony Chazapis
# 
3 5635f9ef Antony Chazapis
# Redistribution and use in source and binary forms, with or
4 5635f9ef Antony Chazapis
# without modification, are permitted provided that the following
5 5635f9ef Antony Chazapis
# conditions are met:
6 5635f9ef Antony Chazapis
# 
7 5635f9ef Antony Chazapis
#   1. Redistributions of source code must retain the above
8 5635f9ef Antony Chazapis
#      copyright notice, this list of conditions and the following
9 5635f9ef Antony Chazapis
#      disclaimer.
10 5635f9ef Antony Chazapis
# 
11 5635f9ef Antony Chazapis
#   2. Redistributions in binary form must reproduce the above
12 5635f9ef Antony Chazapis
#      copyright notice, this list of conditions and the following
13 5635f9ef Antony Chazapis
#      disclaimer in the documentation and/or other materials
14 5635f9ef Antony Chazapis
#      provided with the distribution.
15 5635f9ef Antony Chazapis
# 
16 5635f9ef Antony Chazapis
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 5635f9ef Antony Chazapis
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 5635f9ef Antony Chazapis
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 5635f9ef Antony Chazapis
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 5635f9ef Antony Chazapis
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 5635f9ef Antony Chazapis
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 5635f9ef Antony Chazapis
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 5635f9ef Antony Chazapis
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 5635f9ef Antony Chazapis
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 5635f9ef Antony Chazapis
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 5635f9ef Antony Chazapis
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 5635f9ef Antony Chazapis
# POSSIBILITY OF SUCH DAMAGE.
28 5635f9ef Antony Chazapis
# 
29 5635f9ef Antony Chazapis
# The views and conclusions contained in the software and
30 5635f9ef Antony Chazapis
# documentation are those of the authors and should not be
31 5635f9ef Antony Chazapis
# interpreted as representing official policies, either expressed
32 5635f9ef Antony Chazapis
# or implied, of GRNET S.A.
33 5635f9ef Antony Chazapis
34 b956618e Antony Chazapis
import os
35 b956618e Antony Chazapis
import logging
36 b956618e Antony Chazapis
import hashlib
37 b956618e Antony Chazapis
38 486b2dc2 Giorgos Verigakis
from django.conf import settings
39 b956618e Antony Chazapis
from django.http import HttpResponse
40 b956618e Antony Chazapis
from django.template.loader import render_to_string
41 b956618e Antony Chazapis
from django.utils import simplejson as json
42 b956618e Antony Chazapis
from django.utils.http import parse_etags
43 b956618e Antony Chazapis
44 b956618e Antony Chazapis
from pithos.api.faults import (Fault, NotModified, BadRequest, Unauthorized, ItemNotFound, Conflict,
45 b956618e Antony Chazapis
    LengthRequired, PreconditionFailed, RangeNotSatisfiable, UnprocessableEntity)
46 02c0c3fa Antony Chazapis
from pithos.api.util import (format_header_key, printable_header_dict, get_account_headers,
47 02c0c3fa Antony Chazapis
    put_account_headers, get_container_headers, put_container_headers, get_object_headers, put_object_headers,
48 e0f916bb Antony Chazapis
    update_manifest_meta, update_sharing_meta, update_public_meta, validate_modification_preconditions,
49 3436eeb0 Antony Chazapis
    validate_matching_preconditions, split_container_object_string, copy_or_move_object,
50 3ab38c43 Antony Chazapis
    get_int_parameter, get_content_length, get_content_range, raw_input_socket,
51 3436eeb0 Antony Chazapis
    socket_read_iterator, object_data_response, put_object_block, hashmap_hash, api_method)
52 b956618e Antony Chazapis
from pithos.backends import backend
53 cca6c617 Antony Chazapis
from pithos.backends.base import NotAllowedError
54 b956618e Antony Chazapis
55 b956618e Antony Chazapis
56 b956618e Antony Chazapis
logger = logging.getLogger(__name__)
57 b956618e Antony Chazapis
58 b956618e Antony Chazapis
59 b956618e Antony Chazapis
def top_demux(request):
60 b956618e Antony Chazapis
    if request.method == 'GET':
61 b956618e Antony Chazapis
        return authenticate(request)
62 b956618e Antony Chazapis
    else:
63 b956618e Antony Chazapis
        return method_not_allowed(request)
64 b956618e Antony Chazapis
65 b956618e Antony Chazapis
def account_demux(request, v_account):
66 b956618e Antony Chazapis
    if request.method == 'HEAD':
67 b956618e Antony Chazapis
        return account_meta(request, v_account)
68 b956618e Antony Chazapis
    elif request.method == 'POST':
69 b956618e Antony Chazapis
        return account_update(request, v_account)
70 83dd59c5 Antony Chazapis
    elif request.method == 'GET':
71 83dd59c5 Antony Chazapis
        return container_list(request, v_account)
72 b956618e Antony Chazapis
    else:
73 b956618e Antony Chazapis
        return method_not_allowed(request)
74 b956618e Antony Chazapis
75 b956618e Antony Chazapis
def container_demux(request, v_account, v_container):
76 b956618e Antony Chazapis
    if request.method == 'HEAD':
77 b956618e Antony Chazapis
        return container_meta(request, v_account, v_container)
78 b956618e Antony Chazapis
    elif request.method == 'PUT':
79 b956618e Antony Chazapis
        return container_create(request, v_account, v_container)
80 b956618e Antony Chazapis
    elif request.method == 'POST':
81 b956618e Antony Chazapis
        return container_update(request, v_account, v_container)
82 b956618e Antony Chazapis
    elif request.method == 'DELETE':
83 b956618e Antony Chazapis
        return container_delete(request, v_account, v_container)
84 83dd59c5 Antony Chazapis
    elif request.method == 'GET':
85 83dd59c5 Antony Chazapis
        return object_list(request, v_account, v_container)
86 b956618e Antony Chazapis
    else:
87 b956618e Antony Chazapis
        return method_not_allowed(request)
88 b956618e Antony Chazapis
89 b956618e Antony Chazapis
def object_demux(request, v_account, v_container, v_object):
90 b956618e Antony Chazapis
    if request.method == 'HEAD':
91 b956618e Antony Chazapis
        return object_meta(request, v_account, v_container, v_object)
92 b956618e Antony Chazapis
    elif request.method == 'GET':
93 b956618e Antony Chazapis
        return object_read(request, v_account, v_container, v_object)
94 b956618e Antony Chazapis
    elif request.method == 'PUT':
95 b956618e Antony Chazapis
        return object_write(request, v_account, v_container, v_object)
96 b956618e Antony Chazapis
    elif request.method == 'COPY':
97 b956618e Antony Chazapis
        return object_copy(request, v_account, v_container, v_object)
98 b956618e Antony Chazapis
    elif request.method == 'MOVE':
99 b956618e Antony Chazapis
        return object_move(request, v_account, v_container, v_object)
100 b956618e Antony Chazapis
    elif request.method == 'POST':
101 b956618e Antony Chazapis
        return object_update(request, v_account, v_container, v_object)
102 b956618e Antony Chazapis
    elif request.method == 'DELETE':
103 b956618e Antony Chazapis
        return object_delete(request, v_account, v_container, v_object)
104 b956618e Antony Chazapis
    else:
105 b956618e Antony Chazapis
        return method_not_allowed(request)
106 b956618e Antony Chazapis
107 b956618e Antony Chazapis
@api_method('GET')
108 b956618e Antony Chazapis
def authenticate(request):
109 b956618e Antony Chazapis
    # Normal Response Codes: 204
110 b956618e Antony Chazapis
    # Error Response Codes: serviceUnavailable (503),
111 b956618e Antony Chazapis
    #                       unauthorized (401),
112 b956618e Antony Chazapis
    #                       badRequest (400)
113 b956618e Antony Chazapis
    
114 b956618e Antony Chazapis
    x_auth_user = request.META.get('HTTP_X_AUTH_USER')
115 b956618e Antony Chazapis
    x_auth_key = request.META.get('HTTP_X_AUTH_KEY')
116 b956618e Antony Chazapis
    if not x_auth_user or not x_auth_key:
117 b956618e Antony Chazapis
        raise BadRequest('Missing X-Auth-User or X-Auth-Key header')
118 b956618e Antony Chazapis
    response = HttpResponse(status=204)
119 486b2dc2 Giorgos Verigakis
    inv_auth_tokens = dict((v, k) for k, v in settings.AUTH_TOKENS.items())
120 486b2dc2 Giorgos Verigakis
    response['X-Auth-Token'] = inv_auth_tokens.get(x_auth_user, '0000')
121 b956618e Antony Chazapis
    response['X-Storage-Url'] = os.path.join(request.build_absolute_uri(), 'demo')
122 b956618e Antony Chazapis
    return response
123 b956618e Antony Chazapis
124 b956618e Antony Chazapis
@api_method('HEAD')
125 b956618e Antony Chazapis
def account_meta(request, v_account):
126 b956618e Antony Chazapis
    # Normal Response Codes: 204
127 b956618e Antony Chazapis
    # Error Response Codes: serviceUnavailable (503),
128 b956618e Antony Chazapis
    #                       unauthorized (401),
129 b956618e Antony Chazapis
    #                       badRequest (400)
130 b956618e Antony Chazapis
    
131 83dd59c5 Antony Chazapis
    until = get_int_parameter(request, 'until')
132 cca6c617 Antony Chazapis
    try:
133 cca6c617 Antony Chazapis
        meta = backend.get_account_meta(request.user, v_account, until)
134 02c0c3fa Antony Chazapis
        groups = backend.get_account_groups(request.user, v_account)
135 cca6c617 Antony Chazapis
    except NotAllowedError:
136 cca6c617 Antony Chazapis
        raise Unauthorized('Access denied')
137 b956618e Antony Chazapis
    
138 b956618e Antony Chazapis
    response = HttpResponse(status=204)
139 02c0c3fa Antony Chazapis
    put_account_headers(response, meta, groups)
140 b956618e Antony Chazapis
    return response
141 b956618e Antony Chazapis
142 b956618e Antony Chazapis
@api_method('POST')
143 b956618e Antony Chazapis
def account_update(request, v_account):
144 b956618e Antony Chazapis
    # Normal Response Codes: 202
145 b956618e Antony Chazapis
    # Error Response Codes: serviceUnavailable (503),
146 b956618e Antony Chazapis
    #                       unauthorized (401),
147 b956618e Antony Chazapis
    #                       badRequest (400)
148 b956618e Antony Chazapis
    
149 02c0c3fa Antony Chazapis
    meta, groups = get_account_headers(request)
150 a6eb13e9 Antony Chazapis
    replace = True
151 a6eb13e9 Antony Chazapis
    if 'update' in request.GET:
152 02c0c3fa Antony Chazapis
        replace = False    
153 02c0c3fa Antony Chazapis
    if groups:
154 02c0c3fa Antony Chazapis
        try:
155 02c0c3fa Antony Chazapis
            backend.update_account_groups(request.user, v_account, groups, replace)
156 02c0c3fa Antony Chazapis
        except NotAllowedError:
157 02c0c3fa Antony Chazapis
            raise Unauthorized('Access denied')
158 02c0c3fa Antony Chazapis
        except ValueError:
159 02c0c3fa Antony Chazapis
            raise BadRequest('Invalid groups header')
160 cca6c617 Antony Chazapis
    try:
161 cca6c617 Antony Chazapis
        backend.update_account_meta(request.user, v_account, meta, replace)
162 cca6c617 Antony Chazapis
    except NotAllowedError:
163 cca6c617 Antony Chazapis
        raise Unauthorized('Access denied')
164 b956618e Antony Chazapis
    return HttpResponse(status=202)
165 b956618e Antony Chazapis
166 b956618e Antony Chazapis
@api_method('GET', format_allowed=True)
167 b956618e Antony Chazapis
def container_list(request, v_account):
168 b956618e Antony Chazapis
    # Normal Response Codes: 200, 204
169 b956618e Antony Chazapis
    # Error Response Codes: serviceUnavailable (503),
170 b956618e Antony Chazapis
    #                       itemNotFound (404),
171 b956618e Antony Chazapis
    #                       unauthorized (401),
172 b956618e Antony Chazapis
    #                       badRequest (400)
173 b956618e Antony Chazapis
    
174 83dd59c5 Antony Chazapis
    until = get_int_parameter(request, 'until')
175 cca6c617 Antony Chazapis
    try:
176 cca6c617 Antony Chazapis
        meta = backend.get_account_meta(request.user, v_account, until)
177 02c0c3fa Antony Chazapis
        groups = backend.get_account_groups(request.user, v_account)
178 cca6c617 Antony Chazapis
    except NotAllowedError:
179 cca6c617 Antony Chazapis
        raise Unauthorized('Access denied')
180 b956618e Antony Chazapis
    
181 b956618e Antony Chazapis
    validate_modification_preconditions(request, meta)
182 b956618e Antony Chazapis
    
183 b956618e Antony Chazapis
    response = HttpResponse()
184 02c0c3fa Antony Chazapis
    put_account_headers(response, meta, groups)
185 b956618e Antony Chazapis
    
186 b956618e Antony Chazapis
    marker = request.GET.get('marker')
187 b956618e Antony Chazapis
    limit = request.GET.get('limit')
188 b956618e Antony Chazapis
    if limit:
189 b956618e Antony Chazapis
        try:
190 b956618e Antony Chazapis
            limit = int(limit)
191 b956618e Antony Chazapis
            if limit <= 0:
192 b956618e Antony Chazapis
                raise ValueError
193 b956618e Antony Chazapis
        except ValueError:
194 b956618e Antony Chazapis
            limit = 10000
195 b956618e Antony Chazapis
    
196 b956618e Antony Chazapis
    try:
197 83dd59c5 Antony Chazapis
        containers = backend.list_containers(request.user, v_account, marker, limit, until)
198 cca6c617 Antony Chazapis
    except NotAllowedError:
199 cca6c617 Antony Chazapis
        raise Unauthorized('Access denied')
200 b956618e Antony Chazapis
    except NameError:
201 b956618e Antony Chazapis
        containers = []
202 b956618e Antony Chazapis
    
203 b956618e Antony Chazapis
    if request.serialization == 'text':
204 b956618e Antony Chazapis
        if len(containers) == 0:
205 b956618e Antony Chazapis
            # The cloudfiles python bindings expect 200 if json/xml.
206 b956618e Antony Chazapis
            response.status_code = 204
207 b956618e Antony Chazapis
            return response
208 b956618e Antony Chazapis
        response.status_code = 200
209 83dd59c5 Antony Chazapis
        response.content = '\n'.join([x[0] for x in containers]) + '\n'
210 b956618e Antony Chazapis
        return response
211 b956618e Antony Chazapis
    
212 b956618e Antony Chazapis
    container_meta = []
213 b956618e Antony Chazapis
    for x in containers:
214 83dd59c5 Antony Chazapis
        if x[1] is not None:
215 83dd59c5 Antony Chazapis
            try:
216 83dd59c5 Antony Chazapis
                meta = backend.get_container_meta(request.user, v_account, x[0], until)
217 3ab38c43 Antony Chazapis
                policy = backend.get_container_policy(request.user, v_account, x[0])
218 3ab38c43 Antony Chazapis
                for k, v in policy.iteritems():
219 3ab38c43 Antony Chazapis
                    meta['X-Container-Policy-' + k] = v
220 02c0c3fa Antony Chazapis
                container_meta.append(printable_header_dict(meta))
221 cca6c617 Antony Chazapis
            except NotAllowedError:
222 cca6c617 Antony Chazapis
                raise Unauthorized('Access denied')
223 83dd59c5 Antony Chazapis
            except NameError:
224 83dd59c5 Antony Chazapis
                pass
225 b956618e Antony Chazapis
    if request.serialization == 'xml':
226 2c22e4ac Antony Chazapis
        data = render_to_string('containers.xml', {'account': v_account, 'containers': container_meta})
227 b956618e Antony Chazapis
    elif request.serialization  == 'json':
228 b956618e Antony Chazapis
        data = json.dumps(container_meta)
229 b956618e Antony Chazapis
    response.status_code = 200
230 b956618e Antony Chazapis
    response.content = data
231 b956618e Antony Chazapis
    return response
232 b956618e Antony Chazapis
233 b956618e Antony Chazapis
@api_method('HEAD')
234 b956618e Antony Chazapis
def container_meta(request, v_account, v_container):
235 b956618e Antony Chazapis
    # Normal Response Codes: 204
236 b956618e Antony Chazapis
    # Error Response Codes: serviceUnavailable (503),
237 b956618e Antony Chazapis
    #                       itemNotFound (404),
238 b956618e Antony Chazapis
    #                       unauthorized (401),
239 b956618e Antony Chazapis
    #                       badRequest (400)
240 b956618e Antony Chazapis
    
241 83dd59c5 Antony Chazapis
    until = get_int_parameter(request, 'until')
242 b956618e Antony Chazapis
    try:
243 83dd59c5 Antony Chazapis
        meta = backend.get_container_meta(request.user, v_account, v_container, until)
244 83dd59c5 Antony Chazapis
        meta['object_meta'] = backend.list_object_meta(request.user, v_account, v_container, until)
245 3ab38c43 Antony Chazapis
        policy = backend.get_container_policy(request.user, v_account, v_container)
246 cca6c617 Antony Chazapis
    except NotAllowedError:
247 cca6c617 Antony Chazapis
        raise Unauthorized('Access denied')
248 b956618e Antony Chazapis
    except NameError:
249 b956618e Antony Chazapis
        raise ItemNotFound('Container does not exist')
250 b956618e Antony Chazapis
    
251 b956618e Antony Chazapis
    response = HttpResponse(status=204)
252 3ab38c43 Antony Chazapis
    put_container_headers(response, meta, policy)
253 b956618e Antony Chazapis
    return response
254 b956618e Antony Chazapis
255 b956618e Antony Chazapis
@api_method('PUT')
256 b956618e Antony Chazapis
def container_create(request, v_account, v_container):
257 b956618e Antony Chazapis
    # Normal Response Codes: 201, 202
258 b956618e Antony Chazapis
    # Error Response Codes: serviceUnavailable (503),
259 b956618e Antony Chazapis
    #                       itemNotFound (404),
260 b956618e Antony Chazapis
    #                       unauthorized (401),
261 b956618e Antony Chazapis
    #                       badRequest (400)
262 b956618e Antony Chazapis
    
263 3ab38c43 Antony Chazapis
    meta, policy = get_container_headers(request)
264 b956618e Antony Chazapis
    
265 b956618e Antony Chazapis
    try:
266 3ab38c43 Antony Chazapis
        backend.put_container(request.user, v_account, v_container, policy)
267 b956618e Antony Chazapis
        ret = 201
268 cca6c617 Antony Chazapis
    except NotAllowedError:
269 cca6c617 Antony Chazapis
        raise Unauthorized('Access denied')
270 b956618e Antony Chazapis
    except NameError:
271 b956618e Antony Chazapis
        ret = 202
272 b956618e Antony Chazapis
    
273 b956618e Antony Chazapis
    if len(meta) > 0:
274 cca6c617 Antony Chazapis
        try:
275 cca6c617 Antony Chazapis
            backend.update_container_meta(request.user, v_account, v_container, meta, replace=True)
276 cca6c617 Antony Chazapis
        except NotAllowedError:
277 cca6c617 Antony Chazapis
            raise Unauthorized('Access denied')
278 cca6c617 Antony Chazapis
        except NameError:
279 cca6c617 Antony Chazapis
            raise ItemNotFound('Container does not exist')
280 b956618e Antony Chazapis
    
281 b956618e Antony Chazapis
    return HttpResponse(status=ret)
282 b956618e Antony Chazapis
283 b956618e Antony Chazapis
@api_method('POST')
284 b956618e Antony Chazapis
def container_update(request, v_account, v_container):
285 b956618e Antony Chazapis
    # Normal Response Codes: 202
286 b956618e Antony Chazapis
    # Error Response Codes: serviceUnavailable (503),
287 b956618e Antony Chazapis
    #                       itemNotFound (404),
288 b956618e Antony Chazapis
    #                       unauthorized (401),
289 b956618e Antony Chazapis
    #                       badRequest (400)
290 b956618e Antony Chazapis
    
291 3ab38c43 Antony Chazapis
    meta, policy = get_container_headers(request)
292 a6eb13e9 Antony Chazapis
    replace = True
293 a6eb13e9 Antony Chazapis
    if 'update' in request.GET:
294 a6eb13e9 Antony Chazapis
        replace = False
295 3ab38c43 Antony Chazapis
    if policy:
296 3ab38c43 Antony Chazapis
        try:
297 3ab38c43 Antony Chazapis
            backend.update_container_policy(request.user, v_account, v_container, policy, replace)
298 3ab38c43 Antony Chazapis
        except NotAllowedError:
299 3ab38c43 Antony Chazapis
            raise Unauthorized('Access denied')
300 3ab38c43 Antony Chazapis
        except NameError:
301 3ab38c43 Antony Chazapis
            raise ItemNotFound('Container does not exist')
302 3ab38c43 Antony Chazapis
        except ValueError:
303 3ab38c43 Antony Chazapis
            raise BadRequest('Invalid policy header')
304 b956618e Antony Chazapis
    try:
305 a6eb13e9 Antony Chazapis
        backend.update_container_meta(request.user, v_account, v_container, meta, replace)
306 cca6c617 Antony Chazapis
    except NotAllowedError:
307 cca6c617 Antony Chazapis
        raise Unauthorized('Access denied')
308 b956618e Antony Chazapis
    except NameError:
309 b956618e Antony Chazapis
        raise ItemNotFound('Container does not exist')
310 b956618e Antony Chazapis
    return HttpResponse(status=202)
311 b956618e Antony Chazapis
312 b956618e Antony Chazapis
@api_method('DELETE')
313 b956618e Antony Chazapis
def container_delete(request, v_account, v_container):
314 b956618e Antony Chazapis
    # Normal Response Codes: 204
315 b956618e Antony Chazapis
    # Error Response Codes: serviceUnavailable (503),
316 b956618e Antony Chazapis
    #                       conflict (409),
317 b956618e Antony Chazapis
    #                       itemNotFound (404),
318 b956618e Antony Chazapis
    #                       unauthorized (401),
319 b956618e Antony Chazapis
    #                       badRequest (400)
320 b956618e Antony Chazapis
    
321 b956618e Antony Chazapis
    try:
322 83dd59c5 Antony Chazapis
        backend.delete_container(request.user, v_account, v_container)
323 cca6c617 Antony Chazapis
    except NotAllowedError:
324 cca6c617 Antony Chazapis
        raise Unauthorized('Access denied')
325 b956618e Antony Chazapis
    except NameError:
326 b956618e Antony Chazapis
        raise ItemNotFound('Container does not exist')
327 b956618e Antony Chazapis
    except IndexError:
328 b956618e Antony Chazapis
        raise Conflict('Container is not empty')
329 b956618e Antony Chazapis
    return HttpResponse(status=204)
330 b956618e Antony Chazapis
331 b956618e Antony Chazapis
@api_method('GET', format_allowed=True)
332 b956618e Antony Chazapis
def object_list(request, v_account, v_container):
333 b956618e Antony Chazapis
    # Normal Response Codes: 200, 204
334 b956618e Antony Chazapis
    # Error Response Codes: serviceUnavailable (503),
335 b956618e Antony Chazapis
    #                       itemNotFound (404),
336 b956618e Antony Chazapis
    #                       unauthorized (401),
337 b956618e Antony Chazapis
    #                       badRequest (400)
338 b956618e Antony Chazapis
    
339 83dd59c5 Antony Chazapis
    until = get_int_parameter(request, 'until')
340 b956618e Antony Chazapis
    try:
341 83dd59c5 Antony Chazapis
        meta = backend.get_container_meta(request.user, v_account, v_container, until)
342 83dd59c5 Antony Chazapis
        meta['object_meta'] = backend.list_object_meta(request.user, v_account, v_container, until)
343 3ab38c43 Antony Chazapis
        policy = backend.get_container_policy(request.user, v_account, v_container)
344 cca6c617 Antony Chazapis
    except NotAllowedError:
345 cca6c617 Antony Chazapis
        raise Unauthorized('Access denied')
346 b956618e Antony Chazapis
    except NameError:
347 b956618e Antony Chazapis
        raise ItemNotFound('Container does not exist')
348 b956618e Antony Chazapis
    
349 b956618e Antony Chazapis
    validate_modification_preconditions(request, meta)
350 b956618e Antony Chazapis
    
351 b956618e Antony Chazapis
    response = HttpResponse()
352 3ab38c43 Antony Chazapis
    put_container_headers(response, meta, policy)
353 b956618e Antony Chazapis
    
354 b956618e Antony Chazapis
    path = request.GET.get('path')
355 b956618e Antony Chazapis
    prefix = request.GET.get('prefix')
356 b956618e Antony Chazapis
    delimiter = request.GET.get('delimiter')
357 b956618e Antony Chazapis
    
358 b956618e Antony Chazapis
    # Path overrides prefix and delimiter.
359 b956618e Antony Chazapis
    virtual = True
360 b956618e Antony Chazapis
    if path:
361 b956618e Antony Chazapis
        prefix = path
362 b956618e Antony Chazapis
        delimiter = '/'
363 b956618e Antony Chazapis
        virtual = False
364 b956618e Antony Chazapis
    
365 b956618e Antony Chazapis
    # Naming policy.
366 b956618e Antony Chazapis
    if prefix and delimiter:
367 b956618e Antony Chazapis
        prefix = prefix + delimiter
368 b956618e Antony Chazapis
    if not prefix:
369 b956618e Antony Chazapis
        prefix = ''
370 b956618e Antony Chazapis
    prefix = prefix.lstrip('/')
371 b956618e Antony Chazapis
    
372 b956618e Antony Chazapis
    marker = request.GET.get('marker')
373 b956618e Antony Chazapis
    limit = request.GET.get('limit')
374 b956618e Antony Chazapis
    if limit:
375 b956618e Antony Chazapis
        try:
376 b956618e Antony Chazapis
            limit = int(limit)
377 b956618e Antony Chazapis
            if limit <= 0:
378 b956618e Antony Chazapis
                raise ValueError
379 b956618e Antony Chazapis
        except ValueError:
380 b956618e Antony Chazapis
            limit = 10000
381 b956618e Antony Chazapis
    
382 22dab079 Antony Chazapis
    keys = request.GET.get('meta')
383 22dab079 Antony Chazapis
    if keys:
384 22dab079 Antony Chazapis
        keys = keys.split(',')
385 02c0c3fa Antony Chazapis
        keys = [format_header_key('X-Object-Meta-' + x.strip()) for x in keys if x.strip() != '']
386 22dab079 Antony Chazapis
    else:
387 22dab079 Antony Chazapis
        keys = []
388 22dab079 Antony Chazapis
    
389 b956618e Antony Chazapis
    try:
390 83dd59c5 Antony Chazapis
        objects = backend.list_objects(request.user, v_account, v_container, prefix, delimiter, marker, limit, virtual, keys, until)
391 cca6c617 Antony Chazapis
    except NotAllowedError:
392 cca6c617 Antony Chazapis
        raise Unauthorized('Access denied')
393 b956618e Antony Chazapis
    except NameError:
394 b956618e Antony Chazapis
        raise ItemNotFound('Container does not exist')
395 b956618e Antony Chazapis
    
396 b956618e Antony Chazapis
    if request.serialization == 'text':
397 b956618e Antony Chazapis
        if len(objects) == 0:
398 b956618e Antony Chazapis
            # The cloudfiles python bindings expect 200 if json/xml.
399 b956618e Antony Chazapis
            response.status_code = 204
400 b956618e Antony Chazapis
            return response
401 b956618e Antony Chazapis
        response.status_code = 200
402 83dd59c5 Antony Chazapis
        response.content = '\n'.join([x[0] for x in objects]) + '\n'
403 b956618e Antony Chazapis
        return response
404 b956618e Antony Chazapis
    
405 b956618e Antony Chazapis
    object_meta = []
406 b956618e Antony Chazapis
    for x in objects:
407 83dd59c5 Antony Chazapis
        if x[1] is None:
408 b956618e Antony Chazapis
            # Virtual objects/directories.
409 83dd59c5 Antony Chazapis
            object_meta.append({'subdir': x[0]})
410 83dd59c5 Antony Chazapis
        else:
411 83dd59c5 Antony Chazapis
            try:
412 83dd59c5 Antony Chazapis
                meta = backend.get_object_meta(request.user, v_account, v_container, x[0], x[1])
413 e8886082 Antony Chazapis
                if until is None:
414 e8886082 Antony Chazapis
                    permissions = backend.get_object_permissions(request.user, v_account, v_container, x[0])
415 e0f916bb Antony Chazapis
                    public = backend.get_object_public(request.user, v_account, v_container, x[0])
416 e8886082 Antony Chazapis
                else:
417 e8886082 Antony Chazapis
                    permissions = None
418 e0f916bb Antony Chazapis
                    public = None
419 cca6c617 Antony Chazapis
            except NotAllowedError:
420 cca6c617 Antony Chazapis
                raise Unauthorized('Access denied')
421 83dd59c5 Antony Chazapis
            except NameError:
422 83dd59c5 Antony Chazapis
                pass
423 cca6c617 Antony Chazapis
            update_sharing_meta(permissions, v_account, v_container, x[0], meta)
424 e0f916bb Antony Chazapis
            update_public_meta(public, meta)
425 02c0c3fa Antony Chazapis
            object_meta.append(printable_header_dict(meta))
426 b956618e Antony Chazapis
    if request.serialization == 'xml':
427 b956618e Antony Chazapis
        data = render_to_string('objects.xml', {'container': v_container, 'objects': object_meta})
428 b956618e Antony Chazapis
    elif request.serialization  == 'json':
429 b956618e Antony Chazapis
        data = json.dumps(object_meta)
430 b956618e Antony Chazapis
    response.status_code = 200
431 b956618e Antony Chazapis
    response.content = data
432 b956618e Antony Chazapis
    return response
433 b956618e Antony Chazapis
434 b956618e Antony Chazapis
@api_method('HEAD')
435 b956618e Antony Chazapis
def object_meta(request, v_account, v_container, v_object):
436 b956618e Antony Chazapis
    # Normal Response Codes: 204
437 b956618e Antony Chazapis
    # Error Response Codes: serviceUnavailable (503),
438 b956618e Antony Chazapis
    #                       itemNotFound (404),
439 b956618e Antony Chazapis
    #                       unauthorized (401),
440 b956618e Antony Chazapis
    #                       badRequest (400)
441 b956618e Antony Chazapis
    
442 104626e3 Antony Chazapis
    version = request.GET.get('version')
443 b956618e Antony Chazapis
    try:
444 83dd59c5 Antony Chazapis
        meta = backend.get_object_meta(request.user, v_account, v_container, v_object, version)
445 e8886082 Antony Chazapis
        if version is None:
446 e8886082 Antony Chazapis
            permissions = backend.get_object_permissions(request.user, v_account, v_container, v_object)
447 e0f916bb Antony Chazapis
            public = backend.get_object_public(request.user, v_account, v_container, v_object)
448 e8886082 Antony Chazapis
        else:
449 e8886082 Antony Chazapis
            permissions = None
450 e0f916bb Antony Chazapis
            public = None
451 cca6c617 Antony Chazapis
    except NotAllowedError:
452 cca6c617 Antony Chazapis
        raise Unauthorized('Access denied')
453 b956618e Antony Chazapis
    except NameError:
454 b956618e Antony Chazapis
        raise ItemNotFound('Object does not exist')
455 58a6c894 Antony Chazapis
    except IndexError:
456 58a6c894 Antony Chazapis
        raise ItemNotFound('Version does not exist')
457 b956618e Antony Chazapis
    
458 8cb45c13 Antony Chazapis
    update_manifest_meta(request, v_account, meta)
459 cca6c617 Antony Chazapis
    update_sharing_meta(permissions, v_account, v_container, v_object, meta)
460 e0f916bb Antony Chazapis
    update_public_meta(public, meta)
461 8cb45c13 Antony Chazapis
    
462 cb146cf9 Antony Chazapis
    response = HttpResponse(status=200)
463 02c0c3fa Antony Chazapis
    put_object_headers(response, meta)
464 b956618e Antony Chazapis
    return response
465 b956618e Antony Chazapis
466 22dab079 Antony Chazapis
@api_method('GET', format_allowed=True)
467 b956618e Antony Chazapis
def object_read(request, v_account, v_container, v_object):
468 b956618e Antony Chazapis
    # Normal Response Codes: 200, 206
469 b956618e Antony Chazapis
    # Error Response Codes: serviceUnavailable (503),
470 b956618e Antony Chazapis
    #                       rangeNotSatisfiable (416),
471 b956618e Antony Chazapis
    #                       preconditionFailed (412),
472 b956618e Antony Chazapis
    #                       itemNotFound (404),
473 b956618e Antony Chazapis
    #                       unauthorized (401),
474 b956618e Antony Chazapis
    #                       badRequest (400),
475 b956618e Antony Chazapis
    #                       notModified (304)
476 b956618e Antony Chazapis
    
477 104626e3 Antony Chazapis
    version = request.GET.get('version')
478 e8886082 Antony Chazapis
    
479 e8886082 Antony Chazapis
    # Reply with the version list. Do this first, as the object may be deleted.
480 104626e3 Antony Chazapis
    if version == 'list':
481 e8886082 Antony Chazapis
        if request.serialization == 'text':
482 e8886082 Antony Chazapis
            raise BadRequest('No format specified for version list.')
483 e8886082 Antony Chazapis
        
484 cca6c617 Antony Chazapis
        try:
485 cca6c617 Antony Chazapis
            v = backend.list_versions(request.user, v_account, v_container, v_object)
486 cca6c617 Antony Chazapis
        except NotAllowedError:
487 cca6c617 Antony Chazapis
            raise Unauthorized('Access denied')
488 cca6c617 Antony Chazapis
        d = {'versions': v}
489 e8886082 Antony Chazapis
        if request.serialization == 'xml':
490 e8886082 Antony Chazapis
            d['object'] = v_object
491 e8886082 Antony Chazapis
            data = render_to_string('versions.xml', d)
492 e8886082 Antony Chazapis
        elif request.serialization  == 'json':
493 e8886082 Antony Chazapis
            data = json.dumps(d)
494 e8886082 Antony Chazapis
        
495 e8886082 Antony Chazapis
        response = HttpResponse(data, status=200)
496 e8886082 Antony Chazapis
        response['Content-Length'] = len(data)
497 e8886082 Antony Chazapis
        return response
498 e8886082 Antony Chazapis
    
499 b956618e Antony Chazapis
    try:
500 83dd59c5 Antony Chazapis
        meta = backend.get_object_meta(request.user, v_account, v_container, v_object, version)
501 e8886082 Antony Chazapis
        if version is None:
502 e8886082 Antony Chazapis
            permissions = backend.get_object_permissions(request.user, v_account, v_container, v_object)
503 307915f1 Antony Chazapis
            public = backend.get_object_public(request.user, v_account, v_container, v_object)
504 e8886082 Antony Chazapis
        else:
505 e8886082 Antony Chazapis
            permissions = None
506 e0f916bb Antony Chazapis
            public = None
507 cca6c617 Antony Chazapis
    except NotAllowedError:
508 cca6c617 Antony Chazapis
        raise Unauthorized('Access denied')
509 b956618e Antony Chazapis
    except NameError:
510 b956618e Antony Chazapis
        raise ItemNotFound('Object does not exist')
511 58a6c894 Antony Chazapis
    except IndexError:
512 58a6c894 Antony Chazapis
        raise ItemNotFound('Version does not exist')
513 b956618e Antony Chazapis
    
514 8cb45c13 Antony Chazapis
    update_manifest_meta(request, v_account, meta)
515 cca6c617 Antony Chazapis
    update_sharing_meta(permissions, v_account, v_container, v_object, meta)
516 e0f916bb Antony Chazapis
    update_public_meta(public, meta)
517 8cb45c13 Antony Chazapis
    
518 22dab079 Antony Chazapis
    # Evaluate conditions.
519 b956618e Antony Chazapis
    validate_modification_preconditions(request, meta)
520 22dab079 Antony Chazapis
    try:
521 22dab079 Antony Chazapis
        validate_matching_preconditions(request, meta)
522 22dab079 Antony Chazapis
    except NotModified:
523 22dab079 Antony Chazapis
        response = HttpResponse(status=304)
524 22dab079 Antony Chazapis
        response['ETag'] = meta['hash']
525 22dab079 Antony Chazapis
        return response
526 b956618e Antony Chazapis
    
527 8cb45c13 Antony Chazapis
    sizes = []
528 8cb45c13 Antony Chazapis
    hashmaps = []
529 8cb45c13 Antony Chazapis
    if 'X-Object-Manifest' in meta:
530 8cb45c13 Antony Chazapis
        try:
531 6d817842 Antony Chazapis
            src_container, src_name = split_container_object_string('/' + meta['X-Object-Manifest'])
532 8cb45c13 Antony Chazapis
            objects = backend.list_objects(request.user, v_account, src_container, prefix=src_name, virtual=False)
533 cca6c617 Antony Chazapis
        except NotAllowedError:
534 cca6c617 Antony Chazapis
            raise Unauthorized('Access denied')
535 8cb45c13 Antony Chazapis
        except ValueError:
536 8cb45c13 Antony Chazapis
            raise BadRequest('Invalid X-Object-Manifest header')
537 8cb45c13 Antony Chazapis
        except NameError:
538 8cb45c13 Antony Chazapis
            raise ItemNotFound('Container does not exist')
539 8cb45c13 Antony Chazapis
        
540 8cb45c13 Antony Chazapis
        try:
541 8cb45c13 Antony Chazapis
            for x in objects:
542 8cb45c13 Antony Chazapis
                s, h = backend.get_object_hashmap(request.user, v_account, src_container, x[0], x[1])
543 8cb45c13 Antony Chazapis
                sizes.append(s)
544 8cb45c13 Antony Chazapis
                hashmaps.append(h)
545 cca6c617 Antony Chazapis
        except NotAllowedError:
546 cca6c617 Antony Chazapis
            raise Unauthorized('Access denied')
547 8cb45c13 Antony Chazapis
        except NameError:
548 8cb45c13 Antony Chazapis
            raise ItemNotFound('Object does not exist')
549 8cb45c13 Antony Chazapis
        except IndexError:
550 8cb45c13 Antony Chazapis
            raise ItemNotFound('Version does not exist')
551 8cb45c13 Antony Chazapis
    else:
552 8cb45c13 Antony Chazapis
        try:
553 8cb45c13 Antony Chazapis
            s, h = backend.get_object_hashmap(request.user, v_account, v_container, v_object, version)
554 8cb45c13 Antony Chazapis
            sizes.append(s)
555 8cb45c13 Antony Chazapis
            hashmaps.append(h)
556 cca6c617 Antony Chazapis
        except NotAllowedError:
557 cca6c617 Antony Chazapis
            raise Unauthorized('Access denied')
558 8cb45c13 Antony Chazapis
        except NameError:
559 8cb45c13 Antony Chazapis
            raise ItemNotFound('Object does not exist')
560 8cb45c13 Antony Chazapis
        except IndexError:
561 8cb45c13 Antony Chazapis
            raise ItemNotFound('Version does not exist')
562 b956618e Antony Chazapis
    
563 22dab079 Antony Chazapis
    # Reply with the hashmap.
564 22dab079 Antony Chazapis
    if request.serialization != 'text':
565 8cb45c13 Antony Chazapis
        size = sum(sizes)
566 8cb45c13 Antony Chazapis
        hashmap = sum(hashmaps, [])
567 6bc372a4 Antony Chazapis
        d = {'block_size': backend.block_size, 'block_hash': backend.hash_algorithm, 'bytes': size, 'hashes': hashmap}
568 22dab079 Antony Chazapis
        if request.serialization == 'xml':
569 6bc372a4 Antony Chazapis
            d['object'] = v_object
570 6bc372a4 Antony Chazapis
            data = render_to_string('hashes.xml', d)
571 22dab079 Antony Chazapis
        elif request.serialization  == 'json':
572 6bc372a4 Antony Chazapis
            data = json.dumps(d)
573 22dab079 Antony Chazapis
        
574 22dab079 Antony Chazapis
        response = HttpResponse(data, status=200)
575 02c0c3fa Antony Chazapis
        put_object_headers(response, meta)
576 22dab079 Antony Chazapis
        response['Content-Length'] = len(data)
577 22dab079 Antony Chazapis
        return response
578 22dab079 Antony Chazapis
    
579 8cb45c13 Antony Chazapis
    return object_data_response(request, sizes, hashmaps, meta)
580 b956618e Antony Chazapis
581 76985443 Sofia Papagiannaki
@api_method('PUT', format_allowed=True)
582 b956618e Antony Chazapis
def object_write(request, v_account, v_container, v_object):
583 b956618e Antony Chazapis
    # Normal Response Codes: 201
584 b956618e Antony Chazapis
    # Error Response Codes: serviceUnavailable (503),
585 b956618e Antony Chazapis
    #                       unprocessableEntity (422),
586 b956618e Antony Chazapis
    #                       lengthRequired (411),
587 3436eeb0 Antony Chazapis
    #                       conflict (409),
588 b956618e Antony Chazapis
    #                       itemNotFound (404),
589 b956618e Antony Chazapis
    #                       unauthorized (401),
590 b956618e Antony Chazapis
    #                       badRequest (400)
591 b956618e Antony Chazapis
    copy_from = request.META.get('HTTP_X_COPY_FROM')
592 b956618e Antony Chazapis
    move_from = request.META.get('HTTP_X_MOVE_FROM')
593 b956618e Antony Chazapis
    if copy_from or move_from:
594 b956618e Antony Chazapis
        # TODO: Why is this required? Copy this ammount?
595 22dab079 Antony Chazapis
        content_length = get_content_length(request)
596 b956618e Antony Chazapis
        
597 b956618e Antony Chazapis
        if move_from:
598 83dd59c5 Antony Chazapis
            try:
599 83dd59c5 Antony Chazapis
                src_container, src_name = split_container_object_string(move_from)
600 83dd59c5 Antony Chazapis
            except ValueError:
601 83dd59c5 Antony Chazapis
                raise BadRequest('Invalid X-Move-From header')
602 83dd59c5 Antony Chazapis
            copy_or_move_object(request, v_account, src_container, src_name, v_container, v_object, move=True)
603 b956618e Antony Chazapis
        else:
604 83dd59c5 Antony Chazapis
            try:
605 83dd59c5 Antony Chazapis
                src_container, src_name = split_container_object_string(copy_from)
606 83dd59c5 Antony Chazapis
            except ValueError:
607 83dd59c5 Antony Chazapis
                raise BadRequest('Invalid X-Copy-From header')
608 83dd59c5 Antony Chazapis
            copy_or_move_object(request, v_account, src_container, src_name, v_container, v_object, move=False)
609 b956618e Antony Chazapis
        return HttpResponse(status=201)
610 b956618e Antony Chazapis
    
611 3ab38c43 Antony Chazapis
    meta, permissions, public = get_object_headers(request)
612 b956618e Antony Chazapis
    content_length = -1
613 b956618e Antony Chazapis
    if request.META.get('HTTP_TRANSFER_ENCODING') != 'chunked':
614 22dab079 Antony Chazapis
        content_length = get_content_length(request)
615 b956618e Antony Chazapis
    # Should be BadRequest, but API says otherwise.
616 b956618e Antony Chazapis
    if 'Content-Type' not in meta:
617 b956618e Antony Chazapis
        raise LengthRequired('Missing Content-Type header')
618 b956618e Antony Chazapis
    
619 76985443 Sofia Papagiannaki
    if request.serialization == 'json':
620 76985443 Sofia Papagiannaki
        data = ''
621 76985443 Sofia Papagiannaki
        sock = raw_input_socket(request)
622 76985443 Sofia Papagiannaki
        for block in socket_read_iterator(sock, content_length, backend.block_size):
623 76985443 Sofia Papagiannaki
            data = '%s%s' % (data, block)
624 76985443 Sofia Papagiannaki
        d = json.loads(data)
625 76985443 Sofia Papagiannaki
        if not hasattr(d, '__getitem__'):
626 76985443 Sofia Papagiannaki
            raise BadRequest('Invalid data formating')
627 76985443 Sofia Papagiannaki
        try:
628 76985443 Sofia Papagiannaki
            hashmap = d['hashes']
629 76985443 Sofia Papagiannaki
            size = d['bytes']
630 76985443 Sofia Papagiannaki
        except KeyError:
631 76985443 Sofia Papagiannaki
            raise BadRequest('Invalid data formatting')
632 76985443 Sofia Papagiannaki
        meta.update({'hash': hashmap_hash(hashmap)}) # Update ETag.
633 ce2eba7e Sofia Papagiannaki
    elif request.serialization == 'xml':
634 76985443 Sofia Papagiannaki
        #TODO support for xml
635 76985443 Sofia Papagiannaki
        raise BadRequest('Format xml is not supported')
636 76985443 Sofia Papagiannaki
    else:
637 76985443 Sofia Papagiannaki
        md5 = hashlib.md5()
638 76985443 Sofia Papagiannaki
        size = 0
639 76985443 Sofia Papagiannaki
        hashmap = []
640 76985443 Sofia Papagiannaki
        sock = raw_input_socket(request)
641 76985443 Sofia Papagiannaki
        for data in socket_read_iterator(sock, content_length, backend.block_size):
642 76985443 Sofia Papagiannaki
            # TODO: Raise 408 (Request Timeout) if this takes too long.
643 76985443 Sofia Papagiannaki
            # TODO: Raise 499 (Client Disconnect) if a length is defined and we stop before getting this much data.
644 76985443 Sofia Papagiannaki
            size += len(data)
645 76985443 Sofia Papagiannaki
            hashmap.append(backend.put_block(data))
646 76985443 Sofia Papagiannaki
            md5.update(data)
647 76985443 Sofia Papagiannaki
        
648 76985443 Sofia Papagiannaki
        meta['hash'] = md5.hexdigest().lower()
649 76985443 Sofia Papagiannaki
        etag = request.META.get('HTTP_ETAG')
650 76985443 Sofia Papagiannaki
        if etag and parse_etags(etag)[0].lower() != meta['hash']:
651 76985443 Sofia Papagiannaki
            raise UnprocessableEntity('Object ETag does not match')
652 22dab079 Antony Chazapis
    
653 22dab079 Antony Chazapis
    try:
654 3436eeb0 Antony Chazapis
        backend.update_object_hashmap(request.user, v_account, v_container, v_object, size, hashmap, meta, True, permissions)
655 cca6c617 Antony Chazapis
    except NotAllowedError:
656 cca6c617 Antony Chazapis
        raise Unauthorized('Access denied')
657 76985443 Sofia Papagiannaki
    except IndexError, e:
658 1993fea9 Antony Chazapis
        raise Conflict(json.dumps(e.data))
659 22dab079 Antony Chazapis
    except NameError:
660 22dab079 Antony Chazapis
        raise ItemNotFound('Container does not exist')
661 3436eeb0 Antony Chazapis
    except ValueError:
662 3436eeb0 Antony Chazapis
        raise BadRequest('Invalid sharing header')
663 1993fea9 Antony Chazapis
    except AttributeError, e:
664 1993fea9 Antony Chazapis
        raise Conflict(json.dumps(e.data))
665 e0f916bb Antony Chazapis
    if public is not None:
666 e0f916bb Antony Chazapis
        try:
667 e0f916bb Antony Chazapis
            backend.update_object_public(request.user, v_account, v_container, v_object, public)
668 e0f916bb Antony Chazapis
        except NotAllowedError:
669 e0f916bb Antony Chazapis
            raise Unauthorized('Access denied')
670 e0f916bb Antony Chazapis
        except NameError:
671 e0f916bb Antony Chazapis
            raise ItemNotFound('Object does not exist')
672 b956618e Antony Chazapis
    
673 1993fea9 Antony Chazapis
    response = HttpResponse(status=201)
674 b956618e Antony Chazapis
    response['ETag'] = meta['hash']
675 b956618e Antony Chazapis
    return response
676 b956618e Antony Chazapis
677 b956618e Antony Chazapis
@api_method('COPY')
678 b956618e Antony Chazapis
def object_copy(request, v_account, v_container, v_object):
679 b956618e Antony Chazapis
    # Normal Response Codes: 201
680 b956618e Antony Chazapis
    # Error Response Codes: serviceUnavailable (503),
681 b956618e Antony Chazapis
    #                       itemNotFound (404),
682 b956618e Antony Chazapis
    #                       unauthorized (401),
683 b956618e Antony Chazapis
    #                       badRequest (400)
684 b956618e Antony Chazapis
    
685 b956618e Antony Chazapis
    dest_path = request.META.get('HTTP_DESTINATION')
686 b956618e Antony Chazapis
    if not dest_path:
687 b956618e Antony Chazapis
        raise BadRequest('Missing Destination header')
688 83dd59c5 Antony Chazapis
    try:
689 83dd59c5 Antony Chazapis
        dest_container, dest_name = split_container_object_string(dest_path)
690 83dd59c5 Antony Chazapis
    except ValueError:
691 83dd59c5 Antony Chazapis
        raise BadRequest('Invalid Destination header')
692 83dd59c5 Antony Chazapis
    copy_or_move_object(request, v_account, v_container, v_object, dest_container, dest_name, move=False)
693 b956618e Antony Chazapis
    return HttpResponse(status=201)
694 b956618e Antony Chazapis
695 b956618e Antony Chazapis
@api_method('MOVE')
696 b956618e Antony Chazapis
def object_move(request, v_account, v_container, v_object):
697 b956618e Antony Chazapis
    # Normal Response Codes: 201
698 b956618e Antony Chazapis
    # Error Response Codes: serviceUnavailable (503),
699 b956618e Antony Chazapis
    #                       itemNotFound (404),
700 b956618e Antony Chazapis
    #                       unauthorized (401),
701 b956618e Antony Chazapis
    #                       badRequest (400)
702 b956618e Antony Chazapis
    
703 b956618e Antony Chazapis
    dest_path = request.META.get('HTTP_DESTINATION')
704 b956618e Antony Chazapis
    if not dest_path:
705 b956618e Antony Chazapis
        raise BadRequest('Missing Destination header')
706 83dd59c5 Antony Chazapis
    try:
707 83dd59c5 Antony Chazapis
        dest_container, dest_name = split_container_object_string(dest_path)
708 83dd59c5 Antony Chazapis
    except ValueError:
709 83dd59c5 Antony Chazapis
        raise BadRequest('Invalid Destination header')
710 83dd59c5 Antony Chazapis
    copy_or_move_object(request, v_account, v_container, v_object, dest_container, dest_name, move=True)
711 b956618e Antony Chazapis
    return HttpResponse(status=201)
712 b956618e Antony Chazapis
713 b956618e Antony Chazapis
@api_method('POST')
714 b956618e Antony Chazapis
def object_update(request, v_account, v_container, v_object):
715 e9285524 Antony Chazapis
    # Normal Response Codes: 202, 204
716 b956618e Antony Chazapis
    # Error Response Codes: serviceUnavailable (503),
717 3436eeb0 Antony Chazapis
    #                       conflict (409),
718 b956618e Antony Chazapis
    #                       itemNotFound (404),
719 b956618e Antony Chazapis
    #                       unauthorized (401),
720 b956618e Antony Chazapis
    #                       badRequest (400)
721 b956618e Antony Chazapis
    
722 3ab38c43 Antony Chazapis
    meta, permissions, public = get_object_headers(request)
723 22dab079 Antony Chazapis
    content_type = meta.get('Content-Type')
724 22dab079 Antony Chazapis
    if content_type:
725 b956618e Antony Chazapis
        del(meta['Content-Type']) # Do not allow changing the Content-Type.
726 22dab079 Antony Chazapis
    
727 cfe6939d Antony Chazapis
    try:
728 83dd59c5 Antony Chazapis
        prev_meta = backend.get_object_meta(request.user, v_account, v_container, v_object)
729 cca6c617 Antony Chazapis
    except NotAllowedError:
730 cca6c617 Antony Chazapis
        raise Unauthorized('Access denied')
731 cfe6939d Antony Chazapis
    except NameError:
732 cfe6939d Antony Chazapis
        raise ItemNotFound('Object does not exist')
733 ac62f6da Antony Chazapis
    # If replacing, keep previous values of 'Content-Type' and 'hash'.
734 ac62f6da Antony Chazapis
    replace = True
735 ac62f6da Antony Chazapis
    if 'update' in request.GET:
736 ac62f6da Antony Chazapis
        replace = False
737 ac62f6da Antony Chazapis
    if replace:
738 22dab079 Antony Chazapis
        for k in ('Content-Type', 'hash'):
739 22dab079 Antony Chazapis
            if k in prev_meta:
740 22dab079 Antony Chazapis
                meta[k] = prev_meta[k]
741 22dab079 Antony Chazapis
    
742 ac62f6da Antony Chazapis
    # A Content-Type header indicates data updates.
743 ac62f6da Antony Chazapis
    if not content_type or content_type != 'application/octet-stream':
744 cca6c617 Antony Chazapis
        # Do permissions first, as it may fail easier.
745 cca6c617 Antony Chazapis
        if permissions is not None:
746 ac62f6da Antony Chazapis
            try:
747 ac62f6da Antony Chazapis
                backend.update_object_permissions(request.user, v_account, v_container, v_object, permissions)
748 cca6c617 Antony Chazapis
            except NotAllowedError:
749 cca6c617 Antony Chazapis
                raise Unauthorized('Access denied')
750 ac62f6da Antony Chazapis
            except NameError:
751 ac62f6da Antony Chazapis
                raise ItemNotFound('Object does not exist')
752 ac62f6da Antony Chazapis
            except ValueError:
753 ac62f6da Antony Chazapis
                raise BadRequest('Invalid sharing header')
754 1993fea9 Antony Chazapis
            except AttributeError, e:
755 1993fea9 Antony Chazapis
                raise Conflict(json.dumps(e.data))
756 e0f916bb Antony Chazapis
        if public is not None:
757 e0f916bb Antony Chazapis
            try:
758 e0f916bb Antony Chazapis
                backend.update_object_public(request.user, v_account, v_container, v_object, public)
759 e0f916bb Antony Chazapis
            except NotAllowedError:
760 e0f916bb Antony Chazapis
                raise Unauthorized('Access denied')
761 e0f916bb Antony Chazapis
            except NameError:
762 e0f916bb Antony Chazapis
                raise ItemNotFound('Object does not exist')
763 cca6c617 Antony Chazapis
        try:
764 cca6c617 Antony Chazapis
            backend.update_object_meta(request.user, v_account, v_container, v_object, meta, replace)
765 cca6c617 Antony Chazapis
        except NotAllowedError:
766 cca6c617 Antony Chazapis
            raise Unauthorized('Access denied')
767 cca6c617 Antony Chazapis
        except NameError:
768 cca6c617 Antony Chazapis
            raise ItemNotFound('Object does not exist')
769 cfe6939d Antony Chazapis
        return HttpResponse(status=202)
770 ac62f6da Antony Chazapis
    
771 cfe6939d Antony Chazapis
    # Single range update. Range must be in Content-Range.
772 22dab079 Antony Chazapis
    # Based on: http://code.google.com/p/gears/wiki/ContentRangePostProposal
773 cfe6939d Antony Chazapis
    # (with the addition that '*' is allowed for the range - will append).
774 22dab079 Antony Chazapis
    content_range = request.META.get('HTTP_CONTENT_RANGE')
775 22dab079 Antony Chazapis
    if not content_range:
776 ac62f6da Antony Chazapis
        raise BadRequest('Missing Content-Range header')
777 22dab079 Antony Chazapis
    ranges = get_content_range(request)
778 22dab079 Antony Chazapis
    if not ranges:
779 ac62f6da Antony Chazapis
        raise RangeNotSatisfiable('Invalid Content-Range header')
780 cfe6939d Antony Chazapis
    # Require either a Content-Length, or 'chunked' Transfer-Encoding.
781 cfe6939d Antony Chazapis
    content_length = -1
782 cfe6939d Antony Chazapis
    if request.META.get('HTTP_TRANSFER_ENCODING') != 'chunked':
783 cfe6939d Antony Chazapis
        content_length = get_content_length(request)
784 22dab079 Antony Chazapis
    
785 cfe6939d Antony Chazapis
    try:
786 83dd59c5 Antony Chazapis
        size, hashmap = backend.get_object_hashmap(request.user, v_account, v_container, v_object)
787 cca6c617 Antony Chazapis
    except NotAllowedError:
788 cca6c617 Antony Chazapis
        raise Unauthorized('Access denied')
789 cfe6939d Antony Chazapis
    except NameError:
790 cfe6939d Antony Chazapis
        raise ItemNotFound('Object does not exist')
791 cfe6939d Antony Chazapis
    
792 cfe6939d Antony Chazapis
    offset, length, total = ranges
793 cfe6939d Antony Chazapis
    if offset is None:
794 cfe6939d Antony Chazapis
        offset = size
795 cb146cf9 Antony Chazapis
    elif offset > size:
796 cb146cf9 Antony Chazapis
        raise RangeNotSatisfiable('Supplied offset is beyond object limits')
797 9a7f747a Sofia Papagiannaki
    if length is None or content_length == -1:
798 cfe6939d Antony Chazapis
        length = content_length # Nevermind the error.
799 cfe6939d Antony Chazapis
    elif length != content_length:
800 cfe6939d Antony Chazapis
        raise BadRequest('Content length does not match range length')
801 cfe6939d Antony Chazapis
    if total is not None and (total != size or offset >= size or (length > 0 and offset + length >= size)):
802 cfe6939d Antony Chazapis
        raise RangeNotSatisfiable('Supplied range will change provided object limits')
803 cfe6939d Antony Chazapis
    
804 cfe6939d Antony Chazapis
    sock = raw_input_socket(request)
805 cfe6939d Antony Chazapis
    data = ''
806 cfe6939d Antony Chazapis
    for d in socket_read_iterator(sock, length, backend.block_size):
807 cfe6939d Antony Chazapis
        # TODO: Raise 408 (Request Timeout) if this takes too long.
808 cfe6939d Antony Chazapis
        # TODO: Raise 499 (Client Disconnect) if a length is defined and we stop before getting this much data.
809 cfe6939d Antony Chazapis
        data += d
810 cb146cf9 Antony Chazapis
        bytes = put_object_block(hashmap, data, offset)
811 cb146cf9 Antony Chazapis
        offset += bytes
812 cb146cf9 Antony Chazapis
        data = data[bytes:]
813 cfe6939d Antony Chazapis
    if len(data) > 0:
814 cb146cf9 Antony Chazapis
        put_object_block(hashmap, data, offset)
815 cfe6939d Antony Chazapis
    
816 cfe6939d Antony Chazapis
    if offset > size:
817 cfe6939d Antony Chazapis
        size = offset
818 ac62f6da Antony Chazapis
    meta.update({'hash': hashmap_hash(hashmap)}) # Update ETag.
819 cfe6939d Antony Chazapis
    try:
820 ac62f6da Antony Chazapis
        backend.update_object_hashmap(request.user, v_account, v_container, v_object, size, hashmap, meta, replace, permissions)
821 cca6c617 Antony Chazapis
    except NotAllowedError:
822 cca6c617 Antony Chazapis
        raise Unauthorized('Access denied')
823 cfe6939d Antony Chazapis
    except NameError:
824 cfe6939d Antony Chazapis
        raise ItemNotFound('Container does not exist')
825 3436eeb0 Antony Chazapis
    except ValueError:
826 3436eeb0 Antony Chazapis
        raise BadRequest('Invalid sharing header')
827 1993fea9 Antony Chazapis
    except AttributeError, e:
828 1993fea9 Antony Chazapis
        raise Conflict(json.dumps(e.data))
829 e0f916bb Antony Chazapis
    if public is not None:
830 e0f916bb Antony Chazapis
        try:
831 e0f916bb Antony Chazapis
            backend.update_object_public(request.user, v_account, v_container, v_object, public)
832 e0f916bb Antony Chazapis
        except NotAllowedError:
833 e0f916bb Antony Chazapis
            raise Unauthorized('Access denied')
834 e0f916bb Antony Chazapis
        except NameError:
835 e0f916bb Antony Chazapis
            raise ItemNotFound('Object does not exist')
836 3436eeb0 Antony Chazapis
    
837 e9285524 Antony Chazapis
    response = HttpResponse(status=204)
838 e9285524 Antony Chazapis
    response['ETag'] = meta['hash']
839 e9285524 Antony Chazapis
    return response
840 b956618e Antony Chazapis
841 b956618e Antony Chazapis
@api_method('DELETE')
842 b956618e Antony Chazapis
def object_delete(request, v_account, v_container, v_object):
843 b956618e Antony Chazapis
    # Normal Response Codes: 204
844 b956618e Antony Chazapis
    # Error Response Codes: serviceUnavailable (503),
845 b956618e Antony Chazapis
    #                       itemNotFound (404),
846 b956618e Antony Chazapis
    #                       unauthorized (401),
847 b956618e Antony Chazapis
    #                       badRequest (400)
848 b956618e Antony Chazapis
    
849 b956618e Antony Chazapis
    try:
850 83dd59c5 Antony Chazapis
        backend.delete_object(request.user, v_account, v_container, v_object)
851 cca6c617 Antony Chazapis
    except NotAllowedError:
852 cca6c617 Antony Chazapis
        raise Unauthorized('Access denied')
853 b956618e Antony Chazapis
    except NameError:
854 b956618e Antony Chazapis
        raise ItemNotFound('Object does not exist')
855 b956618e Antony Chazapis
    return HttpResponse(status=204)
856 b956618e Antony Chazapis
857 b956618e Antony Chazapis
@api_method()
858 b956618e Antony Chazapis
def method_not_allowed(request):
859 b956618e Antony Chazapis
    raise BadRequest('Method not allowed')