Statistics
| Branch: | Tag: | Revision:

root / pithos / api / functions.py @ 3436eeb0

History | View | Annotate | Download (27.7 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 b956618e Antony Chazapis
from django.http import HttpResponse
39 b956618e Antony Chazapis
from django.template.loader import render_to_string
40 b956618e Antony Chazapis
from django.utils import simplejson as json
41 b956618e Antony Chazapis
from django.utils.http import parse_etags
42 b956618e Antony Chazapis
43 b956618e Antony Chazapis
from pithos.api.faults import (Fault, NotModified, BadRequest, Unauthorized, ItemNotFound, Conflict,
44 b956618e Antony Chazapis
    LengthRequired, PreconditionFailed, RangeNotSatisfiable, UnprocessableEntity)
45 22dab079 Antony Chazapis
from pithos.api.util import (format_meta_key, printable_meta_dict, get_account_meta,
46 22dab079 Antony Chazapis
    put_account_meta, get_container_meta, put_container_meta, get_object_meta, put_object_meta,
47 3436eeb0 Antony Chazapis
    update_manifest_meta, format_permissions, validate_modification_preconditions,
48 3436eeb0 Antony Chazapis
    validate_matching_preconditions, split_container_object_string, copy_or_move_object,
49 3436eeb0 Antony Chazapis
    get_int_parameter, get_content_length, get_content_range, get_sharing, raw_input_socket,
50 3436eeb0 Antony Chazapis
    socket_read_iterator, object_data_response, put_object_block, hashmap_hash, api_method)
51 b956618e Antony Chazapis
from pithos.backends import backend
52 b956618e Antony Chazapis
53 b956618e Antony Chazapis
54 b956618e Antony Chazapis
logger = logging.getLogger(__name__)
55 b956618e Antony Chazapis
56 b956618e Antony Chazapis
57 b956618e Antony Chazapis
def top_demux(request):
58 b956618e Antony Chazapis
    if request.method == 'GET':
59 b956618e Antony Chazapis
        return authenticate(request)
60 b956618e Antony Chazapis
    else:
61 b956618e Antony Chazapis
        return method_not_allowed(request)
62 b956618e Antony Chazapis
63 b956618e Antony Chazapis
def account_demux(request, v_account):
64 b956618e Antony Chazapis
    if request.method == 'HEAD':
65 b956618e Antony Chazapis
        return account_meta(request, v_account)
66 b956618e Antony Chazapis
    elif request.method == 'POST':
67 b956618e Antony Chazapis
        return account_update(request, v_account)
68 83dd59c5 Antony Chazapis
    elif request.method == 'GET':
69 83dd59c5 Antony Chazapis
        return container_list(request, v_account)
70 b956618e Antony Chazapis
    else:
71 b956618e Antony Chazapis
        return method_not_allowed(request)
72 b956618e Antony Chazapis
73 b956618e Antony Chazapis
def container_demux(request, v_account, v_container):
74 b956618e Antony Chazapis
    if request.method == 'HEAD':
75 b956618e Antony Chazapis
        return container_meta(request, v_account, v_container)
76 b956618e Antony Chazapis
    elif request.method == 'PUT':
77 b956618e Antony Chazapis
        return container_create(request, v_account, v_container)
78 b956618e Antony Chazapis
    elif request.method == 'POST':
79 b956618e Antony Chazapis
        return container_update(request, v_account, v_container)
80 b956618e Antony Chazapis
    elif request.method == 'DELETE':
81 b956618e Antony Chazapis
        return container_delete(request, v_account, v_container)
82 83dd59c5 Antony Chazapis
    elif request.method == 'GET':
83 83dd59c5 Antony Chazapis
        return object_list(request, v_account, v_container)
84 b956618e Antony Chazapis
    else:
85 b956618e Antony Chazapis
        return method_not_allowed(request)
86 b956618e Antony Chazapis
87 b956618e Antony Chazapis
def object_demux(request, v_account, v_container, v_object):
88 b956618e Antony Chazapis
    if request.method == 'HEAD':
89 b956618e Antony Chazapis
        return object_meta(request, v_account, v_container, v_object)
90 b956618e Antony Chazapis
    elif request.method == 'GET':
91 b956618e Antony Chazapis
        return object_read(request, v_account, v_container, v_object)
92 b956618e Antony Chazapis
    elif request.method == 'PUT':
93 b956618e Antony Chazapis
        return object_write(request, v_account, v_container, v_object)
94 b956618e Antony Chazapis
    elif request.method == 'COPY':
95 b956618e Antony Chazapis
        return object_copy(request, v_account, v_container, v_object)
96 b956618e Antony Chazapis
    elif request.method == 'MOVE':
97 b956618e Antony Chazapis
        return object_move(request, v_account, v_container, v_object)
98 b956618e Antony Chazapis
    elif request.method == 'POST':
99 b956618e Antony Chazapis
        return object_update(request, v_account, v_container, v_object)
100 b956618e Antony Chazapis
    elif request.method == 'DELETE':
101 b956618e Antony Chazapis
        return object_delete(request, v_account, v_container, v_object)
102 b956618e Antony Chazapis
    else:
103 b956618e Antony Chazapis
        return method_not_allowed(request)
104 b956618e Antony Chazapis
105 b956618e Antony Chazapis
@api_method('GET')
106 b956618e Antony Chazapis
def authenticate(request):
107 b956618e Antony Chazapis
    # Normal Response Codes: 204
108 b956618e Antony Chazapis
    # Error Response Codes: serviceUnavailable (503),
109 b956618e Antony Chazapis
    #                       unauthorized (401),
110 b956618e Antony Chazapis
    #                       badRequest (400)
111 b956618e Antony Chazapis
    
112 b956618e Antony Chazapis
    x_auth_user = request.META.get('HTTP_X_AUTH_USER')
113 b956618e Antony Chazapis
    x_auth_key = request.META.get('HTTP_X_AUTH_KEY')
114 b956618e Antony Chazapis
    if not x_auth_user or not x_auth_key:
115 b956618e Antony Chazapis
        raise BadRequest('Missing X-Auth-User or X-Auth-Key header')
116 b956618e Antony Chazapis
    response = HttpResponse(status=204)
117 b956618e Antony Chazapis
    response['X-Auth-Token'] = '0000'
118 b956618e Antony Chazapis
    response['X-Storage-Url'] = os.path.join(request.build_absolute_uri(), 'demo')
119 b956618e Antony Chazapis
    return response
120 b956618e Antony Chazapis
121 b956618e Antony Chazapis
@api_method('HEAD')
122 b956618e Antony Chazapis
def account_meta(request, v_account):
123 b956618e Antony Chazapis
    # Normal Response Codes: 204
124 b956618e Antony Chazapis
    # Error Response Codes: serviceUnavailable (503),
125 b956618e Antony Chazapis
    #                       unauthorized (401),
126 b956618e Antony Chazapis
    #                       badRequest (400)
127 b956618e Antony Chazapis
    
128 83dd59c5 Antony Chazapis
    until = get_int_parameter(request, 'until')
129 83dd59c5 Antony Chazapis
    meta = backend.get_account_meta(request.user, v_account, until)
130 b956618e Antony Chazapis
    
131 b956618e Antony Chazapis
    response = HttpResponse(status=204)
132 b956618e Antony Chazapis
    put_account_meta(response, meta)
133 b956618e Antony Chazapis
    return response
134 b956618e Antony Chazapis
135 b956618e Antony Chazapis
@api_method('POST')
136 b956618e Antony Chazapis
def account_update(request, v_account):
137 b956618e Antony Chazapis
    # Normal Response Codes: 202
138 b956618e Antony Chazapis
    # Error Response Codes: serviceUnavailable (503),
139 b956618e Antony Chazapis
    #                       unauthorized (401),
140 b956618e Antony Chazapis
    #                       badRequest (400)
141 b956618e Antony Chazapis
    
142 b956618e Antony Chazapis
    meta = get_account_meta(request)    
143 83dd59c5 Antony Chazapis
    backend.update_account_meta(request.user, v_account, meta, replace=True)
144 b956618e Antony Chazapis
    return HttpResponse(status=202)
145 b956618e Antony Chazapis
146 b956618e Antony Chazapis
@api_method('GET', format_allowed=True)
147 b956618e Antony Chazapis
def container_list(request, v_account):
148 b956618e Antony Chazapis
    # Normal Response Codes: 200, 204
149 b956618e Antony Chazapis
    # Error Response Codes: serviceUnavailable (503),
150 b956618e Antony Chazapis
    #                       itemNotFound (404),
151 b956618e Antony Chazapis
    #                       unauthorized (401),
152 b956618e Antony Chazapis
    #                       badRequest (400)
153 b956618e Antony Chazapis
    
154 83dd59c5 Antony Chazapis
    until = get_int_parameter(request, 'until')
155 83dd59c5 Antony Chazapis
    meta = backend.get_account_meta(request.user, v_account, until)
156 b956618e Antony Chazapis
    
157 b956618e Antony Chazapis
    validate_modification_preconditions(request, meta)
158 b956618e Antony Chazapis
    
159 b956618e Antony Chazapis
    response = HttpResponse()
160 b956618e Antony Chazapis
    put_account_meta(response, meta)
161 b956618e Antony Chazapis
    
162 b956618e Antony Chazapis
    marker = request.GET.get('marker')
163 b956618e Antony Chazapis
    limit = request.GET.get('limit')
164 b956618e Antony Chazapis
    if limit:
165 b956618e Antony Chazapis
        try:
166 b956618e Antony Chazapis
            limit = int(limit)
167 b956618e Antony Chazapis
            if limit <= 0:
168 b956618e Antony Chazapis
                raise ValueError
169 b956618e Antony Chazapis
        except ValueError:
170 b956618e Antony Chazapis
            limit = 10000
171 b956618e Antony Chazapis
    
172 b956618e Antony Chazapis
    try:
173 83dd59c5 Antony Chazapis
        containers = backend.list_containers(request.user, v_account, marker, limit, until)
174 b956618e Antony Chazapis
    except NameError:
175 b956618e Antony Chazapis
        containers = []
176 b956618e Antony Chazapis
    
177 b956618e Antony Chazapis
    if request.serialization == 'text':
178 b956618e Antony Chazapis
        if len(containers) == 0:
179 b956618e Antony Chazapis
            # The cloudfiles python bindings expect 200 if json/xml.
180 b956618e Antony Chazapis
            response.status_code = 204
181 b956618e Antony Chazapis
            return response
182 b956618e Antony Chazapis
        response.status_code = 200
183 83dd59c5 Antony Chazapis
        response.content = '\n'.join([x[0] for x in containers]) + '\n'
184 b956618e Antony Chazapis
        return response
185 b956618e Antony Chazapis
    
186 b956618e Antony Chazapis
    container_meta = []
187 b956618e Antony Chazapis
    for x in containers:
188 83dd59c5 Antony Chazapis
        if x[1] is not None:
189 83dd59c5 Antony Chazapis
            try:
190 83dd59c5 Antony Chazapis
                meta = backend.get_container_meta(request.user, v_account, x[0], until)
191 83dd59c5 Antony Chazapis
                container_meta.append(printable_meta_dict(meta))
192 83dd59c5 Antony Chazapis
            except NameError:
193 83dd59c5 Antony Chazapis
                pass
194 b956618e Antony Chazapis
    if request.serialization == 'xml':
195 2c22e4ac Antony Chazapis
        data = render_to_string('containers.xml', {'account': v_account, 'containers': container_meta})
196 b956618e Antony Chazapis
    elif request.serialization  == 'json':
197 b956618e Antony Chazapis
        data = json.dumps(container_meta)
198 b956618e Antony Chazapis
    response.status_code = 200
199 b956618e Antony Chazapis
    response.content = data
200 b956618e Antony Chazapis
    return response
201 b956618e Antony Chazapis
202 b956618e Antony Chazapis
@api_method('HEAD')
203 b956618e Antony Chazapis
def container_meta(request, v_account, v_container):
204 b956618e Antony Chazapis
    # Normal Response Codes: 204
205 b956618e Antony Chazapis
    # Error Response Codes: serviceUnavailable (503),
206 b956618e Antony Chazapis
    #                       itemNotFound (404),
207 b956618e Antony Chazapis
    #                       unauthorized (401),
208 b956618e Antony Chazapis
    #                       badRequest (400)
209 b956618e Antony Chazapis
    
210 83dd59c5 Antony Chazapis
    until = get_int_parameter(request, 'until')
211 b956618e Antony Chazapis
    try:
212 83dd59c5 Antony Chazapis
        meta = backend.get_container_meta(request.user, v_account, v_container, until)
213 83dd59c5 Antony Chazapis
        meta['object_meta'] = backend.list_object_meta(request.user, v_account, v_container, until)
214 b956618e Antony Chazapis
    except NameError:
215 b956618e Antony Chazapis
        raise ItemNotFound('Container does not exist')
216 b956618e Antony Chazapis
    
217 b956618e Antony Chazapis
    response = HttpResponse(status=204)
218 b956618e Antony Chazapis
    put_container_meta(response, meta)
219 b956618e Antony Chazapis
    return response
220 b956618e Antony Chazapis
221 b956618e Antony Chazapis
@api_method('PUT')
222 b956618e Antony Chazapis
def container_create(request, v_account, v_container):
223 b956618e Antony Chazapis
    # Normal Response Codes: 201, 202
224 b956618e Antony Chazapis
    # Error Response Codes: serviceUnavailable (503),
225 b956618e Antony Chazapis
    #                       itemNotFound (404),
226 b956618e Antony Chazapis
    #                       unauthorized (401),
227 b956618e Antony Chazapis
    #                       badRequest (400)
228 b956618e Antony Chazapis
    
229 b956618e Antony Chazapis
    meta = get_container_meta(request)
230 b956618e Antony Chazapis
    
231 b956618e Antony Chazapis
    try:
232 83dd59c5 Antony Chazapis
        backend.put_container(request.user, v_account, v_container)
233 b956618e Antony Chazapis
        ret = 201
234 b956618e Antony Chazapis
    except NameError:
235 b956618e Antony Chazapis
        ret = 202
236 b956618e Antony Chazapis
    
237 b956618e Antony Chazapis
    if len(meta) > 0:
238 83dd59c5 Antony Chazapis
        backend.update_container_meta(request.user, v_account, v_container, meta, replace=True)
239 b956618e Antony Chazapis
    
240 b956618e Antony Chazapis
    return HttpResponse(status=ret)
241 b956618e Antony Chazapis
242 b956618e Antony Chazapis
@api_method('POST')
243 b956618e Antony Chazapis
def container_update(request, v_account, v_container):
244 b956618e Antony Chazapis
    # Normal Response Codes: 202
245 b956618e Antony Chazapis
    # Error Response Codes: serviceUnavailable (503),
246 b956618e Antony Chazapis
    #                       itemNotFound (404),
247 b956618e Antony Chazapis
    #                       unauthorized (401),
248 b956618e Antony Chazapis
    #                       badRequest (400)
249 b956618e Antony Chazapis
    
250 b956618e Antony Chazapis
    meta = get_container_meta(request)
251 b956618e Antony Chazapis
    try:
252 83dd59c5 Antony Chazapis
        backend.update_container_meta(request.user, v_account, v_container, meta, replace=True)
253 b956618e Antony Chazapis
    except NameError:
254 b956618e Antony Chazapis
        raise ItemNotFound('Container does not exist')
255 b956618e Antony Chazapis
    return HttpResponse(status=202)
256 b956618e Antony Chazapis
257 b956618e Antony Chazapis
@api_method('DELETE')
258 b956618e Antony Chazapis
def container_delete(request, v_account, v_container):
259 b956618e Antony Chazapis
    # Normal Response Codes: 204
260 b956618e Antony Chazapis
    # Error Response Codes: serviceUnavailable (503),
261 b956618e Antony Chazapis
    #                       conflict (409),
262 b956618e Antony Chazapis
    #                       itemNotFound (404),
263 b956618e Antony Chazapis
    #                       unauthorized (401),
264 b956618e Antony Chazapis
    #                       badRequest (400)
265 b956618e Antony Chazapis
    
266 b956618e Antony Chazapis
    try:
267 83dd59c5 Antony Chazapis
        backend.delete_container(request.user, v_account, v_container)
268 b956618e Antony Chazapis
    except NameError:
269 b956618e Antony Chazapis
        raise ItemNotFound('Container does not exist')
270 b956618e Antony Chazapis
    except IndexError:
271 b956618e Antony Chazapis
        raise Conflict('Container is not empty')
272 b956618e Antony Chazapis
    return HttpResponse(status=204)
273 b956618e Antony Chazapis
274 b956618e Antony Chazapis
@api_method('GET', format_allowed=True)
275 b956618e Antony Chazapis
def object_list(request, v_account, v_container):
276 b956618e Antony Chazapis
    # Normal Response Codes: 200, 204
277 b956618e Antony Chazapis
    # Error Response Codes: serviceUnavailable (503),
278 b956618e Antony Chazapis
    #                       itemNotFound (404),
279 b956618e Antony Chazapis
    #                       unauthorized (401),
280 b956618e Antony Chazapis
    #                       badRequest (400)
281 b956618e Antony Chazapis
    
282 83dd59c5 Antony Chazapis
    until = get_int_parameter(request, 'until')
283 b956618e Antony Chazapis
    try:
284 83dd59c5 Antony Chazapis
        meta = backend.get_container_meta(request.user, v_account, v_container, until)
285 83dd59c5 Antony Chazapis
        meta['object_meta'] = backend.list_object_meta(request.user, v_account, v_container, until)
286 b956618e Antony Chazapis
    except NameError:
287 b956618e Antony Chazapis
        raise ItemNotFound('Container does not exist')
288 b956618e Antony Chazapis
    
289 b956618e Antony Chazapis
    validate_modification_preconditions(request, meta)
290 b956618e Antony Chazapis
    
291 b956618e Antony Chazapis
    response = HttpResponse()
292 b956618e Antony Chazapis
    put_container_meta(response, meta)
293 b956618e Antony Chazapis
    
294 b956618e Antony Chazapis
    path = request.GET.get('path')
295 b956618e Antony Chazapis
    prefix = request.GET.get('prefix')
296 b956618e Antony Chazapis
    delimiter = request.GET.get('delimiter')
297 b956618e Antony Chazapis
    
298 b956618e Antony Chazapis
    # Path overrides prefix and delimiter.
299 b956618e Antony Chazapis
    virtual = True
300 b956618e Antony Chazapis
    if path:
301 b956618e Antony Chazapis
        prefix = path
302 b956618e Antony Chazapis
        delimiter = '/'
303 b956618e Antony Chazapis
        virtual = False
304 b956618e Antony Chazapis
    
305 b956618e Antony Chazapis
    # Naming policy.
306 b956618e Antony Chazapis
    if prefix and delimiter:
307 b956618e Antony Chazapis
        prefix = prefix + delimiter
308 b956618e Antony Chazapis
    if not prefix:
309 b956618e Antony Chazapis
        prefix = ''
310 b956618e Antony Chazapis
    prefix = prefix.lstrip('/')
311 b956618e Antony Chazapis
    
312 b956618e Antony Chazapis
    marker = request.GET.get('marker')
313 b956618e Antony Chazapis
    limit = request.GET.get('limit')
314 b956618e Antony Chazapis
    if limit:
315 b956618e Antony Chazapis
        try:
316 b956618e Antony Chazapis
            limit = int(limit)
317 b956618e Antony Chazapis
            if limit <= 0:
318 b956618e Antony Chazapis
                raise ValueError
319 b956618e Antony Chazapis
        except ValueError:
320 b956618e Antony Chazapis
            limit = 10000
321 b956618e Antony Chazapis
    
322 22dab079 Antony Chazapis
    keys = request.GET.get('meta')
323 22dab079 Antony Chazapis
    if keys:
324 22dab079 Antony Chazapis
        keys = keys.split(',')
325 22dab079 Antony Chazapis
        keys = [format_meta_key('X-Object-Meta-' + x.strip()) for x in keys if x.strip() != '']
326 22dab079 Antony Chazapis
    else:
327 22dab079 Antony Chazapis
        keys = []
328 22dab079 Antony Chazapis
    
329 b956618e Antony Chazapis
    try:
330 83dd59c5 Antony Chazapis
        objects = backend.list_objects(request.user, v_account, v_container, prefix, delimiter, marker, limit, virtual, keys, until)
331 b956618e Antony Chazapis
    except NameError:
332 b956618e Antony Chazapis
        raise ItemNotFound('Container does not exist')
333 b956618e Antony Chazapis
    
334 b956618e Antony Chazapis
    if request.serialization == 'text':
335 b956618e Antony Chazapis
        if len(objects) == 0:
336 b956618e Antony Chazapis
            # The cloudfiles python bindings expect 200 if json/xml.
337 b956618e Antony Chazapis
            response.status_code = 204
338 b956618e Antony Chazapis
            return response
339 b956618e Antony Chazapis
        response.status_code = 200
340 83dd59c5 Antony Chazapis
        response.content = '\n'.join([x[0] for x in objects]) + '\n'
341 b956618e Antony Chazapis
        return response
342 b956618e Antony Chazapis
    
343 b956618e Antony Chazapis
    object_meta = []
344 b956618e Antony Chazapis
    for x in objects:
345 83dd59c5 Antony Chazapis
        if x[1] is None:
346 b956618e Antony Chazapis
            # Virtual objects/directories.
347 83dd59c5 Antony Chazapis
            object_meta.append({'subdir': x[0]})
348 83dd59c5 Antony Chazapis
        else:
349 83dd59c5 Antony Chazapis
            try:
350 83dd59c5 Antony Chazapis
                meta = backend.get_object_meta(request.user, v_account, v_container, x[0], x[1])
351 3436eeb0 Antony Chazapis
                permissions = backend.get_object_permissions(request.user, v_account, v_container, x[0])
352 83dd59c5 Antony Chazapis
            except NameError:
353 83dd59c5 Antony Chazapis
                pass
354 3436eeb0 Antony Chazapis
            if permissions:
355 3436eeb0 Antony Chazapis
                meta['X-Object-Sharing'] = format_permissions(permissions)
356 3436eeb0 Antony Chazapis
            object_meta.append(printable_meta_dict(meta))
357 b956618e Antony Chazapis
    if request.serialization == 'xml':
358 b956618e Antony Chazapis
        data = render_to_string('objects.xml', {'container': v_container, 'objects': object_meta})
359 b956618e Antony Chazapis
    elif request.serialization  == 'json':
360 b956618e Antony Chazapis
        data = json.dumps(object_meta)
361 b956618e Antony Chazapis
    response.status_code = 200
362 b956618e Antony Chazapis
    response.content = data
363 b956618e Antony Chazapis
    return response
364 b956618e Antony Chazapis
365 b956618e Antony Chazapis
@api_method('HEAD')
366 b956618e Antony Chazapis
def object_meta(request, v_account, v_container, v_object):
367 b956618e Antony Chazapis
    # Normal Response Codes: 204
368 b956618e Antony Chazapis
    # Error Response Codes: serviceUnavailable (503),
369 b956618e Antony Chazapis
    #                       itemNotFound (404),
370 b956618e Antony Chazapis
    #                       unauthorized (401),
371 b956618e Antony Chazapis
    #                       badRequest (400)
372 b956618e Antony Chazapis
    
373 83dd59c5 Antony Chazapis
    version = get_int_parameter(request, 'version')
374 b956618e Antony Chazapis
    try:
375 83dd59c5 Antony Chazapis
        meta = backend.get_object_meta(request.user, v_account, v_container, v_object, version)
376 3436eeb0 Antony Chazapis
        permissions = backend.get_object_permissions(request.user, v_account, v_container, v_object)
377 b956618e Antony Chazapis
    except NameError:
378 b956618e Antony Chazapis
        raise ItemNotFound('Object does not exist')
379 58a6c894 Antony Chazapis
    except IndexError:
380 58a6c894 Antony Chazapis
        raise ItemNotFound('Version does not exist')
381 b956618e Antony Chazapis
    
382 3436eeb0 Antony Chazapis
    if permissions:
383 3436eeb0 Antony Chazapis
        meta['X-Object-Sharing'] = format_permissions(permissions)
384 8cb45c13 Antony Chazapis
    update_manifest_meta(request, v_account, meta)
385 8cb45c13 Antony Chazapis
    
386 cb146cf9 Antony Chazapis
    response = HttpResponse(status=200)
387 b956618e Antony Chazapis
    put_object_meta(response, meta)
388 b956618e Antony Chazapis
    return response
389 b956618e Antony Chazapis
390 22dab079 Antony Chazapis
@api_method('GET', format_allowed=True)
391 b956618e Antony Chazapis
def object_read(request, v_account, v_container, v_object):
392 b956618e Antony Chazapis
    # Normal Response Codes: 200, 206
393 b956618e Antony Chazapis
    # Error Response Codes: serviceUnavailable (503),
394 b956618e Antony Chazapis
    #                       rangeNotSatisfiable (416),
395 b956618e Antony Chazapis
    #                       preconditionFailed (412),
396 b956618e Antony Chazapis
    #                       itemNotFound (404),
397 b956618e Antony Chazapis
    #                       unauthorized (401),
398 b956618e Antony Chazapis
    #                       badRequest (400),
399 b956618e Antony Chazapis
    #                       notModified (304)
400 b956618e Antony Chazapis
    
401 83dd59c5 Antony Chazapis
    version = get_int_parameter(request, 'version')
402 cb146cf9 Antony Chazapis
    version_list = False
403 cb146cf9 Antony Chazapis
    if version is None and request.GET.get('version') == 'list':
404 cb146cf9 Antony Chazapis
        version_list = True
405 b956618e Antony Chazapis
    try:
406 83dd59c5 Antony Chazapis
        meta = backend.get_object_meta(request.user, v_account, v_container, v_object, version)
407 3436eeb0 Antony Chazapis
        permissions = backend.get_object_permissions(request.user, v_account, v_container, v_object)
408 b956618e Antony Chazapis
    except NameError:
409 b956618e Antony Chazapis
        raise ItemNotFound('Object does not exist')
410 58a6c894 Antony Chazapis
    except IndexError:
411 58a6c894 Antony Chazapis
        raise ItemNotFound('Version does not exist')
412 b956618e Antony Chazapis
    
413 3436eeb0 Antony Chazapis
    if permissions:
414 3436eeb0 Antony Chazapis
        meta['X-Object-Sharing'] = format_permissions(permissions)
415 8cb45c13 Antony Chazapis
    update_manifest_meta(request, v_account, meta)
416 8cb45c13 Antony Chazapis
    
417 22dab079 Antony Chazapis
    # Evaluate conditions.
418 b956618e Antony Chazapis
    validate_modification_preconditions(request, meta)
419 22dab079 Antony Chazapis
    try:
420 22dab079 Antony Chazapis
        validate_matching_preconditions(request, meta)
421 22dab079 Antony Chazapis
    except NotModified:
422 22dab079 Antony Chazapis
        response = HttpResponse(status=304)
423 22dab079 Antony Chazapis
        response['ETag'] = meta['hash']
424 22dab079 Antony Chazapis
        return response
425 b956618e Antony Chazapis
    
426 83dd59c5 Antony Chazapis
    # Reply with the version list.
427 cb146cf9 Antony Chazapis
    if version_list:
428 83dd59c5 Antony Chazapis
        if request.serialization == 'text':
429 83dd59c5 Antony Chazapis
            raise BadRequest('No format specified for version list.')
430 83dd59c5 Antony Chazapis
        
431 83dd59c5 Antony Chazapis
        d = {'versions': backend.list_versions(request.user, v_account, v_container, v_object)}
432 83dd59c5 Antony Chazapis
        if request.serialization == 'xml':
433 83dd59c5 Antony Chazapis
            d['object'] = v_object
434 83dd59c5 Antony Chazapis
            data = render_to_string('versions.xml', d)
435 83dd59c5 Antony Chazapis
        elif request.serialization  == 'json':
436 83dd59c5 Antony Chazapis
            data = json.dumps(d)
437 83dd59c5 Antony Chazapis
        
438 83dd59c5 Antony Chazapis
        response = HttpResponse(data, status=200)
439 83dd59c5 Antony Chazapis
        put_object_meta(response, meta)
440 83dd59c5 Antony Chazapis
        response['Content-Length'] = len(data)
441 83dd59c5 Antony Chazapis
        return response
442 83dd59c5 Antony Chazapis
    
443 8cb45c13 Antony Chazapis
    sizes = []
444 8cb45c13 Antony Chazapis
    hashmaps = []
445 8cb45c13 Antony Chazapis
    if 'X-Object-Manifest' in meta:
446 8cb45c13 Antony Chazapis
        try:
447 6d817842 Antony Chazapis
            src_container, src_name = split_container_object_string('/' + meta['X-Object-Manifest'])
448 8cb45c13 Antony Chazapis
            objects = backend.list_objects(request.user, v_account, src_container, prefix=src_name, virtual=False)
449 8cb45c13 Antony Chazapis
        except ValueError:
450 8cb45c13 Antony Chazapis
            raise BadRequest('Invalid X-Object-Manifest header')
451 8cb45c13 Antony Chazapis
        except NameError:
452 8cb45c13 Antony Chazapis
            raise ItemNotFound('Container does not exist')
453 8cb45c13 Antony Chazapis
        
454 8cb45c13 Antony Chazapis
        try:
455 8cb45c13 Antony Chazapis
            for x in objects:
456 8cb45c13 Antony Chazapis
                s, h = backend.get_object_hashmap(request.user, v_account, src_container, x[0], x[1])
457 8cb45c13 Antony Chazapis
                sizes.append(s)
458 8cb45c13 Antony Chazapis
                hashmaps.append(h)
459 8cb45c13 Antony Chazapis
        except NameError:
460 8cb45c13 Antony Chazapis
            raise ItemNotFound('Object does not exist')
461 8cb45c13 Antony Chazapis
        except IndexError:
462 8cb45c13 Antony Chazapis
            raise ItemNotFound('Version does not exist')
463 8cb45c13 Antony Chazapis
    else:
464 8cb45c13 Antony Chazapis
        try:
465 8cb45c13 Antony Chazapis
            s, h = backend.get_object_hashmap(request.user, v_account, v_container, v_object, version)
466 8cb45c13 Antony Chazapis
            sizes.append(s)
467 8cb45c13 Antony Chazapis
            hashmaps.append(h)
468 8cb45c13 Antony Chazapis
        except NameError:
469 8cb45c13 Antony Chazapis
            raise ItemNotFound('Object does not exist')
470 8cb45c13 Antony Chazapis
        except IndexError:
471 8cb45c13 Antony Chazapis
            raise ItemNotFound('Version does not exist')
472 b956618e Antony Chazapis
    
473 22dab079 Antony Chazapis
    # Reply with the hashmap.
474 22dab079 Antony Chazapis
    if request.serialization != 'text':
475 8cb45c13 Antony Chazapis
        size = sum(sizes)
476 8cb45c13 Antony Chazapis
        hashmap = sum(hashmaps, [])
477 6bc372a4 Antony Chazapis
        d = {'block_size': backend.block_size, 'block_hash': backend.hash_algorithm, 'bytes': size, 'hashes': hashmap}
478 22dab079 Antony Chazapis
        if request.serialization == 'xml':
479 6bc372a4 Antony Chazapis
            d['object'] = v_object
480 6bc372a4 Antony Chazapis
            data = render_to_string('hashes.xml', d)
481 22dab079 Antony Chazapis
        elif request.serialization  == 'json':
482 6bc372a4 Antony Chazapis
            data = json.dumps(d)
483 22dab079 Antony Chazapis
        
484 22dab079 Antony Chazapis
        response = HttpResponse(data, status=200)
485 22dab079 Antony Chazapis
        put_object_meta(response, meta)
486 22dab079 Antony Chazapis
        response['Content-Length'] = len(data)
487 22dab079 Antony Chazapis
        return response
488 22dab079 Antony Chazapis
    
489 8cb45c13 Antony Chazapis
    return object_data_response(request, sizes, hashmaps, meta)
490 b956618e Antony Chazapis
491 b956618e Antony Chazapis
@api_method('PUT')
492 b956618e Antony Chazapis
def object_write(request, v_account, v_container, v_object):
493 b956618e Antony Chazapis
    # Normal Response Codes: 201
494 b956618e Antony Chazapis
    # Error Response Codes: serviceUnavailable (503),
495 b956618e Antony Chazapis
    #                       unprocessableEntity (422),
496 b956618e Antony Chazapis
    #                       lengthRequired (411),
497 3436eeb0 Antony Chazapis
    #                       conflict (409),
498 b956618e Antony Chazapis
    #                       itemNotFound (404),
499 b956618e Antony Chazapis
    #                       unauthorized (401),
500 b956618e Antony Chazapis
    #                       badRequest (400)
501 b956618e Antony Chazapis
    
502 b956618e Antony Chazapis
    copy_from = request.META.get('HTTP_X_COPY_FROM')
503 b956618e Antony Chazapis
    move_from = request.META.get('HTTP_X_MOVE_FROM')
504 b956618e Antony Chazapis
    if copy_from or move_from:
505 b956618e Antony Chazapis
        # TODO: Why is this required? Copy this ammount?
506 22dab079 Antony Chazapis
        content_length = get_content_length(request)
507 b956618e Antony Chazapis
        
508 b956618e Antony Chazapis
        if move_from:
509 83dd59c5 Antony Chazapis
            try:
510 83dd59c5 Antony Chazapis
                src_container, src_name = split_container_object_string(move_from)
511 83dd59c5 Antony Chazapis
            except ValueError:
512 83dd59c5 Antony Chazapis
                raise BadRequest('Invalid X-Move-From header')
513 83dd59c5 Antony Chazapis
            copy_or_move_object(request, v_account, src_container, src_name, v_container, v_object, move=True)
514 b956618e Antony Chazapis
        else:
515 83dd59c5 Antony Chazapis
            try:
516 83dd59c5 Antony Chazapis
                src_container, src_name = split_container_object_string(copy_from)
517 83dd59c5 Antony Chazapis
            except ValueError:
518 83dd59c5 Antony Chazapis
                raise BadRequest('Invalid X-Copy-From header')
519 83dd59c5 Antony Chazapis
            copy_or_move_object(request, v_account, src_container, src_name, v_container, v_object, move=False)
520 b956618e Antony Chazapis
        return HttpResponse(status=201)
521 b956618e Antony Chazapis
    
522 b956618e Antony Chazapis
    meta = get_object_meta(request)
523 3436eeb0 Antony Chazapis
    permissions = get_sharing(request)
524 b956618e Antony Chazapis
    content_length = -1
525 b956618e Antony Chazapis
    if request.META.get('HTTP_TRANSFER_ENCODING') != 'chunked':
526 22dab079 Antony Chazapis
        content_length = get_content_length(request)
527 b956618e Antony Chazapis
    # Should be BadRequest, but API says otherwise.
528 b956618e Antony Chazapis
    if 'Content-Type' not in meta:
529 b956618e Antony Chazapis
        raise LengthRequired('Missing Content-Type header')
530 b956618e Antony Chazapis
    
531 b956618e Antony Chazapis
    md5 = hashlib.md5()
532 58a6c894 Antony Chazapis
    size = 0
533 58a6c894 Antony Chazapis
    hashmap = []
534 58a6c894 Antony Chazapis
    sock = raw_input_socket(request)
535 58a6c894 Antony Chazapis
    for data in socket_read_iterator(sock, content_length, backend.block_size):
536 58a6c894 Antony Chazapis
        # TODO: Raise 408 (Request Timeout) if this takes too long.
537 58a6c894 Antony Chazapis
        # TODO: Raise 499 (Client Disconnect) if a length is defined and we stop before getting this much data.
538 58a6c894 Antony Chazapis
        size += len(data)
539 58a6c894 Antony Chazapis
        hashmap.append(backend.put_block(data))
540 58a6c894 Antony Chazapis
        md5.update(data)
541 b956618e Antony Chazapis
    
542 b956618e Antony Chazapis
    meta['hash'] = md5.hexdigest().lower()
543 b956618e Antony Chazapis
    etag = request.META.get('HTTP_ETAG')
544 b956618e Antony Chazapis
    if etag and parse_etags(etag)[0].lower() != meta['hash']:
545 22dab079 Antony Chazapis
        raise UnprocessableEntity('Object ETag does not match')
546 22dab079 Antony Chazapis
    
547 22dab079 Antony Chazapis
    try:
548 3436eeb0 Antony Chazapis
        backend.update_object_hashmap(request.user, v_account, v_container, v_object, size, hashmap, meta, True, permissions)
549 22dab079 Antony Chazapis
    except NameError:
550 22dab079 Antony Chazapis
        raise ItemNotFound('Container does not exist')
551 3436eeb0 Antony Chazapis
    except ValueError:
552 3436eeb0 Antony Chazapis
        raise BadRequest('Invalid sharing header')
553 3436eeb0 Antony Chazapis
    except AttributeError:
554 3436eeb0 Antony Chazapis
        raise Conflict('Sharing already set above or below this path in the hierarchy')
555 b956618e Antony Chazapis
    
556 b956618e Antony Chazapis
    response = HttpResponse(status=201)
557 b956618e Antony Chazapis
    response['ETag'] = meta['hash']
558 b956618e Antony Chazapis
    return response
559 b956618e Antony Chazapis
560 b956618e Antony Chazapis
@api_method('COPY')
561 b956618e Antony Chazapis
def object_copy(request, v_account, v_container, v_object):
562 b956618e Antony Chazapis
    # Normal Response Codes: 201
563 b956618e Antony Chazapis
    # Error Response Codes: serviceUnavailable (503),
564 b956618e Antony Chazapis
    #                       itemNotFound (404),
565 b956618e Antony Chazapis
    #                       unauthorized (401),
566 b956618e Antony Chazapis
    #                       badRequest (400)
567 b956618e Antony Chazapis
    
568 b956618e Antony Chazapis
    dest_path = request.META.get('HTTP_DESTINATION')
569 b956618e Antony Chazapis
    if not dest_path:
570 b956618e Antony Chazapis
        raise BadRequest('Missing Destination header')
571 83dd59c5 Antony Chazapis
    try:
572 83dd59c5 Antony Chazapis
        dest_container, dest_name = split_container_object_string(dest_path)
573 83dd59c5 Antony Chazapis
    except ValueError:
574 83dd59c5 Antony Chazapis
        raise BadRequest('Invalid Destination header')
575 83dd59c5 Antony Chazapis
    copy_or_move_object(request, v_account, v_container, v_object, dest_container, dest_name, move=False)
576 b956618e Antony Chazapis
    return HttpResponse(status=201)
577 b956618e Antony Chazapis
578 b956618e Antony Chazapis
@api_method('MOVE')
579 b956618e Antony Chazapis
def object_move(request, v_account, v_container, v_object):
580 b956618e Antony Chazapis
    # Normal Response Codes: 201
581 b956618e Antony Chazapis
    # Error Response Codes: serviceUnavailable (503),
582 b956618e Antony Chazapis
    #                       itemNotFound (404),
583 b956618e Antony Chazapis
    #                       unauthorized (401),
584 b956618e Antony Chazapis
    #                       badRequest (400)
585 b956618e Antony Chazapis
    
586 b956618e Antony Chazapis
    dest_path = request.META.get('HTTP_DESTINATION')
587 b956618e Antony Chazapis
    if not dest_path:
588 b956618e Antony Chazapis
        raise BadRequest('Missing Destination header')
589 83dd59c5 Antony Chazapis
    try:
590 83dd59c5 Antony Chazapis
        dest_container, dest_name = split_container_object_string(dest_path)
591 83dd59c5 Antony Chazapis
    except ValueError:
592 83dd59c5 Antony Chazapis
        raise BadRequest('Invalid Destination header')
593 83dd59c5 Antony Chazapis
    copy_or_move_object(request, v_account, v_container, v_object, dest_container, dest_name, move=True)
594 b956618e Antony Chazapis
    return HttpResponse(status=201)
595 b956618e Antony Chazapis
596 b956618e Antony Chazapis
@api_method('POST')
597 b956618e Antony Chazapis
def object_update(request, v_account, v_container, v_object):
598 e9285524 Antony Chazapis
    # Normal Response Codes: 202, 204
599 b956618e Antony Chazapis
    # Error Response Codes: serviceUnavailable (503),
600 3436eeb0 Antony Chazapis
    #                       conflict (409),
601 b956618e Antony Chazapis
    #                       itemNotFound (404),
602 b956618e Antony Chazapis
    #                       unauthorized (401),
603 b956618e Antony Chazapis
    #                       badRequest (400)
604 b956618e Antony Chazapis
    
605 b956618e Antony Chazapis
    meta = get_object_meta(request)
606 3436eeb0 Antony Chazapis
    permissions = get_sharing(request)
607 22dab079 Antony Chazapis
    content_type = meta.get('Content-Type')
608 22dab079 Antony Chazapis
    if content_type:
609 b956618e Antony Chazapis
        del(meta['Content-Type']) # Do not allow changing the Content-Type.
610 22dab079 Antony Chazapis
    
611 cfe6939d Antony Chazapis
    try:
612 83dd59c5 Antony Chazapis
        prev_meta = backend.get_object_meta(request.user, v_account, v_container, v_object)
613 cfe6939d Antony Chazapis
    except NameError:
614 cfe6939d Antony Chazapis
        raise ItemNotFound('Object does not exist')
615 cfe6939d Antony Chazapis
    
616 cfe6939d Antony Chazapis
    # Handle metadata changes.
617 22dab079 Antony Chazapis
    if len(meta) != 0:
618 22dab079 Antony Chazapis
        # Keep previous values of 'Content-Type' and 'hash'.
619 22dab079 Antony Chazapis
        for k in ('Content-Type', 'hash'):
620 22dab079 Antony Chazapis
            if k in prev_meta:
621 22dab079 Antony Chazapis
                meta[k] = prev_meta[k]
622 22dab079 Antony Chazapis
        try:
623 83dd59c5 Antony Chazapis
            backend.update_object_meta(request.user, v_account, v_container, v_object, meta, replace=True)
624 22dab079 Antony Chazapis
        except NameError:
625 22dab079 Antony Chazapis
            raise ItemNotFound('Object does not exist')
626 22dab079 Antony Chazapis
    
627 3436eeb0 Antony Chazapis
    # Handle permission changes.
628 3436eeb0 Antony Chazapis
    if permissions:
629 3436eeb0 Antony Chazapis
        try:
630 3436eeb0 Antony Chazapis
            backend.update_object_permissions(request.user, v_account, v_container, v_object, permissions)
631 3436eeb0 Antony Chazapis
        except NameError:
632 3436eeb0 Antony Chazapis
            raise ItemNotFound('Object does not exist')
633 3436eeb0 Antony Chazapis
        except ValueError:
634 3436eeb0 Antony Chazapis
            raise BadRequest('Invalid sharing header')
635 3436eeb0 Antony Chazapis
        except AttributeError:
636 3436eeb0 Antony Chazapis
            raise Conflict('Sharing already set above or below this path in the hierarchy')
637 3436eeb0 Antony Chazapis
    
638 3436eeb0 Antony Chazapis
    # TODO: Merge above functions with updating the hashmap if there is data in the request.
639 3436eeb0 Antony Chazapis
    
640 cfe6939d Antony Chazapis
    # A Content-Type or Content-Range header may indicate data updates.
641 288c1d55 Antony Chazapis
    if content_type is None:
642 288c1d55 Antony Chazapis
        return HttpResponse(status=202)
643 288c1d55 Antony Chazapis
    if content_type.startswith('multipart/byteranges'):
644 cfe6939d Antony Chazapis
        # TODO: Support multiple update ranges.
645 cfe6939d Antony Chazapis
        return HttpResponse(status=202)
646 cfe6939d Antony Chazapis
    # Single range update. Range must be in Content-Range.
647 22dab079 Antony Chazapis
    # Based on: http://code.google.com/p/gears/wiki/ContentRangePostProposal
648 cfe6939d Antony Chazapis
    # (with the addition that '*' is allowed for the range - will append).
649 288c1d55 Antony Chazapis
    if content_type != 'application/octet-stream':
650 cfe6939d Antony Chazapis
        return HttpResponse(status=202)
651 22dab079 Antony Chazapis
    content_range = request.META.get('HTTP_CONTENT_RANGE')
652 22dab079 Antony Chazapis
    if not content_range:
653 22dab079 Antony Chazapis
        return HttpResponse(status=202)
654 22dab079 Antony Chazapis
    ranges = get_content_range(request)
655 22dab079 Antony Chazapis
    if not ranges:
656 22dab079 Antony Chazapis
        return HttpResponse(status=202)
657 cfe6939d Antony Chazapis
    # Require either a Content-Length, or 'chunked' Transfer-Encoding.
658 cfe6939d Antony Chazapis
    content_length = -1
659 cfe6939d Antony Chazapis
    if request.META.get('HTTP_TRANSFER_ENCODING') != 'chunked':
660 cfe6939d Antony Chazapis
        content_length = get_content_length(request)
661 22dab079 Antony Chazapis
    
662 cfe6939d Antony Chazapis
    try:
663 83dd59c5 Antony Chazapis
        size, hashmap = backend.get_object_hashmap(request.user, v_account, v_container, v_object)
664 cfe6939d Antony Chazapis
    except NameError:
665 cfe6939d Antony Chazapis
        raise ItemNotFound('Object does not exist')
666 cfe6939d Antony Chazapis
    
667 cfe6939d Antony Chazapis
    offset, length, total = ranges
668 cfe6939d Antony Chazapis
    if offset is None:
669 cfe6939d Antony Chazapis
        offset = size
670 cb146cf9 Antony Chazapis
    elif offset > size:
671 cb146cf9 Antony Chazapis
        raise RangeNotSatisfiable('Supplied offset is beyond object limits')
672 9a7f747a Sofia Papagiannaki
    if length is None or content_length == -1:
673 cfe6939d Antony Chazapis
        length = content_length # Nevermind the error.
674 cfe6939d Antony Chazapis
    elif length != content_length:
675 cfe6939d Antony Chazapis
        raise BadRequest('Content length does not match range length')
676 cfe6939d Antony Chazapis
    if total is not None and (total != size or offset >= size or (length > 0 and offset + length >= size)):
677 cfe6939d Antony Chazapis
        raise RangeNotSatisfiable('Supplied range will change provided object limits')
678 cfe6939d Antony Chazapis
    
679 cfe6939d Antony Chazapis
    sock = raw_input_socket(request)
680 cfe6939d Antony Chazapis
    data = ''
681 cfe6939d Antony Chazapis
    for d in socket_read_iterator(sock, length, backend.block_size):
682 cfe6939d Antony Chazapis
        # TODO: Raise 408 (Request Timeout) if this takes too long.
683 cfe6939d Antony Chazapis
        # TODO: Raise 499 (Client Disconnect) if a length is defined and we stop before getting this much data.
684 cfe6939d Antony Chazapis
        data += d
685 cb146cf9 Antony Chazapis
        bytes = put_object_block(hashmap, data, offset)
686 cb146cf9 Antony Chazapis
        offset += bytes
687 cb146cf9 Antony Chazapis
        data = data[bytes:]
688 cfe6939d Antony Chazapis
    if len(data) > 0:
689 cb146cf9 Antony Chazapis
        put_object_block(hashmap, data, offset)
690 cfe6939d Antony Chazapis
    
691 cfe6939d Antony Chazapis
    if offset > size:
692 cfe6939d Antony Chazapis
        size = offset
693 83dd59c5 Antony Chazapis
    meta = {'hash': hashmap_hash(hashmap)} # Update ETag.
694 cfe6939d Antony Chazapis
    try:
695 83dd59c5 Antony Chazapis
        backend.update_object_hashmap(request.user, v_account, v_container, v_object, size, hashmap, meta, False)
696 cfe6939d Antony Chazapis
    except NameError:
697 cfe6939d Antony Chazapis
        raise ItemNotFound('Container does not exist')
698 3436eeb0 Antony Chazapis
    except ValueError:
699 3436eeb0 Antony Chazapis
        raise BadRequest('Invalid sharing header')
700 3436eeb0 Antony Chazapis
    except AttributeError:
701 3436eeb0 Antony Chazapis
        raise Conflict('Sharing already set above or below this path in the hierarchy')
702 3436eeb0 Antony Chazapis
    
703 e9285524 Antony Chazapis
    response = HttpResponse(status=204)
704 e9285524 Antony Chazapis
    response['ETag'] = meta['hash']
705 e9285524 Antony Chazapis
    return response
706 b956618e Antony Chazapis
707 b956618e Antony Chazapis
@api_method('DELETE')
708 b956618e Antony Chazapis
def object_delete(request, v_account, v_container, v_object):
709 b956618e Antony Chazapis
    # Normal Response Codes: 204
710 b956618e Antony Chazapis
    # Error Response Codes: serviceUnavailable (503),
711 b956618e Antony Chazapis
    #                       itemNotFound (404),
712 b956618e Antony Chazapis
    #                       unauthorized (401),
713 b956618e Antony Chazapis
    #                       badRequest (400)
714 b956618e Antony Chazapis
    
715 b956618e Antony Chazapis
    try:
716 83dd59c5 Antony Chazapis
        backend.delete_object(request.user, v_account, v_container, v_object)
717 b956618e Antony Chazapis
    except NameError:
718 b956618e Antony Chazapis
        raise ItemNotFound('Object does not exist')
719 b956618e Antony Chazapis
    return HttpResponse(status=204)
720 b956618e Antony Chazapis
721 b956618e Antony Chazapis
@api_method()
722 b956618e Antony Chazapis
def method_not_allowed(request):
723 b956618e Antony Chazapis
    raise BadRequest('Method not allowed')