Statistics
| Branch: | Tag: | Revision:

root / pithos / api / functions.py @ a0177bc5

History | View | Annotate | Download (36 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 a0177bc5 Giorgos Verigakis
    response['X-Storage-Url'] = os.path.join(request.build_absolute_uri(),
122 a0177bc5 Giorgos Verigakis
                                            x_auth_user)
123 b956618e Antony Chazapis
    return response
124 b956618e Antony Chazapis
125 b956618e Antony Chazapis
@api_method('HEAD')
126 b956618e Antony Chazapis
def account_meta(request, v_account):
127 b956618e Antony Chazapis
    # Normal Response Codes: 204
128 b956618e Antony Chazapis
    # Error Response Codes: serviceUnavailable (503),
129 b956618e Antony Chazapis
    #                       unauthorized (401),
130 b956618e Antony Chazapis
    #                       badRequest (400)
131 b956618e Antony Chazapis
    
132 1495b972 Antony Chazapis
    until = get_int_parameter(request.GET.get('until'))
133 cca6c617 Antony Chazapis
    try:
134 cca6c617 Antony Chazapis
        meta = backend.get_account_meta(request.user, v_account, until)
135 02c0c3fa Antony Chazapis
        groups = backend.get_account_groups(request.user, v_account)
136 cca6c617 Antony Chazapis
    except NotAllowedError:
137 cca6c617 Antony Chazapis
        raise Unauthorized('Access denied')
138 b956618e Antony Chazapis
    
139 b956618e Antony Chazapis
    response = HttpResponse(status=204)
140 02c0c3fa Antony Chazapis
    put_account_headers(response, meta, groups)
141 b956618e Antony Chazapis
    return response
142 b956618e Antony Chazapis
143 b956618e Antony Chazapis
@api_method('POST')
144 b956618e Antony Chazapis
def account_update(request, v_account):
145 b956618e Antony Chazapis
    # Normal Response Codes: 202
146 b956618e Antony Chazapis
    # Error Response Codes: serviceUnavailable (503),
147 b956618e Antony Chazapis
    #                       unauthorized (401),
148 b956618e Antony Chazapis
    #                       badRequest (400)
149 b956618e Antony Chazapis
    
150 02c0c3fa Antony Chazapis
    meta, groups = get_account_headers(request)
151 a6eb13e9 Antony Chazapis
    replace = True
152 a6eb13e9 Antony Chazapis
    if 'update' in request.GET:
153 02c0c3fa Antony Chazapis
        replace = False    
154 02c0c3fa Antony Chazapis
    if groups:
155 02c0c3fa Antony Chazapis
        try:
156 02c0c3fa Antony Chazapis
            backend.update_account_groups(request.user, v_account, groups, replace)
157 02c0c3fa Antony Chazapis
        except NotAllowedError:
158 02c0c3fa Antony Chazapis
            raise Unauthorized('Access denied')
159 02c0c3fa Antony Chazapis
        except ValueError:
160 02c0c3fa Antony Chazapis
            raise BadRequest('Invalid groups header')
161 cca6c617 Antony Chazapis
    try:
162 cca6c617 Antony Chazapis
        backend.update_account_meta(request.user, v_account, meta, replace)
163 cca6c617 Antony Chazapis
    except NotAllowedError:
164 cca6c617 Antony Chazapis
        raise Unauthorized('Access denied')
165 b956618e Antony Chazapis
    return HttpResponse(status=202)
166 b956618e Antony Chazapis
167 b956618e Antony Chazapis
@api_method('GET', format_allowed=True)
168 b956618e Antony Chazapis
def container_list(request, v_account):
169 b956618e Antony Chazapis
    # Normal Response Codes: 200, 204
170 b956618e Antony Chazapis
    # Error Response Codes: serviceUnavailable (503),
171 b956618e Antony Chazapis
    #                       itemNotFound (404),
172 b956618e Antony Chazapis
    #                       unauthorized (401),
173 b956618e Antony Chazapis
    #                       badRequest (400)
174 b956618e Antony Chazapis
    
175 1495b972 Antony Chazapis
    until = get_int_parameter(request.GET.get('until'))
176 cca6c617 Antony Chazapis
    try:
177 cca6c617 Antony Chazapis
        meta = backend.get_account_meta(request.user, v_account, until)
178 02c0c3fa Antony Chazapis
        groups = backend.get_account_groups(request.user, v_account)
179 cca6c617 Antony Chazapis
    except NotAllowedError:
180 cca6c617 Antony Chazapis
        raise Unauthorized('Access denied')
181 b956618e Antony Chazapis
    
182 b956618e Antony Chazapis
    validate_modification_preconditions(request, meta)
183 b956618e Antony Chazapis
    
184 b956618e Antony Chazapis
    response = HttpResponse()
185 02c0c3fa Antony Chazapis
    put_account_headers(response, meta, groups)
186 b956618e Antony Chazapis
    
187 b956618e Antony Chazapis
    marker = request.GET.get('marker')
188 b956618e Antony Chazapis
    limit = request.GET.get('limit')
189 b956618e Antony Chazapis
    if limit:
190 b956618e Antony Chazapis
        try:
191 b956618e Antony Chazapis
            limit = int(limit)
192 b956618e Antony Chazapis
            if limit <= 0:
193 b956618e Antony Chazapis
                raise ValueError
194 b956618e Antony Chazapis
        except ValueError:
195 b956618e Antony Chazapis
            limit = 10000
196 b956618e Antony Chazapis
    
197 b956618e Antony Chazapis
    try:
198 83dd59c5 Antony Chazapis
        containers = backend.list_containers(request.user, v_account, marker, limit, until)
199 cca6c617 Antony Chazapis
    except NotAllowedError:
200 cca6c617 Antony Chazapis
        raise Unauthorized('Access denied')
201 b956618e Antony Chazapis
    except NameError:
202 b956618e Antony Chazapis
        containers = []
203 b956618e Antony Chazapis
    
204 b956618e Antony Chazapis
    if request.serialization == 'text':
205 b956618e Antony Chazapis
        if len(containers) == 0:
206 b956618e Antony Chazapis
            # The cloudfiles python bindings expect 200 if json/xml.
207 b956618e Antony Chazapis
            response.status_code = 204
208 b956618e Antony Chazapis
            return response
209 b956618e Antony Chazapis
        response.status_code = 200
210 83dd59c5 Antony Chazapis
        response.content = '\n'.join([x[0] for x in containers]) + '\n'
211 b956618e Antony Chazapis
        return response
212 b956618e Antony Chazapis
    
213 b956618e Antony Chazapis
    container_meta = []
214 b956618e Antony Chazapis
    for x in containers:
215 83dd59c5 Antony Chazapis
        if x[1] is not None:
216 83dd59c5 Antony Chazapis
            try:
217 83dd59c5 Antony Chazapis
                meta = backend.get_container_meta(request.user, v_account, x[0], until)
218 3ab38c43 Antony Chazapis
                policy = backend.get_container_policy(request.user, v_account, x[0])
219 cca6c617 Antony Chazapis
            except NotAllowedError:
220 cca6c617 Antony Chazapis
                raise Unauthorized('Access denied')
221 83dd59c5 Antony Chazapis
            except NameError:
222 83dd59c5 Antony Chazapis
                pass
223 038f1ae9 Antony Chazapis
            else:
224 038f1ae9 Antony Chazapis
                for k, v in policy.iteritems():
225 038f1ae9 Antony Chazapis
                    meta['X-Container-Policy-' + k] = v
226 038f1ae9 Antony Chazapis
                container_meta.append(printable_header_dict(meta))
227 b956618e Antony Chazapis
    if request.serialization == 'xml':
228 2c22e4ac Antony Chazapis
        data = render_to_string('containers.xml', {'account': v_account, 'containers': container_meta})
229 b956618e Antony Chazapis
    elif request.serialization  == 'json':
230 b956618e Antony Chazapis
        data = json.dumps(container_meta)
231 b956618e Antony Chazapis
    response.status_code = 200
232 b956618e Antony Chazapis
    response.content = data
233 b956618e Antony Chazapis
    return response
234 b956618e Antony Chazapis
235 b956618e Antony Chazapis
@api_method('HEAD')
236 b956618e Antony Chazapis
def container_meta(request, v_account, v_container):
237 b956618e Antony Chazapis
    # Normal Response Codes: 204
238 b956618e Antony Chazapis
    # Error Response Codes: serviceUnavailable (503),
239 b956618e Antony Chazapis
    #                       itemNotFound (404),
240 b956618e Antony Chazapis
    #                       unauthorized (401),
241 b956618e Antony Chazapis
    #                       badRequest (400)
242 b956618e Antony Chazapis
    
243 1495b972 Antony Chazapis
    until = get_int_parameter(request.GET.get('until'))
244 b956618e Antony Chazapis
    try:
245 83dd59c5 Antony Chazapis
        meta = backend.get_container_meta(request.user, v_account, v_container, until)
246 83dd59c5 Antony Chazapis
        meta['object_meta'] = backend.list_object_meta(request.user, v_account, v_container, until)
247 3ab38c43 Antony Chazapis
        policy = backend.get_container_policy(request.user, v_account, v_container)
248 cca6c617 Antony Chazapis
    except NotAllowedError:
249 cca6c617 Antony Chazapis
        raise Unauthorized('Access denied')
250 b956618e Antony Chazapis
    except NameError:
251 b956618e Antony Chazapis
        raise ItemNotFound('Container does not exist')
252 b956618e Antony Chazapis
    
253 b956618e Antony Chazapis
    response = HttpResponse(status=204)
254 3ab38c43 Antony Chazapis
    put_container_headers(response, meta, policy)
255 b956618e Antony Chazapis
    return response
256 b956618e Antony Chazapis
257 b956618e Antony Chazapis
@api_method('PUT')
258 b956618e Antony Chazapis
def container_create(request, v_account, v_container):
259 b956618e Antony Chazapis
    # Normal Response Codes: 201, 202
260 b956618e Antony Chazapis
    # Error Response Codes: serviceUnavailable (503),
261 b956618e Antony Chazapis
    #                       itemNotFound (404),
262 b956618e Antony Chazapis
    #                       unauthorized (401),
263 b956618e Antony Chazapis
    #                       badRequest (400)
264 b956618e Antony Chazapis
    
265 3ab38c43 Antony Chazapis
    meta, policy = get_container_headers(request)
266 b956618e Antony Chazapis
    
267 b956618e Antony Chazapis
    try:
268 3ab38c43 Antony Chazapis
        backend.put_container(request.user, v_account, v_container, policy)
269 b956618e Antony Chazapis
        ret = 201
270 cca6c617 Antony Chazapis
    except NotAllowedError:
271 cca6c617 Antony Chazapis
        raise Unauthorized('Access denied')
272 b956618e Antony Chazapis
    except NameError:
273 b956618e Antony Chazapis
        ret = 202
274 b956618e Antony Chazapis
    
275 b956618e Antony Chazapis
    if len(meta) > 0:
276 cca6c617 Antony Chazapis
        try:
277 cca6c617 Antony Chazapis
            backend.update_container_meta(request.user, v_account, v_container, meta, replace=True)
278 cca6c617 Antony Chazapis
        except NotAllowedError:
279 cca6c617 Antony Chazapis
            raise Unauthorized('Access denied')
280 cca6c617 Antony Chazapis
        except NameError:
281 cca6c617 Antony Chazapis
            raise ItemNotFound('Container does not exist')
282 b956618e Antony Chazapis
    
283 b956618e Antony Chazapis
    return HttpResponse(status=ret)
284 b956618e Antony Chazapis
285 b956618e Antony Chazapis
@api_method('POST')
286 b956618e Antony Chazapis
def container_update(request, v_account, v_container):
287 b956618e Antony Chazapis
    # Normal Response Codes: 202
288 b956618e Antony Chazapis
    # Error Response Codes: serviceUnavailable (503),
289 b956618e Antony Chazapis
    #                       itemNotFound (404),
290 b956618e Antony Chazapis
    #                       unauthorized (401),
291 b956618e Antony Chazapis
    #                       badRequest (400)
292 b956618e Antony Chazapis
    
293 3ab38c43 Antony Chazapis
    meta, policy = get_container_headers(request)
294 a6eb13e9 Antony Chazapis
    replace = True
295 a6eb13e9 Antony Chazapis
    if 'update' in request.GET:
296 a6eb13e9 Antony Chazapis
        replace = False
297 3ab38c43 Antony Chazapis
    if policy:
298 3ab38c43 Antony Chazapis
        try:
299 3ab38c43 Antony Chazapis
            backend.update_container_policy(request.user, v_account, v_container, policy, replace)
300 3ab38c43 Antony Chazapis
        except NotAllowedError:
301 3ab38c43 Antony Chazapis
            raise Unauthorized('Access denied')
302 3ab38c43 Antony Chazapis
        except NameError:
303 3ab38c43 Antony Chazapis
            raise ItemNotFound('Container does not exist')
304 3ab38c43 Antony Chazapis
        except ValueError:
305 3ab38c43 Antony Chazapis
            raise BadRequest('Invalid policy header')
306 b956618e Antony Chazapis
    try:
307 a6eb13e9 Antony Chazapis
        backend.update_container_meta(request.user, v_account, v_container, meta, replace)
308 cca6c617 Antony Chazapis
    except NotAllowedError:
309 cca6c617 Antony Chazapis
        raise Unauthorized('Access denied')
310 b956618e Antony Chazapis
    except NameError:
311 b956618e Antony Chazapis
        raise ItemNotFound('Container does not exist')
312 b956618e Antony Chazapis
    return HttpResponse(status=202)
313 b956618e Antony Chazapis
314 b956618e Antony Chazapis
@api_method('DELETE')
315 b956618e Antony Chazapis
def container_delete(request, v_account, v_container):
316 b956618e Antony Chazapis
    # Normal Response Codes: 204
317 b956618e Antony Chazapis
    # Error Response Codes: serviceUnavailable (503),
318 b956618e Antony Chazapis
    #                       conflict (409),
319 b956618e Antony Chazapis
    #                       itemNotFound (404),
320 b956618e Antony Chazapis
    #                       unauthorized (401),
321 b956618e Antony Chazapis
    #                       badRequest (400)
322 b956618e Antony Chazapis
    
323 b956618e Antony Chazapis
    try:
324 83dd59c5 Antony Chazapis
        backend.delete_container(request.user, v_account, v_container)
325 cca6c617 Antony Chazapis
    except NotAllowedError:
326 cca6c617 Antony Chazapis
        raise Unauthorized('Access denied')
327 b956618e Antony Chazapis
    except NameError:
328 b956618e Antony Chazapis
        raise ItemNotFound('Container does not exist')
329 b956618e Antony Chazapis
    except IndexError:
330 b956618e Antony Chazapis
        raise Conflict('Container is not empty')
331 b956618e Antony Chazapis
    return HttpResponse(status=204)
332 b956618e Antony Chazapis
333 b956618e Antony Chazapis
@api_method('GET', format_allowed=True)
334 b956618e Antony Chazapis
def object_list(request, v_account, v_container):
335 b956618e Antony Chazapis
    # Normal Response Codes: 200, 204
336 b956618e Antony Chazapis
    # Error Response Codes: serviceUnavailable (503),
337 b956618e Antony Chazapis
    #                       itemNotFound (404),
338 b956618e Antony Chazapis
    #                       unauthorized (401),
339 b956618e Antony Chazapis
    #                       badRequest (400)
340 b956618e Antony Chazapis
    
341 1495b972 Antony Chazapis
    until = get_int_parameter(request.GET.get('until'))
342 b956618e Antony Chazapis
    try:
343 83dd59c5 Antony Chazapis
        meta = backend.get_container_meta(request.user, v_account, v_container, until)
344 83dd59c5 Antony Chazapis
        meta['object_meta'] = backend.list_object_meta(request.user, v_account, v_container, until)
345 3ab38c43 Antony Chazapis
        policy = backend.get_container_policy(request.user, v_account, v_container)
346 cca6c617 Antony Chazapis
    except NotAllowedError:
347 cca6c617 Antony Chazapis
        raise Unauthorized('Access denied')
348 b956618e Antony Chazapis
    except NameError:
349 b956618e Antony Chazapis
        raise ItemNotFound('Container does not exist')
350 b956618e Antony Chazapis
    
351 b956618e Antony Chazapis
    validate_modification_preconditions(request, meta)
352 b956618e Antony Chazapis
    
353 b956618e Antony Chazapis
    response = HttpResponse()
354 3ab38c43 Antony Chazapis
    put_container_headers(response, meta, policy)
355 b956618e Antony Chazapis
    
356 b956618e Antony Chazapis
    path = request.GET.get('path')
357 b956618e Antony Chazapis
    prefix = request.GET.get('prefix')
358 b956618e Antony Chazapis
    delimiter = request.GET.get('delimiter')
359 b956618e Antony Chazapis
    
360 b956618e Antony Chazapis
    # Path overrides prefix and delimiter.
361 b956618e Antony Chazapis
    virtual = True
362 b956618e Antony Chazapis
    if path:
363 b956618e Antony Chazapis
        prefix = path
364 b956618e Antony Chazapis
        delimiter = '/'
365 b956618e Antony Chazapis
        virtual = False
366 b956618e Antony Chazapis
    
367 b956618e Antony Chazapis
    # Naming policy.
368 b956618e Antony Chazapis
    if prefix and delimiter:
369 b956618e Antony Chazapis
        prefix = prefix + delimiter
370 b956618e Antony Chazapis
    if not prefix:
371 b956618e Antony Chazapis
        prefix = ''
372 b956618e Antony Chazapis
    prefix = prefix.lstrip('/')
373 b956618e Antony Chazapis
    
374 b956618e Antony Chazapis
    marker = request.GET.get('marker')
375 b956618e Antony Chazapis
    limit = request.GET.get('limit')
376 b956618e Antony Chazapis
    if limit:
377 b956618e Antony Chazapis
        try:
378 b956618e Antony Chazapis
            limit = int(limit)
379 b956618e Antony Chazapis
            if limit <= 0:
380 b956618e Antony Chazapis
                raise ValueError
381 b956618e Antony Chazapis
        except ValueError:
382 b956618e Antony Chazapis
            limit = 10000
383 b956618e Antony Chazapis
    
384 22dab079 Antony Chazapis
    keys = request.GET.get('meta')
385 22dab079 Antony Chazapis
    if keys:
386 22dab079 Antony Chazapis
        keys = keys.split(',')
387 02c0c3fa Antony Chazapis
        keys = [format_header_key('X-Object-Meta-' + x.strip()) for x in keys if x.strip() != '']
388 22dab079 Antony Chazapis
    else:
389 22dab079 Antony Chazapis
        keys = []
390 22dab079 Antony Chazapis
    
391 b956618e Antony Chazapis
    try:
392 83dd59c5 Antony Chazapis
        objects = backend.list_objects(request.user, v_account, v_container, prefix, delimiter, marker, limit, virtual, keys, until)
393 cca6c617 Antony Chazapis
    except NotAllowedError:
394 cca6c617 Antony Chazapis
        raise Unauthorized('Access denied')
395 b956618e Antony Chazapis
    except NameError:
396 b956618e Antony Chazapis
        raise ItemNotFound('Container does not exist')
397 b956618e Antony Chazapis
    
398 b956618e Antony Chazapis
    if request.serialization == 'text':
399 b956618e Antony Chazapis
        if len(objects) == 0:
400 b956618e Antony Chazapis
            # The cloudfiles python bindings expect 200 if json/xml.
401 b956618e Antony Chazapis
            response.status_code = 204
402 b956618e Antony Chazapis
            return response
403 b956618e Antony Chazapis
        response.status_code = 200
404 83dd59c5 Antony Chazapis
        response.content = '\n'.join([x[0] for x in objects]) + '\n'
405 b956618e Antony Chazapis
        return response
406 b956618e Antony Chazapis
    
407 b956618e Antony Chazapis
    object_meta = []
408 b956618e Antony Chazapis
    for x in objects:
409 83dd59c5 Antony Chazapis
        if x[1] is None:
410 b956618e Antony Chazapis
            # Virtual objects/directories.
411 83dd59c5 Antony Chazapis
            object_meta.append({'subdir': x[0]})
412 83dd59c5 Antony Chazapis
        else:
413 83dd59c5 Antony Chazapis
            try:
414 83dd59c5 Antony Chazapis
                meta = backend.get_object_meta(request.user, v_account, v_container, x[0], x[1])
415 e8886082 Antony Chazapis
                if until is None:
416 e8886082 Antony Chazapis
                    permissions = backend.get_object_permissions(request.user, v_account, v_container, x[0])
417 e0f916bb Antony Chazapis
                    public = backend.get_object_public(request.user, v_account, v_container, x[0])
418 e8886082 Antony Chazapis
                else:
419 e8886082 Antony Chazapis
                    permissions = None
420 e0f916bb Antony Chazapis
                    public = None
421 cca6c617 Antony Chazapis
            except NotAllowedError:
422 cca6c617 Antony Chazapis
                raise Unauthorized('Access denied')
423 83dd59c5 Antony Chazapis
            except NameError:
424 83dd59c5 Antony Chazapis
                pass
425 038f1ae9 Antony Chazapis
            else:
426 038f1ae9 Antony Chazapis
                update_sharing_meta(permissions, v_account, v_container, x[0], meta)
427 038f1ae9 Antony Chazapis
                update_public_meta(public, meta)
428 038f1ae9 Antony Chazapis
                object_meta.append(printable_header_dict(meta))
429 b956618e Antony Chazapis
    if request.serialization == 'xml':
430 b956618e Antony Chazapis
        data = render_to_string('objects.xml', {'container': v_container, 'objects': object_meta})
431 b956618e Antony Chazapis
    elif request.serialization  == 'json':
432 b956618e Antony Chazapis
        data = json.dumps(object_meta)
433 b956618e Antony Chazapis
    response.status_code = 200
434 b956618e Antony Chazapis
    response.content = data
435 b956618e Antony Chazapis
    return response
436 b956618e Antony Chazapis
437 b956618e Antony Chazapis
@api_method('HEAD')
438 b956618e Antony Chazapis
def object_meta(request, v_account, v_container, v_object):
439 b956618e Antony Chazapis
    # Normal Response Codes: 204
440 b956618e Antony Chazapis
    # Error Response Codes: serviceUnavailable (503),
441 b956618e Antony Chazapis
    #                       itemNotFound (404),
442 b956618e Antony Chazapis
    #                       unauthorized (401),
443 b956618e Antony Chazapis
    #                       badRequest (400)
444 b956618e Antony Chazapis
    
445 104626e3 Antony Chazapis
    version = request.GET.get('version')
446 b956618e Antony Chazapis
    try:
447 83dd59c5 Antony Chazapis
        meta = backend.get_object_meta(request.user, v_account, v_container, v_object, version)
448 e8886082 Antony Chazapis
        if version is None:
449 e8886082 Antony Chazapis
            permissions = backend.get_object_permissions(request.user, v_account, v_container, v_object)
450 e0f916bb Antony Chazapis
            public = backend.get_object_public(request.user, v_account, v_container, v_object)
451 e8886082 Antony Chazapis
        else:
452 e8886082 Antony Chazapis
            permissions = None
453 e0f916bb Antony Chazapis
            public = None
454 cca6c617 Antony Chazapis
    except NotAllowedError:
455 cca6c617 Antony Chazapis
        raise Unauthorized('Access denied')
456 b956618e Antony Chazapis
    except NameError:
457 b956618e Antony Chazapis
        raise ItemNotFound('Object does not exist')
458 58a6c894 Antony Chazapis
    except IndexError:
459 58a6c894 Antony Chazapis
        raise ItemNotFound('Version does not exist')
460 b956618e Antony Chazapis
    
461 8cb45c13 Antony Chazapis
    update_manifest_meta(request, v_account, meta)
462 cca6c617 Antony Chazapis
    update_sharing_meta(permissions, v_account, v_container, v_object, meta)
463 e0f916bb Antony Chazapis
    update_public_meta(public, meta)
464 8cb45c13 Antony Chazapis
    
465 cb146cf9 Antony Chazapis
    response = HttpResponse(status=200)
466 02c0c3fa Antony Chazapis
    put_object_headers(response, meta)
467 b956618e Antony Chazapis
    return response
468 b956618e Antony Chazapis
469 22dab079 Antony Chazapis
@api_method('GET', format_allowed=True)
470 b956618e Antony Chazapis
def object_read(request, v_account, v_container, v_object):
471 b956618e Antony Chazapis
    # Normal Response Codes: 200, 206
472 b956618e Antony Chazapis
    # Error Response Codes: serviceUnavailable (503),
473 b956618e Antony Chazapis
    #                       rangeNotSatisfiable (416),
474 b956618e Antony Chazapis
    #                       preconditionFailed (412),
475 b956618e Antony Chazapis
    #                       itemNotFound (404),
476 b956618e Antony Chazapis
    #                       unauthorized (401),
477 b956618e Antony Chazapis
    #                       badRequest (400),
478 b956618e Antony Chazapis
    #                       notModified (304)
479 b956618e Antony Chazapis
    
480 104626e3 Antony Chazapis
    version = request.GET.get('version')
481 e8886082 Antony Chazapis
    
482 e8886082 Antony Chazapis
    # Reply with the version list. Do this first, as the object may be deleted.
483 104626e3 Antony Chazapis
    if version == 'list':
484 e8886082 Antony Chazapis
        if request.serialization == 'text':
485 e8886082 Antony Chazapis
            raise BadRequest('No format specified for version list.')
486 e8886082 Antony Chazapis
        
487 cca6c617 Antony Chazapis
        try:
488 cca6c617 Antony Chazapis
            v = backend.list_versions(request.user, v_account, v_container, v_object)
489 cca6c617 Antony Chazapis
        except NotAllowedError:
490 cca6c617 Antony Chazapis
            raise Unauthorized('Access denied')
491 cca6c617 Antony Chazapis
        d = {'versions': v}
492 e8886082 Antony Chazapis
        if request.serialization == 'xml':
493 e8886082 Antony Chazapis
            d['object'] = v_object
494 e8886082 Antony Chazapis
            data = render_to_string('versions.xml', d)
495 e8886082 Antony Chazapis
        elif request.serialization  == 'json':
496 e8886082 Antony Chazapis
            data = json.dumps(d)
497 e8886082 Antony Chazapis
        
498 e8886082 Antony Chazapis
        response = HttpResponse(data, status=200)
499 e8886082 Antony Chazapis
        response['Content-Length'] = len(data)
500 e8886082 Antony Chazapis
        return response
501 e8886082 Antony Chazapis
    
502 b956618e Antony Chazapis
    try:
503 83dd59c5 Antony Chazapis
        meta = backend.get_object_meta(request.user, v_account, v_container, v_object, version)
504 e8886082 Antony Chazapis
        if version is None:
505 e8886082 Antony Chazapis
            permissions = backend.get_object_permissions(request.user, v_account, v_container, v_object)
506 307915f1 Antony Chazapis
            public = backend.get_object_public(request.user, v_account, v_container, v_object)
507 e8886082 Antony Chazapis
        else:
508 e8886082 Antony Chazapis
            permissions = None
509 e0f916bb Antony Chazapis
            public = None
510 cca6c617 Antony Chazapis
    except NotAllowedError:
511 cca6c617 Antony Chazapis
        raise Unauthorized('Access denied')
512 b956618e Antony Chazapis
    except NameError:
513 b956618e Antony Chazapis
        raise ItemNotFound('Object does not exist')
514 58a6c894 Antony Chazapis
    except IndexError:
515 58a6c894 Antony Chazapis
        raise ItemNotFound('Version does not exist')
516 b956618e Antony Chazapis
    
517 8cb45c13 Antony Chazapis
    update_manifest_meta(request, v_account, meta)
518 cca6c617 Antony Chazapis
    update_sharing_meta(permissions, v_account, v_container, v_object, meta)
519 e0f916bb Antony Chazapis
    update_public_meta(public, meta)
520 8cb45c13 Antony Chazapis
    
521 22dab079 Antony Chazapis
    # Evaluate conditions.
522 b956618e Antony Chazapis
    validate_modification_preconditions(request, meta)
523 22dab079 Antony Chazapis
    try:
524 22dab079 Antony Chazapis
        validate_matching_preconditions(request, meta)
525 22dab079 Antony Chazapis
    except NotModified:
526 22dab079 Antony Chazapis
        response = HttpResponse(status=304)
527 22dab079 Antony Chazapis
        response['ETag'] = meta['hash']
528 22dab079 Antony Chazapis
        return response
529 b956618e Antony Chazapis
    
530 8cb45c13 Antony Chazapis
    sizes = []
531 8cb45c13 Antony Chazapis
    hashmaps = []
532 8cb45c13 Antony Chazapis
    if 'X-Object-Manifest' in meta:
533 8cb45c13 Antony Chazapis
        try:
534 6d817842 Antony Chazapis
            src_container, src_name = split_container_object_string('/' + meta['X-Object-Manifest'])
535 8cb45c13 Antony Chazapis
            objects = backend.list_objects(request.user, v_account, src_container, prefix=src_name, virtual=False)
536 cca6c617 Antony Chazapis
        except NotAllowedError:
537 cca6c617 Antony Chazapis
            raise Unauthorized('Access denied')
538 8cb45c13 Antony Chazapis
        except ValueError:
539 8cb45c13 Antony Chazapis
            raise BadRequest('Invalid X-Object-Manifest header')
540 8cb45c13 Antony Chazapis
        except NameError:
541 8cb45c13 Antony Chazapis
            raise ItemNotFound('Container does not exist')
542 8cb45c13 Antony Chazapis
        
543 8cb45c13 Antony Chazapis
        try:
544 8cb45c13 Antony Chazapis
            for x in objects:
545 8cb45c13 Antony Chazapis
                s, h = backend.get_object_hashmap(request.user, v_account, src_container, x[0], x[1])
546 8cb45c13 Antony Chazapis
                sizes.append(s)
547 8cb45c13 Antony Chazapis
                hashmaps.append(h)
548 cca6c617 Antony Chazapis
        except NotAllowedError:
549 cca6c617 Antony Chazapis
            raise Unauthorized('Access denied')
550 8cb45c13 Antony Chazapis
        except NameError:
551 8cb45c13 Antony Chazapis
            raise ItemNotFound('Object does not exist')
552 8cb45c13 Antony Chazapis
        except IndexError:
553 8cb45c13 Antony Chazapis
            raise ItemNotFound('Version does not exist')
554 8cb45c13 Antony Chazapis
    else:
555 8cb45c13 Antony Chazapis
        try:
556 8cb45c13 Antony Chazapis
            s, h = backend.get_object_hashmap(request.user, v_account, v_container, v_object, version)
557 8cb45c13 Antony Chazapis
            sizes.append(s)
558 8cb45c13 Antony Chazapis
            hashmaps.append(h)
559 cca6c617 Antony Chazapis
        except NotAllowedError:
560 cca6c617 Antony Chazapis
            raise Unauthorized('Access denied')
561 8cb45c13 Antony Chazapis
        except NameError:
562 8cb45c13 Antony Chazapis
            raise ItemNotFound('Object does not exist')
563 8cb45c13 Antony Chazapis
        except IndexError:
564 8cb45c13 Antony Chazapis
            raise ItemNotFound('Version does not exist')
565 b956618e Antony Chazapis
    
566 22dab079 Antony Chazapis
    # Reply with the hashmap.
567 22dab079 Antony Chazapis
    if request.serialization != 'text':
568 8cb45c13 Antony Chazapis
        size = sum(sizes)
569 8cb45c13 Antony Chazapis
        hashmap = sum(hashmaps, [])
570 6bc372a4 Antony Chazapis
        d = {'block_size': backend.block_size, 'block_hash': backend.hash_algorithm, 'bytes': size, 'hashes': hashmap}
571 22dab079 Antony Chazapis
        if request.serialization == 'xml':
572 6bc372a4 Antony Chazapis
            d['object'] = v_object
573 6bc372a4 Antony Chazapis
            data = render_to_string('hashes.xml', d)
574 22dab079 Antony Chazapis
        elif request.serialization  == 'json':
575 6bc372a4 Antony Chazapis
            data = json.dumps(d)
576 22dab079 Antony Chazapis
        
577 22dab079 Antony Chazapis
        response = HttpResponse(data, status=200)
578 02c0c3fa Antony Chazapis
        put_object_headers(response, meta)
579 22dab079 Antony Chazapis
        response['Content-Length'] = len(data)
580 22dab079 Antony Chazapis
        return response
581 22dab079 Antony Chazapis
    
582 8cb45c13 Antony Chazapis
    return object_data_response(request, sizes, hashmaps, meta)
583 b956618e Antony Chazapis
584 76985443 Sofia Papagiannaki
@api_method('PUT', format_allowed=True)
585 b956618e Antony Chazapis
def object_write(request, v_account, v_container, v_object):
586 b956618e Antony Chazapis
    # Normal Response Codes: 201
587 b956618e Antony Chazapis
    # Error Response Codes: serviceUnavailable (503),
588 b956618e Antony Chazapis
    #                       unprocessableEntity (422),
589 b956618e Antony Chazapis
    #                       lengthRequired (411),
590 3436eeb0 Antony Chazapis
    #                       conflict (409),
591 b956618e Antony Chazapis
    #                       itemNotFound (404),
592 b956618e Antony Chazapis
    #                       unauthorized (401),
593 b956618e Antony Chazapis
    #                       badRequest (400)
594 7278d371 Antony Chazapis
    
595 7278d371 Antony Chazapis
    if not request.GET.get('format'):
596 7278d371 Antony Chazapis
        request.serialization = 'text'
597 7278d371 Antony Chazapis
    
598 b956618e Antony Chazapis
    copy_from = request.META.get('HTTP_X_COPY_FROM')
599 b956618e Antony Chazapis
    move_from = request.META.get('HTTP_X_MOVE_FROM')
600 b956618e Antony Chazapis
    if copy_from or move_from:
601 ab2e317e Antony Chazapis
        content_length = get_content_length(request) # Required by the API.
602 b956618e Antony Chazapis
        
603 b956618e Antony Chazapis
        if move_from:
604 83dd59c5 Antony Chazapis
            try:
605 83dd59c5 Antony Chazapis
                src_container, src_name = split_container_object_string(move_from)
606 83dd59c5 Antony Chazapis
            except ValueError:
607 83dd59c5 Antony Chazapis
                raise BadRequest('Invalid X-Move-From header')
608 83dd59c5 Antony Chazapis
            copy_or_move_object(request, v_account, src_container, src_name, v_container, v_object, move=True)
609 b956618e Antony Chazapis
        else:
610 83dd59c5 Antony Chazapis
            try:
611 83dd59c5 Antony Chazapis
                src_container, src_name = split_container_object_string(copy_from)
612 83dd59c5 Antony Chazapis
            except ValueError:
613 83dd59c5 Antony Chazapis
                raise BadRequest('Invalid X-Copy-From header')
614 83dd59c5 Antony Chazapis
            copy_or_move_object(request, v_account, src_container, src_name, v_container, v_object, move=False)
615 b956618e Antony Chazapis
        return HttpResponse(status=201)
616 b956618e Antony Chazapis
    
617 3ab38c43 Antony Chazapis
    meta, permissions, public = get_object_headers(request)
618 b956618e Antony Chazapis
    content_length = -1
619 b956618e Antony Chazapis
    if request.META.get('HTTP_TRANSFER_ENCODING') != 'chunked':
620 22dab079 Antony Chazapis
        content_length = get_content_length(request)
621 b956618e Antony Chazapis
    # Should be BadRequest, but API says otherwise.
622 b956618e Antony Chazapis
    if 'Content-Type' not in meta:
623 b956618e Antony Chazapis
        raise LengthRequired('Missing Content-Type header')
624 b956618e Antony Chazapis
    
625 76985443 Sofia Papagiannaki
    if request.serialization == 'json':
626 76985443 Sofia Papagiannaki
        data = ''
627 76985443 Sofia Papagiannaki
        sock = raw_input_socket(request)
628 76985443 Sofia Papagiannaki
        for block in socket_read_iterator(sock, content_length, backend.block_size):
629 76985443 Sofia Papagiannaki
            data = '%s%s' % (data, block)
630 76985443 Sofia Papagiannaki
        d = json.loads(data)
631 76985443 Sofia Papagiannaki
        if not hasattr(d, '__getitem__'):
632 76985443 Sofia Papagiannaki
            raise BadRequest('Invalid data formating')
633 76985443 Sofia Papagiannaki
        try:
634 76985443 Sofia Papagiannaki
            hashmap = d['hashes']
635 76985443 Sofia Papagiannaki
            size = d['bytes']
636 76985443 Sofia Papagiannaki
        except KeyError:
637 76985443 Sofia Papagiannaki
            raise BadRequest('Invalid data formatting')
638 76985443 Sofia Papagiannaki
        meta.update({'hash': hashmap_hash(hashmap)}) # Update ETag.
639 ce2eba7e Sofia Papagiannaki
    elif request.serialization == 'xml':
640 76985443 Sofia Papagiannaki
        #TODO support for xml
641 76985443 Sofia Papagiannaki
        raise BadRequest('Format xml is not supported')
642 76985443 Sofia Papagiannaki
    else:
643 76985443 Sofia Papagiannaki
        md5 = hashlib.md5()
644 76985443 Sofia Papagiannaki
        size = 0
645 76985443 Sofia Papagiannaki
        hashmap = []
646 76985443 Sofia Papagiannaki
        sock = raw_input_socket(request)
647 76985443 Sofia Papagiannaki
        for data in socket_read_iterator(sock, content_length, backend.block_size):
648 76985443 Sofia Papagiannaki
            # TODO: Raise 408 (Request Timeout) if this takes too long.
649 76985443 Sofia Papagiannaki
            # TODO: Raise 499 (Client Disconnect) if a length is defined and we stop before getting this much data.
650 76985443 Sofia Papagiannaki
            size += len(data)
651 76985443 Sofia Papagiannaki
            hashmap.append(backend.put_block(data))
652 76985443 Sofia Papagiannaki
            md5.update(data)
653 76985443 Sofia Papagiannaki
        
654 76985443 Sofia Papagiannaki
        meta['hash'] = md5.hexdigest().lower()
655 76985443 Sofia Papagiannaki
        etag = request.META.get('HTTP_ETAG')
656 76985443 Sofia Papagiannaki
        if etag and parse_etags(etag)[0].lower() != meta['hash']:
657 76985443 Sofia Papagiannaki
            raise UnprocessableEntity('Object ETag does not match')
658 22dab079 Antony Chazapis
    
659 22dab079 Antony Chazapis
    try:
660 3436eeb0 Antony Chazapis
        backend.update_object_hashmap(request.user, v_account, v_container, v_object, size, hashmap, meta, True, permissions)
661 cca6c617 Antony Chazapis
    except NotAllowedError:
662 cca6c617 Antony Chazapis
        raise Unauthorized('Access denied')
663 76985443 Sofia Papagiannaki
    except IndexError, e:
664 1993fea9 Antony Chazapis
        raise Conflict(json.dumps(e.data))
665 22dab079 Antony Chazapis
    except NameError:
666 22dab079 Antony Chazapis
        raise ItemNotFound('Container does not exist')
667 3436eeb0 Antony Chazapis
    except ValueError:
668 3436eeb0 Antony Chazapis
        raise BadRequest('Invalid sharing header')
669 1993fea9 Antony Chazapis
    except AttributeError, e:
670 1993fea9 Antony Chazapis
        raise Conflict(json.dumps(e.data))
671 e0f916bb Antony Chazapis
    if public is not None:
672 e0f916bb Antony Chazapis
        try:
673 e0f916bb Antony Chazapis
            backend.update_object_public(request.user, v_account, v_container, v_object, public)
674 e0f916bb Antony Chazapis
        except NotAllowedError:
675 e0f916bb Antony Chazapis
            raise Unauthorized('Access denied')
676 e0f916bb Antony Chazapis
        except NameError:
677 e0f916bb Antony Chazapis
            raise ItemNotFound('Object does not exist')
678 b956618e Antony Chazapis
    
679 1993fea9 Antony Chazapis
    response = HttpResponse(status=201)
680 b956618e Antony Chazapis
    response['ETag'] = meta['hash']
681 b956618e Antony Chazapis
    return response
682 b956618e Antony Chazapis
683 b956618e Antony Chazapis
@api_method('COPY')
684 b956618e Antony Chazapis
def object_copy(request, v_account, v_container, v_object):
685 b956618e Antony Chazapis
    # Normal Response Codes: 201
686 b956618e Antony Chazapis
    # Error Response Codes: serviceUnavailable (503),
687 b956618e Antony Chazapis
    #                       itemNotFound (404),
688 b956618e Antony Chazapis
    #                       unauthorized (401),
689 b956618e Antony Chazapis
    #                       badRequest (400)
690 b956618e Antony Chazapis
    
691 b956618e Antony Chazapis
    dest_path = request.META.get('HTTP_DESTINATION')
692 b956618e Antony Chazapis
    if not dest_path:
693 b956618e Antony Chazapis
        raise BadRequest('Missing Destination header')
694 83dd59c5 Antony Chazapis
    try:
695 83dd59c5 Antony Chazapis
        dest_container, dest_name = split_container_object_string(dest_path)
696 83dd59c5 Antony Chazapis
    except ValueError:
697 83dd59c5 Antony Chazapis
        raise BadRequest('Invalid Destination header')
698 83dd59c5 Antony Chazapis
    copy_or_move_object(request, v_account, v_container, v_object, dest_container, dest_name, move=False)
699 b956618e Antony Chazapis
    return HttpResponse(status=201)
700 b956618e Antony Chazapis
701 b956618e Antony Chazapis
@api_method('MOVE')
702 b956618e Antony Chazapis
def object_move(request, v_account, v_container, v_object):
703 b956618e Antony Chazapis
    # Normal Response Codes: 201
704 b956618e Antony Chazapis
    # Error Response Codes: serviceUnavailable (503),
705 b956618e Antony Chazapis
    #                       itemNotFound (404),
706 b956618e Antony Chazapis
    #                       unauthorized (401),
707 b956618e Antony Chazapis
    #                       badRequest (400)
708 b956618e Antony Chazapis
    
709 b956618e Antony Chazapis
    dest_path = request.META.get('HTTP_DESTINATION')
710 b956618e Antony Chazapis
    if not dest_path:
711 b956618e Antony Chazapis
        raise BadRequest('Missing Destination header')
712 83dd59c5 Antony Chazapis
    try:
713 83dd59c5 Antony Chazapis
        dest_container, dest_name = split_container_object_string(dest_path)
714 83dd59c5 Antony Chazapis
    except ValueError:
715 83dd59c5 Antony Chazapis
        raise BadRequest('Invalid Destination header')
716 83dd59c5 Antony Chazapis
    copy_or_move_object(request, v_account, v_container, v_object, dest_container, dest_name, move=True)
717 b956618e Antony Chazapis
    return HttpResponse(status=201)
718 b956618e Antony Chazapis
719 b956618e Antony Chazapis
@api_method('POST')
720 b956618e Antony Chazapis
def object_update(request, v_account, v_container, v_object):
721 e9285524 Antony Chazapis
    # Normal Response Codes: 202, 204
722 b956618e Antony Chazapis
    # Error Response Codes: serviceUnavailable (503),
723 3436eeb0 Antony Chazapis
    #                       conflict (409),
724 b956618e Antony Chazapis
    #                       itemNotFound (404),
725 b956618e Antony Chazapis
    #                       unauthorized (401),
726 b956618e Antony Chazapis
    #                       badRequest (400)
727 b956618e Antony Chazapis
    
728 3ab38c43 Antony Chazapis
    meta, permissions, public = get_object_headers(request)
729 22dab079 Antony Chazapis
    content_type = meta.get('Content-Type')
730 22dab079 Antony Chazapis
    if content_type:
731 b956618e Antony Chazapis
        del(meta['Content-Type']) # Do not allow changing the Content-Type.
732 22dab079 Antony Chazapis
    
733 cfe6939d Antony Chazapis
    try:
734 83dd59c5 Antony Chazapis
        prev_meta = backend.get_object_meta(request.user, v_account, v_container, v_object)
735 cca6c617 Antony Chazapis
    except NotAllowedError:
736 cca6c617 Antony Chazapis
        raise Unauthorized('Access denied')
737 cfe6939d Antony Chazapis
    except NameError:
738 cfe6939d Antony Chazapis
        raise ItemNotFound('Object does not exist')
739 ac62f6da Antony Chazapis
    # If replacing, keep previous values of 'Content-Type' and 'hash'.
740 ac62f6da Antony Chazapis
    replace = True
741 ac62f6da Antony Chazapis
    if 'update' in request.GET:
742 ac62f6da Antony Chazapis
        replace = False
743 ac62f6da Antony Chazapis
    if replace:
744 22dab079 Antony Chazapis
        for k in ('Content-Type', 'hash'):
745 22dab079 Antony Chazapis
            if k in prev_meta:
746 22dab079 Antony Chazapis
                meta[k] = prev_meta[k]
747 22dab079 Antony Chazapis
    
748 ab2e317e Antony Chazapis
    # A Content-Type or X-Source-Object header indicates data updates.
749 ab2e317e Antony Chazapis
    src_object = request.META.get('HTTP_X_SOURCE_OBJECT')
750 ab2e317e Antony Chazapis
    if (not content_type or content_type != 'application/octet-stream') and not src_object:
751 cca6c617 Antony Chazapis
        # Do permissions first, as it may fail easier.
752 cca6c617 Antony Chazapis
        if permissions is not None:
753 ac62f6da Antony Chazapis
            try:
754 ac62f6da Antony Chazapis
                backend.update_object_permissions(request.user, v_account, v_container, v_object, permissions)
755 cca6c617 Antony Chazapis
            except NotAllowedError:
756 cca6c617 Antony Chazapis
                raise Unauthorized('Access denied')
757 ac62f6da Antony Chazapis
            except NameError:
758 ac62f6da Antony Chazapis
                raise ItemNotFound('Object does not exist')
759 ac62f6da Antony Chazapis
            except ValueError:
760 ac62f6da Antony Chazapis
                raise BadRequest('Invalid sharing header')
761 1993fea9 Antony Chazapis
            except AttributeError, e:
762 1993fea9 Antony Chazapis
                raise Conflict(json.dumps(e.data))
763 e0f916bb Antony Chazapis
        if public is not None:
764 e0f916bb Antony Chazapis
            try:
765 e0f916bb Antony Chazapis
                backend.update_object_public(request.user, v_account, v_container, v_object, public)
766 e0f916bb Antony Chazapis
            except NotAllowedError:
767 e0f916bb Antony Chazapis
                raise Unauthorized('Access denied')
768 e0f916bb Antony Chazapis
            except NameError:
769 e0f916bb Antony Chazapis
                raise ItemNotFound('Object does not exist')
770 cca6c617 Antony Chazapis
        try:
771 cca6c617 Antony Chazapis
            backend.update_object_meta(request.user, v_account, v_container, v_object, meta, replace)
772 cca6c617 Antony Chazapis
        except NotAllowedError:
773 cca6c617 Antony Chazapis
            raise Unauthorized('Access denied')
774 cca6c617 Antony Chazapis
        except NameError:
775 cca6c617 Antony Chazapis
            raise ItemNotFound('Object does not exist')
776 cfe6939d Antony Chazapis
        return HttpResponse(status=202)
777 ac62f6da Antony Chazapis
    
778 cfe6939d Antony Chazapis
    # Single range update. Range must be in Content-Range.
779 22dab079 Antony Chazapis
    # Based on: http://code.google.com/p/gears/wiki/ContentRangePostProposal
780 cfe6939d Antony Chazapis
    # (with the addition that '*' is allowed for the range - will append).
781 22dab079 Antony Chazapis
    content_range = request.META.get('HTTP_CONTENT_RANGE')
782 22dab079 Antony Chazapis
    if not content_range:
783 ac62f6da Antony Chazapis
        raise BadRequest('Missing Content-Range header')
784 22dab079 Antony Chazapis
    ranges = get_content_range(request)
785 22dab079 Antony Chazapis
    if not ranges:
786 ac62f6da Antony Chazapis
        raise RangeNotSatisfiable('Invalid Content-Range header')
787 22dab079 Antony Chazapis
    
788 cfe6939d Antony Chazapis
    try:
789 83dd59c5 Antony Chazapis
        size, hashmap = backend.get_object_hashmap(request.user, v_account, v_container, v_object)
790 cca6c617 Antony Chazapis
    except NotAllowedError:
791 cca6c617 Antony Chazapis
        raise Unauthorized('Access denied')
792 cfe6939d Antony Chazapis
    except NameError:
793 cfe6939d Antony Chazapis
        raise ItemNotFound('Object does not exist')
794 cfe6939d Antony Chazapis
    
795 cfe6939d Antony Chazapis
    offset, length, total = ranges
796 cfe6939d Antony Chazapis
    if offset is None:
797 cfe6939d Antony Chazapis
        offset = size
798 cb146cf9 Antony Chazapis
    elif offset > size:
799 cb146cf9 Antony Chazapis
        raise RangeNotSatisfiable('Supplied offset is beyond object limits')
800 ab2e317e Antony Chazapis
    if src_object:
801 ab2e317e Antony Chazapis
        src_container, src_name = split_container_object_string(src_object)
802 ab2e317e Antony Chazapis
        src_version = request.META.get('HTTP_X_SOURCE_VERSION')
803 ab2e317e Antony Chazapis
        try:
804 ab2e317e Antony Chazapis
            src_size, src_hashmap = backend.get_object_hashmap(request.user, v_account, src_container, src_name, src_version)
805 ab2e317e Antony Chazapis
        except NotAllowedError:
806 ab2e317e Antony Chazapis
            raise Unauthorized('Access denied')
807 ab2e317e Antony Chazapis
        except NameError:
808 ab2e317e Antony Chazapis
            raise ItemNotFound('Source object does not exist')
809 ab2e317e Antony Chazapis
        
810 ab2e317e Antony Chazapis
        if length is None:
811 ab2e317e Antony Chazapis
            length = src_size
812 ab2e317e Antony Chazapis
        elif length > src_size:
813 ab2e317e Antony Chazapis
            raise BadRequest('Object length is smaller than range length')
814 ab2e317e Antony Chazapis
    else:
815 ab2e317e Antony Chazapis
        # Require either a Content-Length, or 'chunked' Transfer-Encoding.
816 ab2e317e Antony Chazapis
        content_length = -1
817 ab2e317e Antony Chazapis
        if request.META.get('HTTP_TRANSFER_ENCODING') != 'chunked':
818 ab2e317e Antony Chazapis
            content_length = get_content_length(request)
819 ab2e317e Antony Chazapis
        
820 ab2e317e Antony Chazapis
        if length is None:
821 ab2e317e Antony Chazapis
            length = content_length
822 ab2e317e Antony Chazapis
        else:
823 ab2e317e Antony Chazapis
            if content_length == -1:
824 ab2e317e Antony Chazapis
                # TODO: Get up to length bytes in chunks.
825 ab2e317e Antony Chazapis
                length = content_length
826 ab2e317e Antony Chazapis
            elif length != content_length:
827 ab2e317e Antony Chazapis
                raise BadRequest('Content length does not match range length')
828 cfe6939d Antony Chazapis
    if total is not None and (total != size or offset >= size or (length > 0 and offset + length >= size)):
829 cfe6939d Antony Chazapis
        raise RangeNotSatisfiable('Supplied range will change provided object limits')
830 cfe6939d Antony Chazapis
    
831 1495b972 Antony Chazapis
    dest_bytes = request.META.get('HTTP_X_OBJECT_BYTES')
832 1495b972 Antony Chazapis
    if dest_bytes is not None:
833 1495b972 Antony Chazapis
        dest_bytes = get_int_parameter(dest_bytes)
834 1495b972 Antony Chazapis
        if dest_bytes is None:
835 1495b972 Antony Chazapis
            raise BadRequest('Invalid X-Object-Bytes header')
836 1495b972 Antony Chazapis
    
837 ab2e317e Antony Chazapis
    if src_object:
838 ab2e317e Antony Chazapis
        if offset % backend.block_size == 0:
839 ab2e317e Antony Chazapis
            # Update the hashes only.
840 ab2e317e Antony Chazapis
            sbi = 0
841 ab2e317e Antony Chazapis
            while length > 0:
842 ab2e317e Antony Chazapis
                bi = int(offset / backend.block_size)
843 ab2e317e Antony Chazapis
                bl = min(length, backend.block_size)
844 ab2e317e Antony Chazapis
                if bi < len(hashmap):
845 ab2e317e Antony Chazapis
                    if bl == backend.block_size:
846 ab2e317e Antony Chazapis
                        hashmap[bi] = src_hashmap[sbi]
847 ab2e317e Antony Chazapis
                    else:
848 ab2e317e Antony Chazapis
                        data = backend.get_block(src_hashmap[sbi])
849 ab2e317e Antony Chazapis
                        hashmap[bi] = backend.update_block(hashmap[bi], data[:bl], 0)
850 ab2e317e Antony Chazapis
                else:
851 ab2e317e Antony Chazapis
                    hashmap.append(src_hashmap[sbi])
852 ab2e317e Antony Chazapis
                offset += bl
853 ab2e317e Antony Chazapis
                length -= bl
854 ab2e317e Antony Chazapis
                sbi += 1
855 ab2e317e Antony Chazapis
        else:
856 ab2e317e Antony Chazapis
            data = ''
857 ab2e317e Antony Chazapis
            sbi = 0
858 ab2e317e Antony Chazapis
            while length > 0:
859 ab2e317e Antony Chazapis
                data += backend.get_block(src_hashmap[sbi])
860 ab2e317e Antony Chazapis
                if length < backend.block_size:
861 ab2e317e Antony Chazapis
                    data = data[:length]
862 ab2e317e Antony Chazapis
                bytes = put_object_block(hashmap, data, offset)
863 ab2e317e Antony Chazapis
                offset += bytes
864 ab2e317e Antony Chazapis
                data = data[bytes:]
865 ab2e317e Antony Chazapis
                length -= bytes
866 ab2e317e Antony Chazapis
                sbi += 1
867 ab2e317e Antony Chazapis
    else:
868 ab2e317e Antony Chazapis
        sock = raw_input_socket(request)
869 ab2e317e Antony Chazapis
        data = ''
870 ab2e317e Antony Chazapis
        for d in socket_read_iterator(sock, length, backend.block_size):
871 ab2e317e Antony Chazapis
            # TODO: Raise 408 (Request Timeout) if this takes too long.
872 ab2e317e Antony Chazapis
            # TODO: Raise 499 (Client Disconnect) if a length is defined and we stop before getting this much data.
873 ab2e317e Antony Chazapis
            data += d
874 ab2e317e Antony Chazapis
            bytes = put_object_block(hashmap, data, offset)
875 ab2e317e Antony Chazapis
            offset += bytes
876 ab2e317e Antony Chazapis
            data = data[bytes:]
877 ab2e317e Antony Chazapis
        if len(data) > 0:
878 ab2e317e Antony Chazapis
            put_object_block(hashmap, data, offset)
879 cfe6939d Antony Chazapis
    
880 cfe6939d Antony Chazapis
    if offset > size:
881 cfe6939d Antony Chazapis
        size = offset
882 1495b972 Antony Chazapis
    if dest_bytes is not None and dest_bytes < size:
883 1495b972 Antony Chazapis
        size = dest_bytes
884 1495b972 Antony Chazapis
        hashmap = hashmap[:(int((size - 1) / backend.block_size) + 1)]
885 ac62f6da Antony Chazapis
    meta.update({'hash': hashmap_hash(hashmap)}) # Update ETag.
886 cfe6939d Antony Chazapis
    try:
887 ac62f6da Antony Chazapis
        backend.update_object_hashmap(request.user, v_account, v_container, v_object, size, hashmap, meta, replace, permissions)
888 cca6c617 Antony Chazapis
    except NotAllowedError:
889 cca6c617 Antony Chazapis
        raise Unauthorized('Access denied')
890 cfe6939d Antony Chazapis
    except NameError:
891 cfe6939d Antony Chazapis
        raise ItemNotFound('Container does not exist')
892 3436eeb0 Antony Chazapis
    except ValueError:
893 3436eeb0 Antony Chazapis
        raise BadRequest('Invalid sharing header')
894 1993fea9 Antony Chazapis
    except AttributeError, e:
895 1993fea9 Antony Chazapis
        raise Conflict(json.dumps(e.data))
896 e0f916bb Antony Chazapis
    if public is not None:
897 e0f916bb Antony Chazapis
        try:
898 e0f916bb Antony Chazapis
            backend.update_object_public(request.user, v_account, v_container, v_object, public)
899 e0f916bb Antony Chazapis
        except NotAllowedError:
900 e0f916bb Antony Chazapis
            raise Unauthorized('Access denied')
901 e0f916bb Antony Chazapis
        except NameError:
902 e0f916bb Antony Chazapis
            raise ItemNotFound('Object does not exist')
903 3436eeb0 Antony Chazapis
    
904 e9285524 Antony Chazapis
    response = HttpResponse(status=204)
905 e9285524 Antony Chazapis
    response['ETag'] = meta['hash']
906 e9285524 Antony Chazapis
    return response
907 b956618e Antony Chazapis
908 b956618e Antony Chazapis
@api_method('DELETE')
909 b956618e Antony Chazapis
def object_delete(request, v_account, v_container, v_object):
910 b956618e Antony Chazapis
    # Normal Response Codes: 204
911 b956618e Antony Chazapis
    # Error Response Codes: serviceUnavailable (503),
912 b956618e Antony Chazapis
    #                       itemNotFound (404),
913 b956618e Antony Chazapis
    #                       unauthorized (401),
914 b956618e Antony Chazapis
    #                       badRequest (400)
915 b956618e Antony Chazapis
    
916 b956618e Antony Chazapis
    try:
917 83dd59c5 Antony Chazapis
        backend.delete_object(request.user, v_account, v_container, v_object)
918 cca6c617 Antony Chazapis
    except NotAllowedError:
919 cca6c617 Antony Chazapis
        raise Unauthorized('Access denied')
920 b956618e Antony Chazapis
    except NameError:
921 b956618e Antony Chazapis
        raise ItemNotFound('Object does not exist')
922 b956618e Antony Chazapis
    return HttpResponse(status=204)
923 b956618e Antony Chazapis
924 b956618e Antony Chazapis
@api_method()
925 b956618e Antony Chazapis
def method_not_allowed(request):
926 b956618e Antony Chazapis
    raise BadRequest('Method not allowed')